[gnome-flashback/wip/segeiger/inputmethods: 4/4] libinputsources: initial implementation for GfIBusManager



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

    libinputsources: initial implementation for GfIBusManager

 configure.ac                                       |    3 +-
 gnome-flashback/libinput-sources/Makefile.am       |    4 +-
 gnome-flashback/libinput-sources/gf-ibus-manager.c |  367 ++++++++++++++++++++
 gnome-flashback/libinput-sources/gf-ibus-manager.h |   39 ++
 .../libinput-sources/gf-input-sources.c            |   48 +++-
 5 files changed, 457 insertions(+), 4 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 917977b..06f401a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -41,6 +41,7 @@ GLIB_REQUIRED=2.44.0
 GSETTINGS_DESKTOP_SCHEMAS_REQUIRED=3.12.0
 POLKIT_AGENT_REQUIRED=0.97
 POLKIT_GOBJECT_REQUIRED=0.97
+IBUS_REQUIRED=1.5.10
 
 PKG_CHECK_MODULES(GNOME_FLASHBACK, gtk+-3.0 >= $GTK_REQUIRED dbus-glib-1)
 AC_SUBST(GNOME_FLASHBACK_CFLAGS)
@@ -70,7 +71,7 @@ PKG_CHECK_MODULES(IDLE_MONITOR, gtk+-3.0 >= $GTK_REQUIRED x11 xext)
 AC_SUBST(IDLE_MONITOR_CFLAGS)
 AC_SUBST(IDLE_MONITOR_LIBS)
 
-PKG_CHECK_MODULES(INPUT_SOURCES, gtk+-3.0 >= $GTK_REQUIRED)
+PKG_CHECK_MODULES(INPUT_SOURCES, gtk+-3.0 >= $GTK_REQUIRED ibus-1.0 >= $IBUS_REQUIRED)
 AC_SUBST(INPUT_SOURCES_CFLAGS)
 AC_SUBST(INPUT_SOURCES_LIBS)
 
diff --git a/gnome-flashback/libinput-sources/Makefile.am b/gnome-flashback/libinput-sources/Makefile.am
index bfe63d9..f358a89 100644
--- a/gnome-flashback/libinput-sources/Makefile.am
+++ b/gnome-flashback/libinput-sources/Makefile.am
@@ -13,8 +13,10 @@ libinput_sources_la_CFLAGS = \
        $(NULL)
 
 libinput_sources_la_SOURCES = \
-  gf-input-sources.c \
+       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..3328441
--- /dev/null
+++ b/gnome-flashback/libinput-sources/gf-ibus-manager.c
@@ -0,0 +1,367 @@
+/*
+ * 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/>.
+ */
+
+/**
+ * This coded is based on js/ibusManager.js from GNOME Shell
+ */
+
+#include "config.h"
+
+#include "gf-ibus-manager.h"
+#include "gf-input-sources.h"
+
+#include <glib/gi18n-lib.h>
+
+struct _GfIBusManager
+{
+    GObject parent;
+
+    IBusBus *ibus;
+    GHashTable *engines; // maps an engine name to an engine (char* to IBusEngine*)
+    IBusPanelService *panelService;
+    gboolean ready;
+    gulong registerPropertiesSignalId;
+    guint preloadEnginesTimerId;
+
+    const gchar *currentEngineName;
+
+    /* stores the ids to be used by the timeout callback in preload_engines */
+    char **engineIds;
+};
+
+G_DEFINE_TYPE (GfIBusManager, gf_ibus_manager, G_TYPE_OBJECT)
+
+static void spawn (void);
+static void initEngines (IBusBus *ibus, GAsyncResult *result,
+                         GfIBusManager *manager);
+static void initPanelService (IBusBus *ibus, GAsyncResult *result,
+                              GfIBusManager *manager);
+static void setContentType (IBusPanelService *panelService, guint purpose, guint hints,
+                            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 GF_IBUS_MANAGER_MAX_INPUT_SOURCE_ACTIVATION_TIME 4000
+#define GF_IBUS_MANAGER_PRELOAD_ENGINES_DELAY_TIME 30
+
+static void
+onConnected (GfIBusManager *manager)
+{
+    ibus_bus_list_engines_async (manager->ibus, -1, NULL, (GAsyncReadyCallback) initEngines, manager);
+
+    ibus_bus_request_name_async (manager->ibus,
+                                 IBUS_SERVICE_PANEL,
+                                 IBUS_BUS_NAME_FLAG_REPLACE_EXISTING,
+                                 -1,
+                                 NULL,
+                                 (GAsyncReadyCallback) initPanelService,
+                                 manager);
+}
+
+static void
+onDisconnected (GfIBusManager *manager)
+{
+    if (manager->panelService) {
+        g_object_unref (manager->panelService);
+        manager->panelService = NULL;
+    }
+
+    // FIXME: something about the candidatePopup, we will ignore that for now.
+    // this.candidatePopup.setPanelService(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);
+
+    spawn ();
+}
+
+static void
+on_register_properties (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 (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), 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);
+}
+
+static void
+add_to_engines (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 (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, manager);
+        updateReadiness (manager);
+    } else {
+        onDisconnected (manager);
+    }
+}
+
+static void
+updateProperty (IBusPanelService *panelService, IBusProperty *property, GfIBusManager *manager)
+{
+    g_signal_emit (manager, GF_IBUS_MANAGER_PROPERTY_UPDATED, 0, manager->currentEngineName, property);
+}
+
+static void
+get_global_engine_callback (IBusBus *ibus, guint i, 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);
+        onDisconnected (manager);
+        g_free (error);
+    }
+
+    if (!engineDescription) {
+        return;
+    }
+    onEngineChanged (manager, ibus_engine_desc_get_name (engineDescription));
+
+}
+
+static gboolean
+preload_engines_callback (gpointer user_data)
+{
+    GfIBusManager *manager;
+    g_return_val_if_fail (GF_IS_IBUS_MANAGER (user_data), G_SOURCE_REMOVE);
+
+    manager = GF_IBUS_MANAGER (user_data);
+    ibus_bus_preload_engines_async (manager->ibus, (const gchar *const *) manager->engineIds, -1, NULL, 
NULL, NULL);
+    manager->preloadEnginesTimerId = 0;
+    manager->engineIds = NULL;
+    return G_SOURCE_REMOVE;
+}
+
+static void
+initPanelService (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);
+        onDisconnected (manager);
+        g_free (error);
+    }
+
+    if (success) {
+        //FIXME: what to do about the object_path: IBus.PATH_PANEL that is passed to the panel service 
constructor in Javascript?
+        manager->panelService = ibus_panel_service_new (ibus_bus_get_connection (manager->ibus));
+
+        //FIXME: something about the candidatePopup, we deal with it later.
+        //this.candidatePopup.setPanelService (this.panelService);
+
+        g_signal_connect (manager->panelService, "update-property", G_CALLBACK (updateProperty), manager);
+        g_signal_connect (manager->panelService, "set-content-type", G_CALLBACK (setContentType), manager);
+
+        ibus_bus_get_global_engine_async (manager->ibus, -1, NULL, (GAsyncReadyCallback) 
get_global_engine_callback, manager);
+        updateReadiness (manager);
+    } else {
+        onDisconnected (manager);
+    }
+}
+
+void
+setContentType (IBusPanelService *panelService, guint purpose, guint hints, GfIBusManager *manager)
+{
+    g_signal_emit (manager, GF_IBUS_MANAGER_SET_CONTENT_TYPE, purpose, hints, manager);
+}
+
+static void
+spawn (void)
+{
+    // start a new subprocess for ibus-daemon
+    const char const *argv[] = {"ibus-daemon", "--xim", "--panel", "disabled"};
+    GError *error = NULL;
+    g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_NONE, &error);
+    if (error) {
+        g_log ("gnome-flashback", G_LOG_LEVEL_ERROR, _("Failed to launch ibus-daemon: %s"), error->message);
+        g_free (error);
+    }
+}
+
+static void
+gf_ibus_manager_class_init (GfIBusManagerClass *manager_class)
+{
+    gf_ibus_manager_signals[GF_IBUS_MANAGER_READY] =
+            g_signal_new ("ready",
+                          G_TYPE_FROM_CLASS (manager_class),
+                          G_SIGNAL_RUN_LAST,
+                          0 /* closure */,
+                          NULL /* accumulator */,
+                          NULL /* accumulator data */,
+                          g_cclosure_marshal_VOID__BOOLEAN,
+                          G_TYPE_NONE /* return_type */,
+                          1    /* n_params */,
+                          G_TYPE_PARAM_BOOLEAN/* param_types */);
+
+    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 /* offset */,
+                          NULL /* accumulator */,
+                          NULL /* accumulator data */,
+                          NULL /* marshaller */,
+                          G_TYPE_NONE /* return_type */,
+                          2    /* n_params */,
+                          G_TYPE_PARAM_STRING, IBUS_TYPE_PROP_LIST /* param_types */);
+
+    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 /* offset */,
+                          NULL /* accumulator */,
+                          NULL /* accumulator data */,
+                          NULL /* marshaller */,
+                          G_TYPE_NONE /* return_type */,
+                          2    /* n_params */,
+                          G_TYPE_PARAM_STRING, IBUS_TYPE_PROPERTY /* param_types */);
+
+    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 /* offset */,
+                          NULL /* accumulator */,
+                          NULL /* accumulator data */,
+                          NULL /* marshaller */,
+                          G_TYPE_NONE /* return_type */,
+                          2    /* n_params */,
+                          G_TYPE_PARAM_UINT, G_TYPE_PARAM_UINT/* param_types */);
+}
+
+static void
+gf_ibus_manager_init (GfIBusManager *manager)
+{
+    manager->engines = g_hash_table_new (g_str_hash, g_str_equal);
+
+    ibus_init ();
+
+    // candidate popup
+
+    manager->ibus = ibus_bus_new_async ();
+    g_signal_connect (manager->ibus, "connected", G_CALLBACK (onConnected), manager);
+    g_signal_connect (manager->ibus, "disconnected", G_CALLBACK (onDisconnected), 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), manager);
+
+    spawn ();
+}
+
+IBusEngineDesc *
+gf_ibus_manager_get_engine_description (GfIBusManager *manager, const char* id) {
+    g_return_val_if_fail (manager->engines, NULL);
+
+    if (!manager->ready) {
+        return NULL;
+    }
+
+    return g_hash_table_lookup (manager->engines, id);
+}
+
+void
+gf_ibus_manager_set_engine (GfIBusManager *manager, const char* id, GAsyncReadyCallback callback)
+{
+    if (!manager->ready) {
+        if (callback)
+            callback(G_OBJECT (manager), NULL, NULL);
+        return;
+    }
+
+    ibus_bus_set_global_engine_async (manager->ibus,
+                                      id,
+                                      GF_IBUS_MANAGER_MAX_INPUT_SOURCE_ACTIVATION_TIME,
+                                      NULL,
+                                      callback,
+                                      manager);
+}
+
+void
+gf_ibus_manager_preload_engines (GfIBusManager *manager, gchar **ids)
+{
+    if (!manager->ibus || g_strv_length (ids) == 0) {
+        return;
+    }
+
+    if (manager->preloadEnginesTimerId != 0) {
+        g_source_remove (manager->preloadEnginesTimerId);
+        manager->preloadEnginesTimerId = 0;
+    }
+
+    manager->engineIds = ids;
+    manager->preloadEnginesTimerId = g_timeout_add (GF_IBUS_MANAGER_PRELOAD_ENGINES_DELAY_TIME,
+                                                    preload_engines_callback, manager);
+}
+
+GfIBusManager *
+gf_ibus_manager_new (void)
+{
+    return g_object_new (GF_TYPE_IBUS_MANAGER, NULL);
+}
\ No newline at end of file
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..5edcea4
--- /dev/null
+++ b/gnome-flashback/libinput-sources/gf-ibus-manager.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 Alberts Muktupāvels
+ *
+ * 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 GNOME_FLASHBACK_GFIBUSMANAGER_H
+#define GNOME_FLASHBACK_GFIBUSMANAGER_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <ibus-1.0/ibus.h>
+
+#define GF_TYPE_IBUS_MANAGER gf_input_sources_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 char* id, GAsyncReadyCallback callback);
+
+IBusEngineDesc *
+gf_ibus_manager_get_engine_description (GfIBusManager *manager, const char* id);
+
+#endif
diff --git a/gnome-flashback/libinput-sources/gf-input-sources.c 
b/gnome-flashback/libinput-sources/gf-input-sources.c
index 568f495..a0ecfd4 100644
--- a/gnome-flashback/libinput-sources/gf-input-sources.c
+++ b/gnome-flashback/libinput-sources/gf-input-sources.c
@@ -18,24 +18,68 @@
 #include "config.h"
 
 #include "gf-input-sources.h"
+#include "gf-ibus-manager.h"
 
 struct _GfInputSources
 {
   GObject parent;
+
+  GfIBusManager *manager;
 };
 
 G_DEFINE_TYPE (GfInputSources, gf_input_sources, G_TYPE_OBJECT)
 
 static void
-gf_input_sources_class_init (GfInputSourcesClass *sources_class)
+ready_callback (GfIBusManager *manager,
+                gboolean ready,
+                gpointer user_data)
+{
+
+}
+
+static void
+properties_registered (GfIBusManager *manager,
+                       gchar* currentEngineName,
+                       IBusPropList *propertyList,
+                       gpointer user_data)
+{
+
+}
+
+static void
+property_updated (GfIBusManager *manager,
+                  gchar *currentEngineName,
+                  IBusProperty *property,
+                  gpointer user_data)
 {
+
+}
+
+static void
+set_content_type (GfIBusManager *manager,
+                  guint purpose,
+                  guint hint,
+                  gpointer user_data)
+{
+
 }
 
+
 static void
-gf_input_sources_init (GfInputSources *sources)
+gf_input_sources_class_init (GfInputSourcesClass *sources_class)
 {
 }
 
+static void
+gf_input_sources_init (GfInputSources *sources) {
+  sources->manager = gf_ibus_manager_new ();
+
+  g_signal_connect(sources->manager, "ready", G_CALLBACK (ready_callback), sources);
+  g_signal_connect(sources->manager, "properties-registered", G_CALLBACK (properties_registered), sources);
+  g_signal_connect(sources->manager, "property-updated", G_CALLBACK (property_updated), sources);
+  g_signal_connect(sources->manager, "set-content-type", G_CALLBACK (set_content_type), sources);
+}
+
 GfInputSources *
 gf_input_sources_new (void)
 {


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