[gnome-flashback] common: add support for modifiers-only keybindings
- From: Alberts Muktupāvels <muktupavels src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-flashback] common: add support for modifiers-only keybindings
- Date: Wed, 14 Oct 2015 05:46:53 +0000 (UTC)
commit 971c7ac6a56d05ba86efa27e9b01829ce9dc0775
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date: Wed Oct 14 04:03:05 2015 +0300
common: add support for modifiers-only keybindings
More precisely - support for ISO_Next_Group that will be used to
switch input sources.
gnome-flashback/libcommon/gf-keybindings.c | 440 ++++++++++++++++++--
gnome-flashback/libcommon/gf-keybindings.h | 2 +-
.../libinput-sources/gf-input-source-manager.c | 2 +-
gnome-flashback/libshell/flashback-shell.c | 2 +-
4 files changed, 416 insertions(+), 30 deletions(-)
---
diff --git a/gnome-flashback/libcommon/gf-keybindings.c b/gnome-flashback/libcommon/gf-keybindings.c
index cf2ed9d..8313b05 100644
--- a/gnome-flashback/libcommon/gf-keybindings.c
+++ b/gnome-flashback/libcommon/gf-keybindings.c
@@ -24,11 +24,21 @@
#include "gf-keybindings.h"
+#define DESKTOP_INPUT_SOURCES_SCHEMA "org.gnome.desktop.input-sources"
+
+#define KEY_XKB_OPTIONS "xkb-options"
+
struct _GfKeybindings
{
GObject parent;
GHashTable *keybindings;
+ GHashTable *iso_next_group_keybindings;
+
+ gboolean iso_next_group;
+
+ GSettings *settings;
+ gchar *iso_next_group_option;
Display *xdisplay;
Window xwindow;
@@ -59,12 +69,24 @@ typedef struct
enum
{
SIGNAL_ACCELERATOR_ACTIVATED,
+ SIGNAL_MODIFIERS_ACCELERATOR_ACTIVATED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
+enum
+{
+ PROP_0,
+
+ PROP_ISO_NEXT_GROUP,
+
+ LAST_PROP
+};
+
+static GParamSpec *properties[LAST_PROP] = { NULL };
+
G_DEFINE_TYPE (GfKeybindings, gf_keybindings, G_TYPE_OBJECT)
static gboolean
@@ -141,6 +163,9 @@ change_keygrab (GfKeybindings *keybindings,
keycode = keybinding->keycode;
mask = keybinding->mask;
+ if (keycode == 0)
+ return;
+
while (ignored_mask <= keybindings->ignored_mask)
{
if (ignored_mask & ~(keybindings->ignored_mask))
@@ -224,12 +249,183 @@ get_keybinding (GfKeybindings *keybindings,
return keybinding;
}
+static gint
+get_keycodes_for_keysym (GfKeybindings *keybindings,
+ gint keysym,
+ gint **keycodes)
+{
+ GArray *retval;
+ gint min_keycodes;
+ gint max_keycodes;
+ gint keysyms_per_keycode;
+ KeySym *keymap;
+ gint n_keycodes;
+ gint keycode;
+
+ retval = g_array_new (FALSE, FALSE, sizeof (gint));
+
+ XDisplayKeycodes (keybindings->xdisplay, &min_keycodes, &max_keycodes);
+
+ keymap = XGetKeyboardMapping (keybindings->xdisplay, min_keycodes,
+ max_keycodes - min_keycodes + 1,
+ &keysyms_per_keycode);
+
+ keycode = min_keycodes;
+ while (keycode <= max_keycodes)
+ {
+ const KeySym *syms;
+ gint i;
+
+ syms = keymap + (keycode - min_keycodes) * keysyms_per_keycode;
+ i = 0;
+
+ while (i < keysyms_per_keycode)
+ {
+ if (syms[i] == (uint) keysym)
+ g_array_append_val (retval, keycode);
+
+ ++i;
+ }
+
+ ++keycode;
+ }
+
+ XFree (keymap);
+
+ n_keycodes = retval->len;
+ *keycodes = (gint *) g_array_free (retval, n_keycodes == 0 ? TRUE : FALSE);
+
+ return n_keycodes;
+}
+
+static void
+reload_iso_next_group_keybindings (GfKeybindings *keybindings)
+{
+ gint *keycodes;
+ gint n_keycodes;
+ gint i;
+
+ g_hash_table_remove_all (keybindings->iso_next_group_keybindings);
+
+ if (keybindings->iso_next_group_option == NULL)
+ return;
+
+ n_keycodes = get_keycodes_for_keysym (keybindings, XK_ISO_Next_Group,
+ &keycodes);
+
+ if (g_strcmp0 (keybindings->iso_next_group_option, "toggle") == 0 ||
+ g_strcmp0 (keybindings->iso_next_group_option, "lalt_toggle") == 0 ||
+ g_strcmp0 (keybindings->iso_next_group_option, "lwin_toggle") == 0 ||
+ g_strcmp0 (keybindings->iso_next_group_option, "rwin_toggle") == 0 ||
+ g_strcmp0 (keybindings->iso_next_group_option, "lshift_toggle") == 0 ||
+ g_strcmp0 (keybindings->iso_next_group_option, "rshift_toggle") == 0 ||
+ g_strcmp0 (keybindings->iso_next_group_option, "lctrl_toggle") == 0 ||
+ g_strcmp0 (keybindings->iso_next_group_option, "rctrl_toggle") == 0 ||
+ g_strcmp0 (keybindings->iso_next_group_option, "sclk_toggle") == 0 ||
+ g_strcmp0 (keybindings->iso_next_group_option, "menu_toggle") == 0 ||
+ g_strcmp0 (keybindings->iso_next_group_option, "caps_toggle") == 0)
+ {
+ for (i = 0; i < n_keycodes; i++)
+ {
+ Keybinding *keybinding;
+
+ keybinding = keybinding_new (NULL, 0, 0, keycodes[i], 0, 0);
+ g_hash_table_insert (keybindings->iso_next_group_keybindings,
+ GINT_TO_POINTER (i), keybinding);
+ }
+ }
+ else if (g_strcmp0 (keybindings->iso_next_group_option, "shift_caps_toggle") == 0 ||
+ g_strcmp0 (keybindings->iso_next_group_option, "shifts_toggle") == 0)
+ {
+ for (i = 0; i < n_keycodes; i++)
+ {
+ Keybinding *keybinding;
+
+ keybinding = keybinding_new (NULL, 0, 0, keycodes[i], ShiftMask, 0);
+ g_hash_table_insert (keybindings->iso_next_group_keybindings,
+ GINT_TO_POINTER (i), keybinding);
+ }
+ }
+ else if (g_strcmp0 (keybindings->iso_next_group_option, "alt_caps_toggle") == 0 ||
+ g_strcmp0 (keybindings->iso_next_group_option, "alt_space_toggle") == 0)
+ {
+ for (i = 0; i < n_keycodes; i++)
+ {
+ Keybinding *keybinding;
+
+ keybinding = keybinding_new (NULL, 0, 0, keycodes[i], Mod1Mask, 0);
+ g_hash_table_insert (keybindings->iso_next_group_keybindings,
+ GINT_TO_POINTER (i), keybinding);
+ }
+ }
+ else if (g_strcmp0 (keybindings->iso_next_group_option, "ctrl_shift_toggle") == 0 ||
+ g_strcmp0 (keybindings->iso_next_group_option, "lctrl_lshift_toggle") == 0 ||
+ g_strcmp0 (keybindings->iso_next_group_option, "rctrl_rshift_toggle") == 0)
+ {
+ for (i = 0; i < n_keycodes; i++)
+ {
+ Keybinding *keybinding;
+
+ keybinding = keybinding_new (NULL, 0, 0, keycodes[i], ShiftMask, 0);
+ g_hash_table_insert (keybindings->iso_next_group_keybindings,
+ GINT_TO_POINTER (i), keybinding);
+
+ keybinding = keybinding_new (NULL, 0, 0, keycodes[i], ControlMask, 0);
+ g_hash_table_insert (keybindings->iso_next_group_keybindings,
+ GINT_TO_POINTER (i + n_keycodes), keybinding);
+ }
+ }
+ else if (g_strcmp0 (keybindings->iso_next_group_option, "ctrl_alt_toggle") == 0)
+ {
+ for (i = 0; i < n_keycodes; i++)
+ {
+ Keybinding *keybinding;
+
+ keybinding = keybinding_new (NULL, 0, 0, keycodes[i], Mod1Mask, 0);
+ g_hash_table_insert (keybindings->iso_next_group_keybindings,
+ GINT_TO_POINTER (i), keybinding);
+
+ keybinding = keybinding_new (NULL, 0, 0, keycodes[i], ControlMask, 0);
+ g_hash_table_insert (keybindings->iso_next_group_keybindings,
+ GINT_TO_POINTER (i + n_keycodes), keybinding);
+ }
+ }
+ else if (g_strcmp0 (keybindings->iso_next_group_option, "alt_shift_toggle") == 0 ||
+ g_strcmp0 (keybindings->iso_next_group_option, "lalt_lshift_toggle") == 0)
+ {
+ for (i = 0; i < n_keycodes; i++)
+ {
+ Keybinding *keybinding;
+
+ keybinding = keybinding_new (NULL, 0, 0, keycodes[i], Mod1Mask, 0);
+ g_hash_table_insert (keybindings->iso_next_group_keybindings,
+ GINT_TO_POINTER (i), keybinding);
+
+ keybinding = keybinding_new (NULL, 0, 0, keycodes[i], ShiftMask, 0);
+ g_hash_table_insert (keybindings->iso_next_group_keybindings,
+ GINT_TO_POINTER (i + n_keycodes), keybinding);
+ }
+ }
+
+ g_free (keycodes);
+}
+
static void
ungrab_keybindings (GfKeybindings *keybindings)
{
GHashTableIter iter;
gpointer value;
+ g_hash_table_iter_init (&iter, keybindings->iso_next_group_keybindings);
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ {
+ Keybinding *keybinding;
+
+ keybinding = (Keybinding *) value;
+
+ change_keygrab (keybindings, FALSE, keybinding);
+ }
+
g_hash_table_iter_init (&iter, keybindings->keybindings);
while (g_hash_table_iter_next (&iter, NULL, &value))
{
@@ -278,6 +474,8 @@ reload_keybindings (GfKeybindings *keybindings)
GHashTableIter iter;
gpointer value;
+ reload_iso_next_group_keybindings (keybindings);
+
g_hash_table_iter_init (&iter, keybindings->keybindings);
while (g_hash_table_iter_next (&iter, NULL, &value))
{
@@ -319,6 +517,16 @@ regrab_keybindings (GfKeybindings *keybindings)
GHashTableIter iter;
gpointer value;
+ g_hash_table_iter_init (&iter, keybindings->iso_next_group_keybindings);
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ {
+ Keybinding *keybinding;
+
+ keybinding = (Keybinding *) value;
+
+ change_keygrab (keybindings, TRUE, keybinding);
+ }
+
g_hash_table_iter_init (&iter, keybindings->keybindings);
while (g_hash_table_iter_next (&iter, NULL, &value))
{
@@ -330,6 +538,90 @@ regrab_keybindings (GfKeybindings *keybindings)
}
}
+static gboolean
+process_iso_next_group (GfKeybindings *keybindings,
+ XEvent *event)
+{
+ gboolean processed;
+ guint state;
+ GList *values;
+ GList *l;
+
+ if (event->type == KeyRelease)
+ return FALSE;
+
+ processed = FALSE;
+ state = event->xkey.state & 0xff & ~(keybindings->ignored_mask);
+ values = g_hash_table_get_values (keybindings->iso_next_group_keybindings);
+
+ for (l = values; l; l = l->next)
+ {
+ Keybinding *keybinding;
+
+ keybinding = (Keybinding *) l->data;
+
+ if (keybinding->keycode == event->xkey.keycode &&
+ keybinding->mask == state)
+ {
+ gboolean freeze;
+
+ g_signal_emit (keybindings,
+ signals[SIGNAL_MODIFIERS_ACCELERATOR_ACTIVATED],
+ 0, &freeze);
+
+ if (!freeze)
+ XUngrabKeyboard (keybindings->xdisplay, event->xkey.time);
+
+ processed = TRUE;
+ break;
+ }
+ }
+
+ g_list_free (values);
+
+ return processed;
+}
+
+static gboolean
+process_event (GfKeybindings *keybindings,
+ XEvent *event)
+{
+ gboolean processed;
+ guint state;
+ GList *values;
+ GList *l;
+
+ if (event->type == KeyRelease)
+ return FALSE;
+
+ processed = FALSE;
+ state = event->xkey.state & 0xff & ~(keybindings->ignored_mask);
+ values = g_hash_table_get_values (keybindings->keybindings);
+
+ for (l = values; l; l = l->next)
+ {
+ Keybinding *keybinding;
+
+ keybinding = (Keybinding *) l->data;
+
+ if (keybinding->keycode == event->xkey.keycode &&
+ keybinding->mask == state)
+ {
+ XUngrabKeyboard (keybindings->xdisplay, event->xkey.time);
+ g_signal_emit (keybindings, signals[SIGNAL_ACCELERATOR_ACTIVATED],
+ 0, keybinding->action);
+
+
+ processed = TRUE;
+ break;
+ }
+ }
+
+ g_list_free (values);
+
+ return processed;
+}
+
static GdkFilterReturn
filter_func (GdkXEvent *xevent,
GdkEvent *event,
@@ -337,8 +629,6 @@ filter_func (GdkXEvent *xevent,
{
GfKeybindings *keybindings;
XEvent *ev;
- GList *values;
- GList *l;
keybindings = GF_KEYBINDINGS (user_data);
ev = (XEvent *) xevent;
@@ -363,43 +653,81 @@ filter_func (GdkXEvent *xevent,
}
}
+ if (ev->type != KeyPress && ev->type != KeyRelease)
+ return GDK_FILTER_CONTINUE;
+
+ if (process_iso_next_group (keybindings, ev))
+ return GDK_FILTER_REMOVE;
+
XAllowEvents (keybindings->xdisplay, AsyncKeyboard, ev->xkey.time);
- if (ev->type != KeyPress)
- return GDK_FILTER_CONTINUE;
+ if (process_event (keybindings, ev))
+ return GDK_FILTER_REMOVE;
- values = g_hash_table_get_values (keybindings->keybindings);
+ return GDK_FILTER_CONTINUE;
+}
- for (l = values; l; l = l->next)
- {
- Keybinding *keybinding;
- guint state;
+static guint
+get_next_action (void)
+{
+ static guint action;
- keybinding = (Keybinding *) l->data;
- state = ev->xkey.state & 0xff & ~(keybindings->ignored_mask);
+ return ++action;
+}
- if (keybinding->keycode == ev->xkey.keycode &&
- keybinding->mask == state)
- {
- XUngrabKeyboard (keybindings->xdisplay, ev->xkey.time);
- g_signal_emit (keybindings, signals[SIGNAL_ACCELERATOR_ACTIVATED],
- 0, keybinding->action);
+static void
+xkb_options_changed_cb (GSettings *settings,
+ gchar *key,
+ gpointer user_data)
+{
+ GfKeybindings *keybindings;
+ gchar **xkb_options;
+ gchar **p;
+ gchar *option;
+
+ keybindings = GF_KEYBINDINGS (user_data);
+ xkb_options = g_settings_get_strv (settings, KEY_XKB_OPTIONS);
+ option = NULL;
+ for (p = xkb_options; p && *p; ++p)
+ {
+ if (g_str_has_prefix (*p, "grp:"))
+ {
+ option = (*p + 4);
break;
}
}
- g_list_free (values);
+ if (g_strcmp0 (option, keybindings->iso_next_group_option) == 0)
+ {
+ g_strfreev (xkb_options);
+ return;
+ }
- return GDK_FILTER_CONTINUE;
+ g_free (keybindings->iso_next_group_option);
+ keybindings->iso_next_group_option = g_strdup (option);
+ g_strfreev (xkb_options);
+
+ reload_iso_next_group_keybindings (keybindings);
}
-static guint
-get_next_action (void)
+static void
+gf_keybindings_constructed (GObject *object)
{
- static guint action;
+ GfKeybindings *keybindings;
- return ++action;
+ keybindings = GF_KEYBINDINGS (object);
+
+ G_OBJECT_CLASS (gf_keybindings_parent_class)->constructed (object);
+
+ if (keybindings->iso_next_group)
+ {
+ keybindings->settings = g_settings_new (DESKTOP_INPUT_SOURCES_SCHEMA);
+
+ g_signal_connect (keybindings->settings, "changed::" KEY_XKB_OPTIONS,
+ G_CALLBACK (xkb_options_changed_cb), keybindings);
+ xkb_options_changed_cb (keybindings->settings, NULL, keybindings);
+ }
}
static void
@@ -415,6 +743,14 @@ gf_keybindings_dispose (GObject *object)
keybindings->keybindings = NULL;
}
+ if (keybindings->iso_next_group_keybindings != NULL)
+ {
+ g_hash_table_destroy (keybindings->iso_next_group_keybindings);
+ keybindings->iso_next_group_keybindings = NULL;
+ }
+
+ g_clear_object (&keybindings->settings);
+
G_OBJECT_CLASS (gf_keybindings_parent_class)->dispose (object);
}
@@ -427,31 +763,77 @@ gf_keybindings_finalize (GObject *object)
gdk_window_remove_filter (NULL, filter_func, keybindings);
+ g_free (keybindings->iso_next_group_option);
+
G_OBJECT_CLASS (gf_keybindings_parent_class)->finalize (object);
}
static void
+gf_keybindings_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GfKeybindings *keybindings;
+
+ keybindings = GF_KEYBINDINGS (object);
+
+ switch (prop_id)
+ {
+ case PROP_ISO_NEXT_GROUP:
+ keybindings->iso_next_group = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
gf_keybindings_class_init (GfKeybindingsClass *keybindings_class)
{
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (keybindings_class);
+ object_class->constructed = gf_keybindings_constructed;
object_class->dispose = gf_keybindings_dispose;
object_class->finalize = gf_keybindings_finalize;
+ object_class->set_property = gf_keybindings_set_property;
/**
* GfKeybindings::accelerator-activated:
* @keybindings: the object on which the signal is emitted
* @action: keybinding action from gf_keybindings_grab
*
- * The ::accelerator-activated signal is emitted each time when keybinding
- * is activated by user.
+ * The ::accelerator-activated signal will be emitted when a keybinding is
+ * activated.
*/
signals[SIGNAL_ACCELERATOR_ACTIVATED] =
g_signal_new ("accelerator-activated",
G_TYPE_FROM_CLASS (keybindings_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_UINT);
+
+ /**
+ * GfKeybindings::modifiers-accelerator-activated:
+ * @keybindings: the object on which the signal is emitted
+ *
+ * The ::modifiers-accelerator-activated signal will be emitted when a
+ * special modifiers-only keybinding is activated.
+ */
+ signals[SIGNAL_MODIFIERS_ACCELERATOR_ACTIVATED] =
+ g_signal_new ("modifiers-accelerator-activated",
+ G_TYPE_FROM_CLASS (keybindings_class), G_SIGNAL_RUN_LAST,
+ 0, g_signal_accumulator_first_wins, NULL, NULL,
+ G_TYPE_BOOLEAN, 0);
+
+ properties[PROP_ISO_NEXT_GROUP] =
+ g_param_spec_boolean ("iso-next-group", "iso-next-group", "iso-next-group",
+ FALSE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
}
static void
@@ -464,6 +846,8 @@ gf_keybindings_init (GfKeybindings *keybindings)
keybindings->keybindings = g_hash_table_new_full (NULL, NULL, NULL,
keybinding_free);
+ keybindings->iso_next_group_keybindings = g_hash_table_new_full (NULL, NULL, NULL,
+ keybinding_free);
display = gdk_display_get_default ();
xkb_major = XkbMajorVersion;
@@ -502,9 +886,11 @@ gf_keybindings_init (GfKeybindings *keybindings)
* Returns: (transfer full): a newly created #GfKeybindings.
*/
GfKeybindings *
-gf_keybindings_new (void)
+gf_keybindings_new (gboolean iso_next_group)
{
- return g_object_new (GF_TYPE_KEYBINDINGS, NULL);
+ return g_object_new (GF_TYPE_KEYBINDINGS,
+ "iso-next-group", iso_next_group,
+ NULL);
}
/**
diff --git a/gnome-flashback/libcommon/gf-keybindings.h b/gnome-flashback/libcommon/gf-keybindings.h
index 571b89e..d8bb035 100644
--- a/gnome-flashback/libcommon/gf-keybindings.h
+++ b/gnome-flashback/libcommon/gf-keybindings.h
@@ -25,7 +25,7 @@ G_BEGIN_DECLS
#define GF_TYPE_KEYBINDINGS gf_keybindings_get_type ()
G_DECLARE_FINAL_TYPE (GfKeybindings, gf_keybindings, GF, KEYBINDINGS, GObject)
-GfKeybindings *gf_keybindings_new (void);
+GfKeybindings *gf_keybindings_new (gboolean iso_next_group);
guint gf_keybindings_grab (GfKeybindings *keybindings,
const gchar *accelerator);
diff --git a/gnome-flashback/libinput-sources/gf-input-source-manager.c
b/gnome-flashback/libinput-sources/gf-input-source-manager.c
index 0832601..b9fcd7c 100644
--- a/gnome-flashback/libinput-sources/gf-input-source-manager.c
+++ b/gnome-flashback/libinput-sources/gf-input-source-manager.c
@@ -265,7 +265,7 @@ static void
keybindings_init (GfInputSourceManager *manager)
{
manager->wm_keybindings = g_settings_new (DESKTOP_WM_KEYBINDINGS_SCHEMA);
- manager->keybindings = gf_keybindings_new ();
+ manager->keybindings = gf_keybindings_new (TRUE);
g_signal_connect (manager->wm_keybindings,
"changed::" KEY_SWITCH_INPUT_SOURCE,
diff --git a/gnome-flashback/libshell/flashback-shell.c b/gnome-flashback/libshell/flashback-shell.c
index 3d978c4..7e51f52 100644
--- a/gnome-flashback/libshell/flashback-shell.c
+++ b/gnome-flashback/libshell/flashback-shell.c
@@ -465,7 +465,7 @@ flashback_shell_init (FlashbackShell *shell)
shell->grabbed_accelerators = g_hash_table_new_full (NULL, NULL, NULL, g_free);
shell->grabbers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
- shell->keybindings = gf_keybindings_new ();
+ shell->keybindings = gf_keybindings_new (FALSE);
g_signal_connect (shell->keybindings, "accelerator-activated",
G_CALLBACK (accelerator_activated), shell);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]