[gnome-flashback] libkey-grabber: initial version



commit cfce8742780e8be335cf19dd49a993cc0ea63b13
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Sun Nov 2 15:05:10 2014 +0200

    libkey-grabber: initial version

 configure.ac                                       |    5 +
 data/org.gnome.gnome-flashback.gschema.xml.in.in   |    5 +
 gnome-flashback/Makefile.am                        |    2 +
 gnome-flashback/flashback-application.c            |   14 +
 gnome-flashback/libkey-grabber/Makefile.am         |   38 +++
 .../libkey-grabber/flashback-key-bindings.c        |  298 ++++++++++++++++++
 .../libkey-grabber/flashback-key-bindings.h        |   61 ++++
 .../libkey-grabber/flashback-key-grabber.c         |  319 ++++++++++++++++++++
 .../libkey-grabber/flashback-key-grabber.h         |   51 +++
 .../libkey-grabber/org.gnome.KeyGrabber.xml        |   23 ++
 10 files changed, 816 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 9fd98c2..b3cce26 100644
--- a/configure.ac
+++ b/configure.ac
@@ -57,6 +57,10 @@ 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(KEY_GRABBER, gtk+-3.0 >= $GTK_REQUIRED x11)
+AC_SUBST(KEY_GRABBER_CFLAGS)
+AC_SUBST(KEY_GRABBER_LIBS)
+
 PKG_CHECK_MODULES(GVC, gobject-2.0 libpulse libpulse-mainloop-glib)
 AM_CONDITIONAL(HAVE_INTROSPECTION, false)
 
@@ -73,6 +77,7 @@ gnome-flashback/libdesktop-background/Makefile
 gnome-flashback/libdisplay-config/Makefile
 gnome-flashback/libend-session-dialog/Makefile
 gnome-flashback/libidle-monitor/Makefile
+gnome-flashback/libkey-grabber/Makefile
 gnome-flashback/libsound-applet/Makefile
 gnome-flashback/libsound-applet/gvc/Makefile
 po/Makefile.in
diff --git a/data/org.gnome.gnome-flashback.gschema.xml.in.in 
b/data/org.gnome.gnome-flashback.gschema.xml.in.in
index 5c766a2..9e91388 100644
--- a/data/org.gnome.gnome-flashback.gschema.xml.in.in
+++ b/data/org.gnome.gnome-flashback.gschema.xml.in.in
@@ -25,6 +25,11 @@
                        <_summary>Idle monitor</_summary>
                        <_description>If set to true, then GNOME Flashback application will be used for user 
activity monitoring.</_description>
                </key>
+               <key name="key-grabber" type="b">
+                       <default>true</default>
+                       <_summary>Key grabber</_summary>
+                       <_description>If set to true, then GNOME Flashback application will be used for key 
grabbing.</_description>
+               </key>
                <key name="sound-applet" type="b">
                        <default>true</default>
                        <_summary>Sound applet</_summary>
diff --git a/gnome-flashback/Makefile.am b/gnome-flashback/Makefile.am
index 3a10388..b250193 100644
--- a/gnome-flashback/Makefile.am
+++ b/gnome-flashback/Makefile.am
@@ -4,6 +4,7 @@ SUBDIRS = \
        libdisplay-config \
        libend-session-dialog \
        libidle-monitor \
+       libkey-grabber \
        libsound-applet
 
 bin_PROGRAMS = \
@@ -28,6 +29,7 @@ gnome_flashback_LDADD = \
        $(top_builddir)/gnome-flashback/libdisplay-config/libdisplay-config.la \
        $(top_builddir)/gnome-flashback/libend-session-dialog/libend-session-dialog.la \
        $(top_builddir)/gnome-flashback/libidle-monitor/libidle-monitor.la \
+       $(top_builddir)/gnome-flashback/libkey-grabber/libkey-grabber.la \
        $(top_builddir)/gnome-flashback/libsound-applet/libsound-applet.la
 
 -include $(top_srcdir)/git.mk
diff --git a/gnome-flashback/flashback-application.c b/gnome-flashback/flashback-application.c
index 3153128..214c0ca 100644
--- a/gnome-flashback/flashback-application.c
+++ b/gnome-flashback/flashback-application.c
@@ -24,6 +24,7 @@
 #include "libdisplay-config/flashback-display-config.h"
 #include "libend-session-dialog/flashback-end-session-dialog.h"
 #include "libidle-monitor/meta-idle-monitor-dbus.h"
+#include "libkey-grabber/flashback-key-grabber.h"
 #include "libsound-applet/gvc-applet.h"
 
 #define FLASHBACK_SCHEMA       "org.gnome.gnome-flashback"
@@ -31,6 +32,7 @@
 #define KEY_DESKTOP_BACKGROUND "desktop-background"
 #define KEY_DISPLAY_CONFIG     "display-config"
 #define KEY_END_SESSION_DIALOG "end-session-dialog"
+#define KEY_KEY_GRABBER        "key-grabber"
 #define KEY_IDLE_MONITOR       "idle-monitor"
 #define KEY_SOUND_APPLET       "sound-applet"
 
@@ -40,6 +42,7 @@ struct _FlashbackApplicationPrivate {
        DesktopBackground          *background;
        FlashbackDisplayConfig     *config;
        FlashbackEndSessionDialog  *dialog;
+       FlashbackKeyGrabber        *grabber;
        MetaIdleMonitorDBus        *idle_monitor;
        GvcApplet                  *applet;
 };
@@ -106,6 +109,16 @@ flashback_application_settings_changed (GSettings   *settings,
                }
        }
 
+       if (key == NULL || g_strcmp0 (key, KEY_KEY_GRABBER) == 0) {
+               if (g_settings_get_boolean (settings, KEY_KEY_GRABBER)) {
+                       if (app->priv->grabber == NULL) {
+                               app->priv->grabber = flashback_key_grabber_new ();
+                       }
+               } else {
+                       g_clear_object (&app->priv->grabber);
+               }
+       }
+
        if (key == NULL || g_strcmp0 (key, KEY_SOUND_APPLET) == 0) {
                if (g_settings_get_boolean (settings, KEY_SOUND_APPLET)) {
                        if (app->priv->applet == NULL) {
@@ -126,6 +139,7 @@ flashback_application_finalize (GObject *object)
        g_clear_object (&app->priv->config);
        g_clear_object (&app->priv->dialog);
        g_clear_object (&app->priv->idle_monitor);
+       g_clear_object (&app->priv->grabber);
        g_clear_object (&app->priv->applet);
        g_clear_object (&app->priv->settings);
 
diff --git a/gnome-flashback/libkey-grabber/Makefile.am b/gnome-flashback/libkey-grabber/Makefile.am
new file mode 100644
index 0000000..5f59776
--- /dev/null
+++ b/gnome-flashback/libkey-grabber/Makefile.am
@@ -0,0 +1,38 @@
+noinst_LTLIBRARIES = \
+       libkey-grabber.la
+
+AM_CPPFLAGS = \
+       $(KEY_GRABBER_CFLAGS) \
+       -I$(top_builddir)/gnome-flashback/libkey-grabber
+
+libkey_grabber_la_SOURCES = \
+       dbus-key-grabber.c \
+       dbus-key-grabber.h \
+       flashback-key-grabber.c \
+       flashback-key-grabber.h \
+       flashback-key-bindings.c \
+       flashback-key-bindings.h
+
+libkey_grabber_la_LIBADD = \
+       $(KEY_GRABBER_LIBS)
+
+dbus-key-grabber.h:
+dbus-key-grabber.c: org.gnome.KeyGrabber.xml
+       $(AM_V_GEN) gdbus-codegen \
+               --interface-prefix org.gnome. \
+               --c-namespace DBus \
+               --generate-c-code dbus-key-grabber \
+               --c-generate-object-manager \
+               $(srcdir)/org.gnome.KeyGrabber.xml
+
+BUILT_SOURCES = \
+       dbus-key-grabber.c \
+       dbus-key-grabber.h
+
+EXTRA_DIST = \
+       org.gnome.KeyGrabber.xml
+
+CLEANFILES = \
+       $(BUILT_SOURCES)
+
+-include $(top_srcdir)/git.mk
diff --git a/gnome-flashback/libkey-grabber/flashback-key-bindings.c 
b/gnome-flashback/libkey-grabber/flashback-key-bindings.c
new file mode 100644
index 0000000..41f59d7
--- /dev/null
+++ b/gnome-flashback/libkey-grabber/flashback-key-bindings.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2014 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/>.
+ */
+
+#include <config.h>
+#include <gtk/gtk.h>
+#include <gtk/gtkx.h>
+
+#include <X11/Xlib.h>
+
+#include "flashback-key-bindings.h"
+
+struct _FlashbackKeyBindingsPrivate {
+       GHashTable *table;
+
+       Display    *xdisplay;
+       Window      xwindow;
+
+       guint       ignored_modifier_mask;
+};
+
+enum {
+       BINDING_ACTIVATED,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef struct {
+       const gchar *name;
+       guint        action;
+       guint        keyval;
+       guint        keycode;
+       guint        modifiers;
+} KeyBinding;
+
+static guint MetaMask = 0;
+static guint SuperMask = 0;
+static guint HyperMask = 0;
+static guint NumLockMask = 0;
+static guint ScrollLockMask = 0;
+
+G_DEFINE_TYPE_WITH_PRIVATE (FlashbackKeyBindings, flashback_key_bindings, G_TYPE_OBJECT)
+
+static guint
+get_real_modifiers (GdkModifierType modifiers)
+{
+       guint mods = 0;
+
+       if (modifiers & GDK_SHIFT_MASK)
+               mods |= ShiftMask;
+       if (modifiers & GDK_CONTROL_MASK)
+               mods |= ControlMask;
+       if (modifiers & GDK_MOD1_MASK)
+               mods |= Mod1Mask;
+       if (modifiers & GDK_META_MASK)
+               mods |= MetaMask;
+       if (modifiers & GDK_HYPER_MASK)
+               mods |= HyperMask;
+       if (modifiers & GDK_SUPER_MASK)
+               mods |= SuperMask;
+       if (modifiers & GDK_MOD2_MASK)
+               mods |= Mod2Mask;
+       if (modifiers & GDK_MOD3_MASK)
+               mods |= Mod3Mask;
+       if (modifiers & GDK_MOD4_MASK)
+               mods |= Mod4Mask;
+       if (modifiers & GDK_MOD5_MASK)
+               mods |= Mod5Mask;
+
+       return mods;
+}
+
+static GdkFilterReturn
+filter_func (GdkXEvent *xevent,
+             GdkEvent  *event,
+             gpointer   user_data)
+{
+       FlashbackKeyBindings *bindings;
+       XEvent *ev;
+
+       bindings = FLASHBACK_KEY_BINDINGS (user_data);
+       ev = xevent;
+
+       XAllowEvents (bindings->priv->xdisplay, AsyncKeyboard, ev->xkey.time);
+
+       if (ev->type == KeyPress) {
+               GList *values, *l;
+
+               values = g_hash_table_get_values (bindings->priv->table);
+
+               for (l = values; l; l = l->next) {
+                       KeyBinding *binding = l->data;
+
+                       if (binding->keycode == ev->xkey.keycode &&
+                           binding->modifiers == (ev->xkey.state & 0xff & 
~(bindings->priv->ignored_modifier_mask))) {
+                               g_signal_emit (bindings, signals[BINDING_ACTIVATED], 0,
+                                              binding->action, 0, 0);
+                               break;
+                       }
+               }
+
+               g_list_free (values);
+       }
+
+       return GDK_FILTER_CONTINUE;
+}
+
+static void
+flashback_key_bindings_change_keygrab (FlashbackKeyBindings *bindings,
+                                       gboolean              grab,
+                                       gint                  keyval,
+                                       guint                 keycode,
+                                       guint                 modifiers)
+{
+       guint ignored_mask;
+
+       gdk_error_trap_push ();
+
+       ignored_mask = 0;
+       while (ignored_mask <= bindings->priv->ignored_modifier_mask) {
+               if (ignored_mask & ~(bindings->priv->ignored_modifier_mask)) {
+                       ++ignored_mask;
+                       continue;
+               }
+
+               if (grab)
+                       XGrabKey (bindings->priv->xdisplay, keycode,
+                                 modifiers | ignored_mask,
+                                 bindings->priv->xwindow,
+                                 True,
+                                 GrabModeAsync, GrabModeSync);
+               else
+                       XUngrabKey (bindings->priv->xdisplay, keycode,
+                                   modifiers | ignored_mask,
+                                   bindings->priv->xwindow);
+
+               ++ignored_mask;
+       }
+
+       gdk_error_trap_pop_ignored ();
+}
+
+static void
+flashback_key_bindings_finalize (GObject *object)
+{
+       FlashbackKeyBindings *bindings;
+
+       bindings = FLASHBACK_KEY_BINDINGS (object);
+
+       gdk_window_remove_filter (NULL, filter_func, bindings);
+
+       if (bindings->priv->table) {
+               g_hash_table_destroy (bindings->priv->table);
+               bindings->priv->table = NULL;
+       }
+
+       G_OBJECT_CLASS (flashback_key_bindings_parent_class)->finalize (object);
+}
+
+static void
+flashback_key_bindings_init (FlashbackKeyBindings *bindings)
+{
+       FlashbackKeyBindingsPrivate *priv;
+
+       bindings->priv = flashback_key_bindings_get_instance_private (bindings);
+       priv = bindings->priv;
+
+       priv->xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+       priv->xwindow = XDefaultRootWindow (priv->xdisplay);
+       priv->table = g_hash_table_new_full (NULL, NULL, NULL, g_free);
+
+       MetaMask = XkbKeysymToModifiers (priv->xdisplay, XK_Meta_L);
+       if (MetaMask == 0)
+               MetaMask = XkbKeysymToModifiers (priv->xdisplay, XK_Meta_R);
+
+       SuperMask = XkbKeysymToModifiers (priv->xdisplay, XK_Super_L);
+       if (SuperMask == 0)
+               SuperMask = XkbKeysymToModifiers (priv->xdisplay, XK_Super_R);
+
+       HyperMask = XkbKeysymToModifiers (priv->xdisplay, XK_Hyper_L);
+       if (HyperMask == 0)
+               HyperMask = XkbKeysymToModifiers (priv->xdisplay, XK_Hyper_R);
+
+       NumLockMask = XkbKeysymToModifiers (priv->xdisplay, XK_Num_Lock);
+       ScrollLockMask = XkbKeysymToModifiers (priv->xdisplay, XK_Scroll_Lock);
+
+       priv->ignored_modifier_mask = NumLockMask | ScrollLockMask | LockMask;
+
+       gdk_window_add_filter (NULL, filter_func, bindings);
+}
+
+static void
+flashback_key_bindings_class_init (FlashbackKeyBindingsClass *class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+       object_class->finalize = flashback_key_bindings_finalize;
+
+       signals[BINDING_ACTIVATED] =
+               g_signal_new ("binding-activated",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_LAST,
+                             G_STRUCT_OFFSET (FlashbackKeyBindingsClass, binding_activated),
+                             NULL, NULL, NULL,
+                             G_TYPE_NONE,
+                             3,
+                             G_TYPE_UINT,
+                             G_TYPE_UINT,
+                             G_TYPE_UINT);
+}
+
+FlashbackKeyBindings *
+flashback_key_bindings_new (void)
+{
+       return g_object_new (FLASHBACK_TYPE_KEY_BINDINGS, NULL);
+}
+
+guint
+flashback_key_bindings_grab (FlashbackKeyBindings *bindings,
+                             const gchar          *accelerator)
+{
+       KeyBinding *binding;
+       guint keyval;
+       GdkModifierType modifiers;
+       guint real_modifiers;
+       guint keycode;
+       static next_action = 0;
+
+       gtk_accelerator_parse (accelerator, &keyval, &modifiers);
+       if (!gtk_accelerator_valid (keyval, modifiers)) {
+               return 0;
+       }
+
+       if (keyval == 0)
+               return 0;
+
+       keycode = XKeysymToKeycode (bindings->priv->xdisplay, keyval);
+       if (keycode == 0)
+               return 0;
+
+       real_modifiers = get_real_modifiers (modifiers);
+
+       flashback_key_bindings_change_keygrab (bindings,
+                                              TRUE,
+                                              keyval,
+                                              keycode,
+                                              real_modifiers);
+
+       binding = g_new0 (KeyBinding, 1);
+
+       binding->name = accelerator;
+       binding->action = ++next_action;
+       binding->keyval = keyval;
+       binding->keycode = keycode;
+       binding->modifiers = real_modifiers;
+
+       g_hash_table_insert (bindings->priv->table, GUINT_TO_POINTER (binding->action), binding);
+
+       return binding->action;
+}
+
+gboolean
+flashback_key_bindings_ungrab (FlashbackKeyBindings *bindings,
+                               guint                 action)
+{
+       KeyBinding *binding;
+
+       binding = (KeyBinding *) g_hash_table_lookup (bindings->priv->table,
+                                                     GUINT_TO_POINTER (action));
+
+       if (binding == NULL)
+               return FALSE;
+
+       flashback_key_bindings_change_keygrab (bindings,
+                                              FALSE,
+                                              binding->keyval,
+                                              binding->keycode,
+                                              binding->modifiers);
+
+       g_hash_table_remove (bindings->priv->table,
+                            GUINT_TO_POINTER (action));
+
+       return TRUE;
+}
diff --git a/gnome-flashback/libkey-grabber/flashback-key-bindings.h 
b/gnome-flashback/libkey-grabber/flashback-key-bindings.h
new file mode 100644
index 0000000..e53fabc
--- /dev/null
+++ b/gnome-flashback/libkey-grabber/flashback-key-bindings.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 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 FLASHBACK_KEY_BINDINGS_H
+#define FLASHBACK_KEY_BINDINGS_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define FLASHBACK_TYPE_KEY_BINDINGS         (flashback_key_bindings_get_type ())
+#define FLASHBACK_KEY_BINDINGS(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), FLASHBACK_TYPE_KEY_BINDINGS, 
FlashbackKeyBindings))
+#define FLASHBACK_KEY_BINDINGS_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c),    FLASHBACK_TYPE_KEY_BINDINGS, 
FlashbackKeyBindingsClass))
+#define FLASHBACK_IS_KEY_BINDINGS(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), FLASHBACK_TYPE_KEY_BINDINGS))
+#define FLASHBACK_IS_KEY_BINDINGS_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c),    FLASHBACK_TYPE_KEY_BINDINGS))
+#define FLASHBACK_KEY_BINDINGS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o),   FLASHBACK_TYPE_KEY_BINDINGS, 
FlashbackKeyBindingsClass))
+
+typedef struct _FlashbackKeyBindings        FlashbackKeyBindings;
+typedef struct _FlashbackKeyBindingsClass   FlashbackKeyBindingsClass;
+typedef struct _FlashbackKeyBindingsPrivate FlashbackKeyBindingsPrivate;
+
+struct _FlashbackKeyBindings {
+       GObject                      parent;
+       FlashbackKeyBindingsPrivate *priv;
+};
+
+struct _FlashbackKeyBindingsClass {
+    GObjectClass parent_class;
+
+       void (*binding_activated) (FlashbackKeyBindings *bindings,
+                                  guint                 action,
+                                  guint                 device,
+                                  guint                 timestamp);
+};
+
+GType                 flashback_key_bindings_get_type (void);
+
+FlashbackKeyBindings *flashback_key_bindings_new      (void);
+
+guint                 flashback_key_bindings_grab     (FlashbackKeyBindings *bindings,
+                                                       const gchar          *accelerator);
+gboolean              flashback_key_bindings_ungrab   (FlashbackKeyBindings *bindings,
+                                                       guint                 action);
+
+G_END_DECLS
+
+#endif
diff --git a/gnome-flashback/libkey-grabber/flashback-key-grabber.c 
b/gnome-flashback/libkey-grabber/flashback-key-grabber.c
new file mode 100644
index 0000000..e83d40e
--- /dev/null
+++ b/gnome-flashback/libkey-grabber/flashback-key-grabber.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2014 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/>.
+ */
+
+#include <config.h>
+#include <gtk/gtk.h>
+
+#include "flashback-key-grabber.h"
+#include "flashback-key-bindings.h"
+#include "dbus-key-grabber.h"
+
+#define KEY_GRABBER_DBUS_NAME "org.gnome.Shell"
+#define KEY_GRABBER_DBUS_PATH "/org/gnome/Shell"
+
+struct _FlashbackKeyGrabberPrivate {
+       gint                    bus_name;
+       GDBusInterfaceSkeleton *iface;
+
+       GHashTable             *grabbed_accelerators;
+       GHashTable             *grabbers;
+
+       FlashbackKeyBindings   *bindings;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (FlashbackKeyGrabber, flashback_key_grabber, G_TYPE_OBJECT)
+
+static void
+binding_activated (FlashbackKeyBindings *bindings,
+                   guint                 action,
+                   guint                 device,
+                   guint                 timestamp,
+                   gpointer              user_data)
+{
+       FlashbackKeyGrabber *grabber;
+
+       grabber = FLASHBACK_KEY_GRABBER (user_data);
+
+       dbus_key_grabber_emit_accelerator_activated (DBUS_KEY_GRABBER (grabber->priv->iface),
+                                                    action,
+                                                    device,
+                                                    timestamp);
+}
+
+static gint
+real_grab (FlashbackKeyGrabber *grabber,
+           const gchar         *accelerator)
+{
+       return flashback_key_bindings_grab (grabber->priv->bindings, accelerator);
+}
+
+static gboolean
+real_ungrab (FlashbackKeyGrabber *grabber,
+             gint                 action)
+{
+       return flashback_key_bindings_ungrab (grabber->priv->bindings, action);
+}
+
+typedef struct {
+       const gchar *sender;
+       FlashbackKeyGrabber *grabber;
+} Data;
+
+static void
+ungrab_accelerator (gpointer key,
+                    gpointer value,
+                    gpointer user_data)
+{
+       guint action;
+       gchar *sender;
+       Data *data;
+
+       action = GPOINTER_TO_UINT (key);
+       sender = (gchar *) value;
+       data = (Data *) user_data;
+
+       if (g_str_equal (sender, data->sender)) {
+               if (real_ungrab (data->grabber, action))
+                       g_hash_table_remove (data->grabber->priv->grabbed_accelerators, key);
+       }
+}
+
+static void
+name_vanished_handler (GDBusConnection *connection,
+                       const gchar     *name,
+                       gpointer         user_data)
+{
+       FlashbackKeyGrabber *grabber;
+       guint id;
+       Data *data;
+
+       grabber = FLASHBACK_KEY_GRABBER (user_data);
+       id = GPOINTER_TO_UINT (g_hash_table_lookup (grabber->priv->grabbers, name));
+       data = g_new0 (Data, 1);
+
+       data->sender = name;
+       data->grabber = grabber;
+
+       g_hash_table_foreach (grabber->priv->grabbed_accelerators, (GHFunc) ungrab_accelerator, data);
+       g_free (data);
+
+       g_bus_unwatch_name (id);
+       g_hash_table_remove (grabber->priv->grabbers, name);
+}
+
+static guint
+grab_accelerator (FlashbackKeyGrabber *grabber,
+                  const gchar         *accelerator,
+                  guint                flags,
+                  const gchar         *sender)
+{
+       guint action;
+
+       action = real_grab (grabber, accelerator);
+       g_hash_table_insert (grabber->priv->grabbed_accelerators,
+                            GUINT_TO_POINTER (action),
+                            g_strdup (sender));
+
+       if (g_hash_table_lookup (grabber->priv->grabbers, sender) == NULL) {
+               guint id = g_bus_watch_name (G_BUS_TYPE_SESSION,
+                                            sender,
+                                            G_BUS_NAME_WATCHER_FLAGS_NONE,
+                                            NULL,
+                                            (GBusNameVanishedCallback) name_vanished_handler,
+                                            grabber,
+                                            NULL);
+               g_hash_table_insert (grabber->priv->grabbers,
+                                    g_strdup (sender),
+                                    GUINT_TO_POINTER (id));
+       }
+
+       return action;
+}
+
+static gboolean
+handle_grab_accelerator (DBusKeyGrabber        *object,
+                         GDBusMethodInvocation *invocation,
+                         const gchar           *accelerator,
+                         guint                  flags,
+                         FlashbackKeyGrabber   *grabber)
+{
+       const gchar *sender;
+       guint action;
+
+       sender = g_dbus_method_invocation_get_sender (invocation);
+       action = grab_accelerator (grabber, accelerator, flags, sender);
+
+       dbus_key_grabber_complete_grab_accelerator (object, invocation, action);
+
+       return TRUE;
+}
+
+static gboolean
+handle_grab_accelerators (DBusKeyGrabber        *object,
+                          GDBusMethodInvocation *invocation,
+                          GVariant              *accelerators,
+                          FlashbackKeyGrabber   *grabber)
+{
+       GVariantBuilder builder;
+       GVariantIter iter;
+       GVariant *child;
+       const gchar *sender;
+
+       g_variant_builder_init (&builder, G_VARIANT_TYPE("au"));
+       g_variant_iter_init (&iter, accelerators);
+
+       sender = g_dbus_method_invocation_get_sender (invocation);
+
+       while ((child = g_variant_iter_next_value (&iter))) {
+               gchar *accelerator;
+               guint flags;
+               guint action;
+
+               g_variant_get (child, "(su)", &accelerator, &flags);
+
+               action = grab_accelerator (grabber, accelerator, flags, sender);
+               g_variant_builder_add (&builder, "u", action);
+
+               g_free (accelerator);
+               g_variant_unref (child);
+       }
+
+       dbus_key_grabber_complete_grab_accelerators (object, invocation, g_variant_builder_end (&builder));
+
+       return TRUE;
+}
+
+static gboolean
+handle_ungrab_accelerator (DBusKeyGrabber        *object,
+                           GDBusMethodInvocation *invocation,
+                           guint                  action,
+                           FlashbackKeyGrabber   *grabber)
+{
+       gchar *sender;
+       gboolean ret;
+
+       ret = FALSE;
+       sender = (gchar *) g_hash_table_lookup (grabber->priv->grabbed_accelerators,
+                                               GUINT_TO_POINTER (action));
+
+       if (g_str_equal (sender, g_dbus_method_invocation_get_sender (invocation))) {
+               ret = real_ungrab (grabber, action);
+
+               if (ret)
+                       g_hash_table_remove (grabber->priv->grabbed_accelerators, GUINT_TO_POINTER (action));
+       }
+
+       dbus_key_grabber_complete_ungrab_accelerator (object, invocation, ret);
+
+       return TRUE;
+}
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+                 const gchar     *name,
+                 gpointer         user_data)
+{
+       FlashbackKeyGrabber *grabber;
+       DBusKeyGrabber *skeleton;
+       GError *error;
+
+       grabber = FLASHBACK_KEY_GRABBER (user_data);
+       skeleton = dbus_key_grabber_skeleton_new ();
+
+       grabber->priv->iface = G_DBUS_INTERFACE_SKELETON (skeleton);
+
+       g_signal_connect (grabber->priv->iface, "handle-grab-accelerator",
+                         G_CALLBACK (handle_grab_accelerator), grabber);
+       g_signal_connect (grabber->priv->iface, "handle-grab-accelerators",
+                         G_CALLBACK (handle_grab_accelerators), grabber);
+       g_signal_connect (grabber->priv->iface, "handle-ungrab-accelerator",
+                         G_CALLBACK (handle_ungrab_accelerator), grabber);
+
+       error = NULL;
+       if (!g_dbus_interface_skeleton_export (grabber->priv->iface,
+                                              connection,
+                                              KEY_GRABBER_DBUS_PATH,
+                                              &error)) {
+               g_warning ("Failed to export interface: %s", error->message);
+               g_error_free (error);
+               return;
+       }
+}
+
+static void
+flashback_key_grabber_finalize (GObject *object)
+{
+       FlashbackKeyGrabber *grabber;
+
+       grabber = FLASHBACK_KEY_GRABBER (object);
+
+       if (grabber->priv->bus_name) {
+               g_bus_unown_name (grabber->priv->bus_name);
+               grabber->priv->bus_name = 0;
+       }
+
+       if (grabber->priv->grabbed_accelerators) {
+               g_hash_table_destroy (grabber->priv->grabbed_accelerators);
+               grabber->priv->grabbed_accelerators = NULL;
+       }
+
+       if (grabber->priv->grabbers) {
+               g_hash_table_destroy (grabber->priv->grabbers);
+               grabber->priv->grabbers = NULL;
+       }
+
+       g_clear_object (&grabber->priv->bindings);
+
+       G_OBJECT_CLASS (flashback_key_grabber_parent_class)->finalize (object);
+}
+
+static void
+flashback_key_grabber_init (FlashbackKeyGrabber *grabber)
+{
+       grabber->priv = flashback_key_grabber_get_instance_private (grabber);
+
+       grabber->priv->grabbed_accelerators = g_hash_table_new_full (NULL, NULL, NULL, g_free);
+       grabber->priv->grabbers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+       grabber->priv->bindings = flashback_key_bindings_new ();
+       g_signal_connect (grabber->priv->bindings, "binding-activated",
+                         G_CALLBACK (binding_activated), grabber);
+
+       grabber->priv->bus_name = g_bus_own_name (G_BUS_TYPE_SESSION,
+                                                 KEY_GRABBER_DBUS_NAME,
+                                                 G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
+                                                 G_BUS_NAME_OWNER_FLAGS_REPLACE,
+                                                 on_bus_acquired,
+                                                 NULL,
+                                                 NULL,
+                                                 grabber,
+                                                 NULL);
+}
+
+static void
+flashback_key_grabber_class_init (FlashbackKeyGrabberClass *class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+       object_class->finalize = flashback_key_grabber_finalize;
+}
+
+FlashbackKeyGrabber *
+flashback_key_grabber_new (void)
+{
+       return g_object_new (FLASHBACK_TYPE_KEY_GRABBER, NULL);
+}
diff --git a/gnome-flashback/libkey-grabber/flashback-key-grabber.h 
b/gnome-flashback/libkey-grabber/flashback-key-grabber.h
new file mode 100644
index 0000000..ac8728d
--- /dev/null
+++ b/gnome-flashback/libkey-grabber/flashback-key-grabber.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 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 FLASHBACK_KEY_GRABBER_H
+#define FLASHBACK_KEY_GRABBER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define FLASHBACK_TYPE_KEY_GRABBER         (flashback_key_grabber_get_type ())
+#define FLASHBACK_KEY_GRABBER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), FLASHBACK_TYPE_KEY_GRABBER, 
FlashbackKeyGrabber))
+#define FLASHBACK_KEY_GRABBER_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c),    FLASHBACK_TYPE_KEY_GRABBER, 
FlashbackKeyGrabberClass))
+#define FLASHBACK_IS_KEY_GRABBER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), FLASHBACK_TYPE_KEY_GRABBER))
+#define FLASHBACK_IS_KEY_GRABBER_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c),    FLASHBACK_TYPE_KEY_GRABBER))
+#define FLASHBACK_KEY_GRABBER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o),   FLASHBACK_TYPE_KEY_GRABBER, 
FlashbackKeyGrabberClass))
+
+typedef struct _FlashbackKeyGrabber        FlashbackKeyGrabber;
+typedef struct _FlashbackKeyGrabberClass   FlashbackKeyGrabberClass;
+typedef struct _FlashbackKeyGrabberPrivate FlashbackKeyGrabberPrivate;
+
+struct _FlashbackKeyGrabber {
+       GObject                     parent;
+       FlashbackKeyGrabberPrivate *priv;
+};
+
+struct _FlashbackKeyGrabberClass {
+    GObjectClass parent_class;
+};
+
+GType                flashback_key_grabber_get_type (void);
+
+FlashbackKeyGrabber *flashback_key_grabber_new      (void);
+
+G_END_DECLS
+
+#endif
diff --git a/gnome-flashback/libkey-grabber/org.gnome.KeyGrabber.xml 
b/gnome-flashback/libkey-grabber/org.gnome.KeyGrabber.xml
new file mode 100644
index 0000000..b47e9c8
--- /dev/null
+++ b/gnome-flashback/libkey-grabber/org.gnome.KeyGrabber.xml
@@ -0,0 +1,23 @@
+<node>
+  <interface name="org.gnome.Shell">
+    <annotation name="org.gtk.GDBus.C.Name" value="KeyGrabber"/>
+    <method name="GrabAccelerator">
+      <arg type="s" direction="in" name="accelerator"/>
+      <arg type="u" direction="in" name="flags"/>
+      <arg type="u" direction="out" name="action"/>
+    </method>
+    <method name="GrabAccelerators">
+      <arg type="a(su)" direction="in" name="accelerators"/>
+      <arg type="au" direction="out" name="actions"/>
+    </method>
+    <method name="UngrabAccelerator">
+      <arg type="u" direction="in" name="action"/>
+      <arg type="b" direction="out" name="success"/>
+    </method>
+    <signal name="AcceleratorActivated">
+      <arg type="u" name="action"/>
+      <arg type="u" name="device"/>
+      <arg type="u" name="timestamp"/>
+    </signal>
+  </interface>
+</node>


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