[gnome-flashback] input-sources: implement org.gnome.Flashback.InputSources



commit 7c699fbc90a7c489e6176d54bff9964e74ded5a6
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Sun Dec 22 20:20:09 2019 +0200

    input-sources: implement org.gnome.Flashback.InputSources

 gnome-flashback/libinput-sources/Makefile.am       |   1 +
 .../libinput-sources/gf-input-sources.c            | 558 ++++++++++++++++++++-
 2 files changed, 558 insertions(+), 1 deletion(-)
---
diff --git a/gnome-flashback/libinput-sources/Makefile.am b/gnome-flashback/libinput-sources/Makefile.am
index 1a4feb9..da61b32 100644
--- a/gnome-flashback/libinput-sources/Makefile.am
+++ b/gnome-flashback/libinput-sources/Makefile.am
@@ -48,6 +48,7 @@ libinput_sources_la_LDFLAGS = \
        $(NULL)
 
 libinput_sources_la_LIBADD = \
+       $(top_builddir)/dbus/libdbus.la \
        $(top_builddir)/gnome-flashback/libcommon/libcommon.la \
        $(INPUT_SOURCES_LIBS) \
        $(LIBM) \
diff --git a/gnome-flashback/libinput-sources/gf-input-sources.c 
b/gnome-flashback/libinput-sources/gf-input-sources.c
index 40c352c..c60e2a7 100644
--- a/gnome-flashback/libinput-sources/gf-input-sources.c
+++ b/gnome-flashback/libinput-sources/gf-input-sources.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015-2016 Alberts Muktupāvels
+ * Copyright (C) 2015-2019 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
@@ -23,6 +23,7 @@
 #include <locale.h>
 #include <utime.h>
 
+#include "dbus/gf-input-sources-gen.h"
 #include "gf-ibus-manager.h"
 #include "gf-input-sources.h"
 #include "gf-input-source-manager.h"
@@ -34,6 +35,9 @@ struct _GfInputSources
 {
   GObject               parent;
 
+  guint                 bus_name_id;
+  GfInputSourcesGen    *input_sources;
+
   GfIBusManager        *ibus_manager;
   GfInputSourceManager *input_source_manager;
 
@@ -877,6 +881,7 @@ current_source_changed_cb (GfInputSourceManager *manager,
                            GfInputSource        *old_source,
                            GfInputSources       *sources)
 {
+  gf_input_sources_gen_emit_changed (sources->input_sources);
   update_status_icon (sources);
 }
 
@@ -888,6 +893,529 @@ status_icon_settings_changed_cb (GSettings      *settings,
   update_status_icon (sources);
 }
 
+static void
+append_icon_info (GVariantBuilder *builder,
+                  GfInputSources  *self)
+{
+  const char *display_name;
+  const char *icon_text;
+  IBusPropList *prop_list;
+
+  display_name = gf_input_source_get_display_name (self->current_source);
+  icon_text = gf_input_source_get_short_name (self->current_source);
+  prop_list = gf_input_source_get_properties (self->current_source);
+
+  if (prop_list != NULL)
+    {
+      guint i;
+
+      for (i = 0; i < prop_list->properties->len; i++)
+        {
+          IBusProperty *prop;
+          const char *key;
+          IBusText *symbol;
+          IBusText *label;
+          const char *tmp;
+
+          prop = ibus_prop_list_get (prop_list, i);
+
+          if (!ibus_property_get_visible (prop))
+            continue;
+
+          key = ibus_property_get_key (prop);
+          if (g_strcmp0 (key, "InputMode") != 0)
+            continue;
+
+          symbol = ibus_property_get_symbol (prop);
+          label = ibus_property_get_label (prop);
+
+          if (symbol != NULL)
+            tmp = ibus_text_get_text (symbol);
+          else
+            tmp = ibus_text_get_text (label);
+
+          if (tmp != NULL && *tmp != '\0' && g_utf8_strlen (tmp, -1) < 3)
+            icon_text = tmp;
+        }
+    }
+
+  g_variant_builder_add (builder, "{sv}", "tooltip",
+                         g_variant_new_string (display_name));
+
+  g_variant_builder_add (builder, "{sv}", "icon-text",
+                         g_variant_new_string (icon_text));
+}
+
+static const char *
+prop_type_to_string (IBusPropType type)
+{
+  switch (type)
+    {
+      case PROP_TYPE_NORMAL:
+        return "normal";
+
+      case PROP_TYPE_TOGGLE:
+        return "toggle";
+
+      case PROP_TYPE_RADIO:
+        return "radio";
+
+      case PROP_TYPE_MENU:
+        return "menu";
+
+      case PROP_TYPE_SEPARATOR:
+        return "separator";
+
+      default:
+        break;
+    }
+
+  return "unknown";
+}
+
+static const char *
+prop_state_to_string (IBusPropState state)
+{
+  switch (state)
+    {
+      case PROP_STATE_UNCHECKED:
+        return "unchecked";
+
+      case PROP_STATE_CHECKED:
+        return "checked";
+
+      case PROP_STATE_INCONSISTENT:
+        return "inconsistent";
+
+      default:
+        break;
+    }
+
+  return "unknown";
+}
+
+static GVariant *
+prop_list_to_variant (IBusPropList *prop_list)
+{
+  GVariantBuilder builder;
+  guint i;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sa{sv})"));
+
+  if (prop_list == NULL)
+    return g_variant_builder_end (&builder);
+
+  for (i = 0; i < prop_list->properties->len; i++)
+    {
+      IBusProperty *prop;
+      GVariantBuilder prop_builder;
+      IBusText *label;
+      IBusText *tooltip;
+      IBusPropType type;
+      IBusPropState state;
+      IBusPropList *sub_props;
+      const char *string;
+
+      prop = ibus_prop_list_get (prop_list, i);
+
+      if (!ibus_property_get_visible (prop))
+        continue;
+
+      g_variant_builder_init (&prop_builder, G_VARIANT_TYPE ("a{sv}"));
+
+      label = ibus_property_get_label (prop);
+      tooltip = ibus_property_get_tooltip (prop);
+      type = ibus_property_get_prop_type (prop);
+      state = ibus_property_get_state (prop);
+      sub_props = ibus_property_get_sub_props (prop);
+
+      string = ibus_property_get_icon (prop);
+      g_variant_builder_add (&prop_builder, "{sv}", "icon",
+                             g_variant_new_string (string));
+
+      string = ibus_text_get_text (label);
+      g_variant_builder_add (&prop_builder, "{sv}", "label",
+                             g_variant_new_string (string));
+
+      string = ibus_text_get_text (tooltip);
+      g_variant_builder_add (&prop_builder, "{sv}", "tooltip",
+                             g_variant_new_string (string));
+
+      string = prop_type_to_string (type);
+      g_variant_builder_add (&prop_builder, "{sv}", "type",
+                             g_variant_new_string (string));
+
+      string = prop_state_to_string (state);
+      g_variant_builder_add (&prop_builder, "{sv}", "state",
+                             g_variant_new_string (string));
+
+      if (type == PROP_TYPE_MENU)
+        {
+          g_variant_builder_add (&prop_builder, "{sv}", "menu",
+                                 prop_list_to_variant (sub_props));
+        }
+
+      g_variant_builder_add (&builder,
+                             "(sa{sv})",
+                             ibus_property_get_key (prop),
+                             &prop_builder);
+    }
+
+  return g_variant_builder_end (&builder);
+}
+
+static void
+append_properties (GVariantBuilder *builder,
+                   GfInputSources  *self)
+{
+  IBusPropList *prop_list;
+  GVariant *properties;
+
+  prop_list = gf_input_source_get_properties (self->current_source);
+  properties = prop_list_to_variant (prop_list);
+
+  g_variant_builder_add (builder, "{sv}", "properties", properties);
+}
+
+static void
+append_layout_info (GVariantBuilder *builder,
+                    GfInputSources  *self)
+{
+  char *layout;
+  char *layout_variant;
+  const char *type;
+  const char *id;
+  GVariant *variant;
+
+  layout = NULL;
+  layout_variant = NULL;
+
+  type = gf_input_source_get_source_type (self->current_source);
+  id = gf_input_source_get_id (self->current_source);
+
+  if (g_strcmp0 (type, INPUT_SOURCE_TYPE_XKB) == 0)
+    {
+      GnomeXkbInfo *info;
+      const char *xkb_layout;
+      const char *xkb_variant;
+
+      info = gnome_xkb_info_new ();
+
+      gnome_xkb_info_get_layout_info (info,
+                                      id,
+                                      NULL,
+                                      NULL,
+                                      &xkb_layout,
+                                      &xkb_variant);
+
+      layout = g_strdup (xkb_layout);
+      layout_variant = g_strdup (xkb_variant);
+
+      g_clear_object (&info);
+    }
+  else if (g_strcmp0 (type, INPUT_SOURCE_TYPE_IBUS) == 0)
+    {
+      IBusEngineDesc *engine_desc;
+
+      engine_desc = gf_ibus_manager_get_engine_desc (self->ibus_manager, id);
+
+      if (engine_desc)
+        {
+          const char *ibus_layout;
+          const char *ibus_variant;
+
+          ibus_layout = ibus_engine_desc_get_layout (engine_desc);
+          ibus_variant = ibus_engine_desc_get_layout_variant (engine_desc);
+
+          layout = g_strdup (ibus_layout);
+          layout_variant = g_strdup (ibus_variant);
+        }
+    }
+
+  if (layout != NULL)
+    {
+      variant = g_variant_new_string (layout);
+      g_variant_builder_add (builder, "{sv}", "layout", variant);
+    }
+
+  if (layout_variant != NULL)
+    {
+      variant = g_variant_new_string (layout_variant);
+      g_variant_builder_add (builder, "{sv}", "layout-variant", variant);
+    }
+
+  g_free (layout_variant);
+  g_free (layout);
+}
+
+static GVariant *
+get_input_sources (GfInputSources *self)
+{
+  GVariantBuilder builder;
+  GList *input_sources;
+  GList *l;
+
+  g_variant_builder_init (&builder,
+                          G_VARIANT_TYPE ("a(ussb)"));
+
+  input_sources = gf_input_source_manager_get_input_sources (self->input_source_manager);
+
+  for (l = input_sources; l != NULL; l = l->next)
+    {
+      GfInputSource *input_source;
+
+      input_source = GF_INPUT_SOURCE (l->data);
+
+      g_variant_builder_add (&builder,
+                             "(ussb)",
+                             gf_input_source_get_index (input_source),
+                             gf_input_source_get_short_name (input_source),
+                             gf_input_source_get_display_name (input_source),
+                             input_source == self->current_source);
+    }
+
+  g_list_free (input_sources);
+
+  return g_variant_builder_end (&builder);
+}
+
+static GVariant *
+get_current_source (GfInputSources *self)
+{
+  GVariantBuilder builder;
+
+  g_variant_builder_init (&builder,
+                          G_VARIANT_TYPE ("a{sv}"));
+
+  append_icon_info (&builder, self);
+  append_layout_info (&builder, self);
+  append_properties (&builder, self);
+
+  return g_variant_builder_end (&builder);
+}
+
+static IBusProperty *
+find_ibus_property (IBusPropList *prop_list,
+                    const char   *find_key)
+{
+  guint i;
+
+  if (prop_list == NULL)
+    return NULL;
+
+  for (i = 0; i < prop_list->properties->len; i++)
+    {
+      IBusProperty *prop;
+      const char *key;
+      IBusPropList *sub_props;
+
+      prop = ibus_prop_list_get (prop_list, i);
+      key = ibus_property_get_key (prop);
+
+      if (g_strcmp0 (key, find_key) == 0)
+        return prop;
+
+      sub_props = ibus_property_get_sub_props (prop);
+      prop = find_ibus_property (sub_props, find_key);
+
+      if (prop != NULL)
+        return prop;
+    }
+
+  return NULL;
+}
+
+static void
+activate_ibus_property (GfInputSources *self,
+                        const char     *key,
+                        IBusProperty   *property)
+{
+  IBusPropType type;
+  IBusPropState state;
+
+  type = ibus_property_get_prop_type (property);
+  state = ibus_property_get_state (property);
+
+  switch (type)
+    {
+      case PROP_TYPE_TOGGLE:
+      case PROP_TYPE_RADIO:
+        if (state == PROP_STATE_CHECKED)
+          state = PROP_STATE_UNCHECKED;
+        else
+          state = PROP_STATE_CHECKED;
+
+        ibus_property_set_state (property, state);
+        break;
+
+      case PROP_TYPE_MENU:
+      case PROP_TYPE_SEPARATOR:
+        g_assert_not_reached ();
+        break;
+
+      case PROP_TYPE_NORMAL:
+      default:
+        break;
+    }
+
+  gf_ibus_manager_activate_property (self->ibus_manager, key, state);
+}
+
+static gboolean
+handle_get_input_sources_cb (GfInputSourcesGen     *object,
+                             GDBusMethodInvocation *invocation,
+                             GfInputSources        *self)
+{
+  GVariant *input_sources;
+  GVariant *current_source;
+
+  input_sources = get_input_sources (self);
+  current_source = get_current_source (self);
+
+  gf_input_sources_gen_complete_get_input_sources (object,
+                                                   invocation,
+                                                   input_sources,
+                                                   current_source);
+
+  return TRUE;
+}
+
+static gboolean
+handle_activate_cb (GfInputSourcesGen     *object,
+                    GDBusMethodInvocation *invocation,
+                    guint                  index,
+                    GfInputSources        *self)
+{
+  GfInputSource *input_source;
+  GList *input_sources;
+  GList *l;
+
+  input_source = NULL;
+  input_sources = gf_input_source_manager_get_input_sources (self->input_source_manager);
+
+  for (l = input_sources; l != NULL; l = g_list_next (l))
+    {
+      input_source = GF_INPUT_SOURCE (l->data);
+
+      if (gf_input_source_get_index (input_source) == index)
+        break;
+
+      input_source = NULL;
+    }
+
+  g_list_free (input_sources);
+
+  if (input_source == NULL)
+    {
+      g_dbus_method_invocation_return_error (invocation,
+                                             G_DBUS_ERROR,
+                                             G_DBUS_ERROR_INVALID_ARGS,
+                                             "Input source with index %d does not exist",
+                                             index);
+
+      return TRUE;
+    }
+
+  gf_input_source_activate (input_source, TRUE);
+  gf_input_sources_gen_complete_activate (object, invocation);
+
+  return TRUE;
+}
+
+static gboolean
+handle_activate_property_cb (GfInputSourcesGen     *object,
+                             GDBusMethodInvocation *invocation,
+                             const char            *key,
+                             GfInputSources        *self)
+{
+  IBusPropList *prop_list;
+  IBusProperty *prop;
+
+  prop_list = gf_input_source_get_properties (self->current_source);
+
+  if (prop_list == NULL)
+    {
+      g_dbus_method_invocation_return_error (invocation,
+                                             G_DBUS_ERROR,
+                                             G_DBUS_ERROR_INVALID_ARGS,
+                                             "Input source does not have properties");
+
+      return TRUE;
+    }
+
+  prop = find_ibus_property (prop_list, key);
+
+  if (prop == NULL)
+    {
+      g_dbus_method_invocation_return_error (invocation,
+                                             G_DBUS_ERROR,
+                                             G_DBUS_ERROR_INVALID_ARGS,
+                                             "Input source does not have %s property",
+                                             key);
+
+      return TRUE;
+    }
+
+  activate_ibus_property (self, key, prop);
+  gf_input_sources_gen_complete_activate_property (object, invocation);
+
+  return TRUE;
+}
+
+static void
+bus_acquired_cb (GDBusConnection *connection,
+                 const gchar     *name,
+                 gpointer         user_data)
+{
+  GfInputSources *sources;
+  GDBusInterfaceSkeleton *skeleton;
+  GError *error;
+  gboolean exported;
+
+  sources = GF_INPUT_SOURCES (user_data);
+  skeleton = G_DBUS_INTERFACE_SKELETON (sources->input_sources);
+
+  g_signal_connect (skeleton, "handle-get-input-sources",
+                    G_CALLBACK (handle_get_input_sources_cb),
+                    sources);
+
+  g_signal_connect (skeleton, "handle-activate",
+                    G_CALLBACK (handle_activate_cb),
+                    sources);
+
+  g_signal_connect (skeleton, "handle-activate-property",
+                    G_CALLBACK (handle_activate_property_cb),
+                    sources);
+
+  error = NULL;
+  exported = g_dbus_interface_skeleton_export (skeleton,
+                                               connection,
+                                               "/org/gnome/Flashback/InputSources",
+                                               &error);
+
+  if (!exported)
+    {
+      g_warning ("Failed to export interface: %s", error->message);
+      g_error_free (error);
+
+      return;
+    }
+}
+
+static void
+name_acquired_cb (GDBusConnection *connection,
+                  const gchar     *name,
+                  gpointer         user_data)
+{
+}
+
+static void
+name_lost_cb (GDBusConnection *connection,
+              const gchar     *name,
+              gpointer         user_data)
+{
+}
+
 static void
 gf_input_sources_dispose (GObject *object)
 {
@@ -895,6 +1423,22 @@ gf_input_sources_dispose (GObject *object)
 
   sources = GF_INPUT_SOURCES (object);
 
+  if (sources->bus_name_id != 0)
+    {
+      g_bus_unown_name (sources->bus_name_id);
+      sources->bus_name_id = 0;
+    }
+
+  if (sources->input_sources)
+    {
+      GDBusInterfaceSkeleton *skeleton;
+
+      skeleton = G_DBUS_INTERFACE_SKELETON (sources->input_sources);
+
+      g_dbus_interface_skeleton_unexport (skeleton);
+      g_clear_object (&sources->input_sources);
+    }
+
   g_clear_object (&sources->ibus_manager);
   g_clear_object (&sources->input_source_manager);
 
@@ -934,6 +1478,18 @@ gf_input_sources_init (GfInputSources *sources)
 {
   const gchar *cache_dir;
 
+  sources->input_sources = gf_input_sources_gen_skeleton_new ();
+
+  sources->bus_name_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+                                         "org.gnome.Flashback.InputSources",
+                                         G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
+                                         G_BUS_NAME_OWNER_FLAGS_REPLACE,
+                                         bus_acquired_cb,
+                                         name_acquired_cb,
+                                         name_lost_cb,
+                                         sources,
+                                         NULL);
+
   sources->ibus_manager = gf_ibus_manager_new ();
   sources->input_source_manager = gf_input_source_manager_new (sources->ibus_manager);
 


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