[gnome-flashback/wip/segeiger/inputmethods: 1/7] input-sources: initial implementation for GfIBusManager



commit 5eaf3039454ef66b8d621565aeb24f0666286b3e
Author: Sebastian Geiger <sbastig gmx net>
Date:   Mon Sep 14 15:20:49 2015 +0200

    input-sources: initial implementation for GfIBusManager

 gnome-flashback/libinput-sources/Makefile.am       |    2 +
 gnome-flashback/libinput-sources/gf-ibus-manager.c |  398 ++++++++++++++++++++
 gnome-flashback/libinput-sources/gf-ibus-manager.h |   40 ++
 3 files changed, 440 insertions(+), 0 deletions(-)
---
diff --git a/gnome-flashback/libinput-sources/Makefile.am b/gnome-flashback/libinput-sources/Makefile.am
index 509eb69..df6aab0 100644
--- a/gnome-flashback/libinput-sources/Makefile.am
+++ b/gnome-flashback/libinput-sources/Makefile.am
@@ -16,6 +16,8 @@ libinput_sources_la_SOURCES = \
        gf-candidate-popup.h \
        gf-input-sources.c \
        gf-input-sources.h \
+       gf-ibus-manager.c \
+       gf-ibus-manager.h \
        $(NULL)
 
 libinput_sources_la_LDFLAGS = \
diff --git a/gnome-flashback/libinput-sources/gf-ibus-manager.c 
b/gnome-flashback/libinput-sources/gf-ibus-manager.c
new file mode 100644
index 0000000..b4ae16d
--- /dev/null
+++ b/gnome-flashback/libinput-sources/gf-ibus-manager.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2015 Sebastian Geiger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Based on code in GNOME Shell:
+ * https://git.gnome.org/browse/gnome-shell/tree/js/misc/ibusManager.js
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "gf-ibus-manager.h"
+#include "gf-candidate-popup.h"
+
+struct _GfIBusManager
+{
+  GObject parent;
+
+  IBusBus *ibus;
+  GHashTable *engines; // maps an engine name to an engine (char* to IBusEngine*)
+  IBusPanelService *panelService;
+  GSubprocess *ibusDaemonSubprocess;
+
+  gboolean ready;
+  gulong registerPropertiesSignalId;
+  guint preloadEnginesTimerId;
+
+  const gchar *currentEngineName;
+
+  /* stores the ids to be used by the timeout callback in preload_engines */
+  char **engineIds;
+
+  GfCandidatePopup *candidate_popup;
+};
+
+G_DEFINE_TYPE (GfIBusManager, gf_ibus_manager, G_TYPE_OBJECT)
+
+static void initEngines_cb (IBusBus *ibus, GAsyncResult *result,
+                            GfIBusManager *manager);
+
+static void initPanelService_cb (IBusBus *ibus, GAsyncResult *result,
+                                 GfIBusManager *manager);
+
+
+enum
+{
+  GF_IBUS_MANAGER_READY,
+  GF_IBUS_MANAGER_PROPERTIES_REGISTERED,
+  GF_IBUS_MANAGER_PROPERTY_UPDATED,
+  GF_IBUS_MANAGER_SET_CONTENT_TYPE,
+  LAST_SIGNAL
+};
+
+static guint gf_ibus_manager_signals[LAST_SIGNAL] = {0};
+
+#define MAX_INPUT_SOURCE_ACTIVATION_TIME 4000
+#define PRELOAD_ENGINES_DELAY_TIME 30
+
+static void
+spawn (GfIBusManager *manager)
+{
+  // start a new subprocess for ibus-daemon
+  GError *error = NULL;
+  manager->ibusDaemonSubprocess = g_subprocess_new (G_SUBPROCESS_FLAGS_NONE, &error,
+                                                    "ibus-daemon", "--xim", "--panel", "disable",
+                                                    NULL);
+  if (error) {
+    g_log ("gnome-flashback", G_LOG_LEVEL_ERROR, _("Failed to launch ibus-daemon: %s"), error->message);
+    g_error_free (error);
+  }
+}
+
+static void
+onConnected_cb (IBusBus *ibus,
+                GfIBusManager *manager)
+{
+  ibus_bus_list_engines_async (ibus, -1, NULL, (GAsyncReadyCallback) initEngines_cb, manager);
+
+  ibus_bus_request_name_async (ibus,
+                               IBUS_SERVICE_PANEL,
+                               IBUS_BUS_NAME_FLAG_REPLACE_EXISTING,
+                               -1,
+                               NULL,
+                               (GAsyncReadyCallback) initPanelService_cb,
+                               manager);
+}
+
+static void
+disconnect (IBusBus *ibus,
+            GfIBusManager *manager)
+{
+  if (manager->panelService) {
+    g_object_unref (manager->panelService);
+    manager->panelService = NULL;
+  }
+
+  gf_candidate_popup_set_panel_service (manager->candidate_popup, NULL);
+
+
+  manager->engines = NULL;
+  manager->ready = FALSE;
+  manager->registerPropertiesSignalId = 0;
+  manager->currentEngineName = NULL;
+
+  g_signal_emit (manager, gf_ibus_manager_signals[GF_IBUS_MANAGER_READY], 0, FALSE);
+
+  spawn (manager);
+}
+
+static void
+on_register_properties_cb (IBusPanelService *panelService,
+                           IBusPropList *properties,
+                           GfIBusManager *manager)
+{
+  if (!ibus_prop_list_get (properties, 0)) {
+    return;
+  }
+
+  g_signal_handler_disconnect (panelService, manager->registerPropertiesSignalId);
+  manager->registerPropertiesSignalId = 0;
+
+  g_signal_emit (manager, GF_IBUS_MANAGER_PROPERTIES_REGISTERED, 0, manager->currentEngineName, properties);
+}
+
+static void
+onEngineChanged_cb (GfIBusManager *manager,
+                    const gchar *engineName)
+{
+  if (!manager->ready) {
+    return;
+  }
+
+  manager->currentEngineName = engineName;
+
+  if (manager->registerPropertiesSignalId != 0) {
+    return;
+  }
+
+  manager->registerPropertiesSignalId =
+    g_signal_connect (manager->panelService, "register-properties", G_CALLBACK (on_register_properties_cb),
+                      manager);
+}
+
+static void
+updateReadiness (GfIBusManager *manager)
+{
+  manager->ready = g_hash_table_size (manager->engines) > 0 && manager->panelService != NULL;
+  g_signal_emit (manager, gf_ibus_manager_signals[GF_IBUS_MANAGER_READY], 0, manager->ready);
+}
+
+static void
+add_to_engines_cb (IBusEngineDesc *engineDescription,
+                   GfIBusManager *manager)
+{
+  const char *name = ibus_engine_desc_get_name (engineDescription);
+  g_hash_table_insert (manager->engines, (gpointer) name, engineDescription);
+}
+
+static void
+initEngines_cb (IBusBus *ibus,
+                GAsyncResult *result,
+                GfIBusManager *manager)
+{
+  GError *error = NULL;
+  GList *engineList = ibus_bus_list_engines_async_finish (ibus, result, &error);
+  if (engineList) {
+    g_list_foreach (engineList, (GFunc) add_to_engines_cb, manager);
+    updateReadiness (manager);
+  } else {
+    disconnect (manager->ibus, manager);
+  }
+}
+
+static void
+updateProperty_cb (IBusPanelService *panelService,
+                   IBusProperty *property,
+                   GfIBusManager *manager)
+{
+  g_signal_emit (manager, GF_IBUS_MANAGER_PROPERTY_UPDATED, 0, manager->currentEngineName, property);
+}
+
+static void
+get_global_engine_cb (IBusBus *ibus,
+                      GAsyncResult *result,
+                      GfIBusManager *manager)
+{
+  GError *error = NULL;
+  IBusEngineDesc *engineDescription = ibus_bus_get_global_engine_async_finish (ibus, result, &error);
+  if (error) {
+    g_log ("gnome-flashback", G_LOG_LEVEL_ERROR,
+           _("Could not get result for 'ibus_bus_get_global_async': %s"),
+           error->message);
+    disconnect (manager->ibus, manager);
+    g_error_free (error);
+  }
+
+  if (!engineDescription) {
+    return;
+  }
+  onEngineChanged_cb (manager, ibus_engine_desc_get_name (engineDescription));
+
+}
+
+static void
+setContentType_cb (IBusPanelService *panelService,
+                   guint purpose,
+                   guint hints,
+                   GfIBusManager *manager)
+{
+  g_signal_emit (manager, gf_ibus_manager_signals[GF_IBUS_MANAGER_SET_CONTENT_TYPE], purpose, hints, 
manager);
+}
+
+
+static void
+initPanelService_cb (IBusBus *ibus,
+                     GAsyncResult *result,
+                     GfIBusManager *manager)
+{
+  GError *error = NULL;
+  gboolean success = ibus_bus_request_name_async_finish (ibus, result, &error);
+  if (error) {
+    g_log ("gnome-flashback", G_LOG_LEVEL_ERROR,
+           _("Could not get result for 'ibus_bus_request_name_async': %s"), error->message);
+    disconnect (manager->ibus, manager);
+    g_error_free (error);
+  }
+
+  if (success) {
+    manager->panelService = ibus_panel_service_new (ibus_bus_get_connection (manager->ibus));
+
+    gf_candidate_popup_set_panel_service (manager->candidate_popup, manager->panelService);
+
+    g_signal_connect (manager->panelService, "update-property", G_CALLBACK (updateProperty_cb), manager);
+    g_signal_connect (manager->panelService, "set-content-type", G_CALLBACK (setContentType_cb), manager);
+
+    ibus_bus_get_global_engine_async (manager->ibus, -1, NULL, (GAsyncReadyCallback) get_global_engine_cb,
+                                      manager);
+
+    updateReadiness (manager);
+  } else {
+    disconnect (manager->ibus, manager);
+  }
+}
+
+static gboolean
+preload_engines_cb (gpointer user_data)
+{
+  GfIBusManager *manager;
+
+  manager = GF_IBUS_MANAGER (user_data);
+
+  ibus_bus_preload_engines_async (manager->ibus, (const gchar *const *) manager->engineIds, -1, NULL, NULL, 
NULL);
+
+  manager->engineIds = NULL;
+
+  manager->preloadEnginesTimerId = 0;
+  return G_SOURCE_REMOVE;
+}
+
+static void
+gf_ibus_manager_dispose (GObject *object)
+{
+  GfIBusManager *manager;
+
+  manager = GF_IBUS_MANAGER (object);
+
+  g_clear_object (&manager->ibusDaemonSubprocess);
+
+  G_OBJECT_CLASS (gf_ibus_manager_parent_class)->dispose (object);
+}
+
+static void
+gf_ibus_manager_class_init (GfIBusManagerClass *manager_class)
+{
+  GObjectClass *object_class;
+
+  object_class = G_OBJECT_CLASS (manager_class);
+
+  object_class->dispose = gf_ibus_manager_dispose;
+
+  gf_ibus_manager_signals[GF_IBUS_MANAGER_READY] =
+    g_signal_new ("ready",
+                  G_TYPE_FROM_CLASS (manager_class),
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+  gf_ibus_manager_signals[GF_IBUS_MANAGER_PROPERTIES_REGISTERED] =
+    g_signal_new ("properties-registered",
+                  G_TYPE_FROM_CLASS (manager_class),
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 2, G_TYPE_STRING, IBUS_TYPE_PROP_LIST);
+
+  gf_ibus_manager_signals[GF_IBUS_MANAGER_PROPERTY_UPDATED] =
+    g_signal_new ("property-updated",
+                  G_TYPE_FROM_CLASS (manager_class),
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 2, G_TYPE_STRING, IBUS_TYPE_PROPERTY);
+
+  gf_ibus_manager_signals[GF_IBUS_MANAGER_SET_CONTENT_TYPE] =
+    g_signal_new ("set-content-type",
+                  G_TYPE_FROM_CLASS (manager_class),
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+}
+
+static void
+gf_ibus_manager_init (GfIBusManager *manager)
+{
+  manager->engines = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                            g_free, g_free);
+
+  ibus_init ();
+
+  // candidate popup
+
+  manager->ibus = ibus_bus_new_async ();
+
+  g_signal_connect (manager->ibus, "connected",
+                    G_CALLBACK (onConnected_cb), manager);
+  g_signal_connect (manager->ibus, "disconnected",
+                    G_CALLBACK (disconnect), manager);
+
+  // Need to set this to get 'global-engine-changed' emissions
+  ibus_bus_set_watch_ibus_signal (manager->ibus, TRUE);
+  g_signal_connect (manager->ibus, "global-engine-changed",
+                    G_CALLBACK (onEngineChanged_cb), manager);
+
+  spawn (manager);
+}
+
+GfIBusManager *
+gf_ibus_manager_new (void)
+{
+  return g_object_new (GF_TYPE_IBUS_MANAGER, NULL);
+}
+
+void
+gf_ibus_manager_preload_engines (GfIBusManager  *manager,
+                                 gchar         **ids)
+{
+  if (!manager->ibus || g_strv_length (ids) == 0)
+    return;
+
+  manager->engineIds = ids;
+
+  if (manager->preloadEnginesTimerId != 0)
+    g_source_remove (manager->preloadEnginesTimerId);
+
+  manager->preloadEnginesTimerId = g_timeout_add (PRELOAD_ENGINES_DELAY_TIME,
+                                                  preload_engines_cb,
+                                                  manager);
+}
+
+void
+gf_ibus_manager_set_engine (GfIBusManager       *manager,
+                            const gchar         *id,
+                            GAsyncReadyCallback  callback)
+{
+  if (!manager->ready) {
+    if (callback != NULL)
+      callback (G_OBJECT (manager), NULL, NULL);
+
+    return;
+  }
+
+  ibus_bus_set_global_engine_async (manager->ibus, id,
+                                    MAX_INPUT_SOURCE_ACTIVATION_TIME,
+                                    NULL, callback, manager);
+}
+
+IBusEngineDesc *
+gf_ibus_manager_get_engine_description (GfIBusManager *manager,
+                                        const gchar   *id)
+{
+  g_return_val_if_fail (manager->engines, NULL);
+
+  if (!manager->ready)
+    return NULL;
+
+  return g_hash_table_lookup (manager->engines, id);
+}
diff --git a/gnome-flashback/libinput-sources/gf-ibus-manager.h 
b/gnome-flashback/libinput-sources/gf-ibus-manager.h
new file mode 100644
index 0000000..ff7a6d9
--- /dev/null
+++ b/gnome-flashback/libinput-sources/gf-ibus-manager.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 Sebastian Geiger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GF_IBUS_MANAGER_H
+#define GF_IBUS_MANAGER_H
+
+#include <glib-object.h>
+#include <ibus-1.0/ibus.h>
+
+#define GF_TYPE_IBUS_MANAGER gf_ibus_manager_get_type ()
+G_DECLARE_FINAL_TYPE (GfIBusManager, gf_ibus_manager,
+                      GF, IBUS_MANAGER, GObject)
+
+GfIBusManager  *gf_ibus_manager_new                    (void);
+
+void            gf_ibus_manager_preload_engines        (GfIBusManager        *manager,
+                                                        gchar               **ids);
+
+void            gf_ibus_manager_set_engine             (GfIBusManager        *manager,
+                                                        const gchar          *id,
+                                                        GAsyncReadyCallback   callback);
+
+IBusEngineDesc *gf_ibus_manager_get_engine_description (GfIBusManager        *manager,
+                                                        const gchar          *id);
+
+#endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]