summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Blandford <jrb@redhat.com>2002-05-30 00:25:29 +0000
committerJonathan Blandford <jrb@src.gnome.org>2002-05-30 00:25:29 +0000
commit3b070f76d1d145e5bfaa16418c83924c4f0a9d89 (patch)
tree73127a814aa78cac09d729425eee67f71fbc0838
parent6f7158ee6188f237850362f2612a9446d9391999 (diff)
downloadgnome-control-center-3b070f76d1d145e5bfaa16418c83924c4f0a9d89.tar.gz
Add gnome-settings-keybinding initialize keybindings support Patch to add
Wed May 29 20:23:13 2002 Jonathan Blandford <jrb@redhat.com> * Makefile.am: Add gnome-settings-keybinding * gnome-settings-daemon.c: (gnome_settings_daemon_new): initialize keybindings support * gnome-settings-keybindings.[ch]: Patch to add support for custom keybindings. Thanks to Erwann Chenede <Erwann.Chenede@sun.com> for the patch.
-rw-r--r--gnome-settings-daemon/ChangeLog9
-rw-r--r--gnome-settings-daemon/Makefile.am2
-rw-r--r--gnome-settings-daemon/gnome-settings-daemon.c3
-rw-r--r--gnome-settings-daemon/gnome-settings-keybindings.c335
-rw-r--r--gnome-settings-daemon/gnome-settings-keybindings.h31
5 files changed, 380 insertions, 0 deletions
diff --git a/gnome-settings-daemon/ChangeLog b/gnome-settings-daemon/ChangeLog
index cf3685509..73925559b 100644
--- a/gnome-settings-daemon/ChangeLog
+++ b/gnome-settings-daemon/ChangeLog
@@ -1,3 +1,12 @@
+Wed May 29 20:23:13 2002 Jonathan Blandford <jrb@redhat.com>
+
+ * Makefile.am: Add gnome-settings-keybinding
+ * gnome-settings-daemon.c: (gnome_settings_daemon_new): initialize
+ keybindings support
+ * gnome-settings-keybindings.[ch]: Patch to add support for custom
+ keybindings. Thanks to Erwann Chenede <Erwann.Chenede@sun.com>
+ for the patch.
+
2002-05-22 Jody Goldberg <jody@gnome.org>
* gnome-settings-accessibility-keyboard.c (set_server_from_gconf) :
diff --git a/gnome-settings-daemon/Makefile.am b/gnome-settings-daemon/Makefile.am
index 670911eeb..6436393aa 100644
--- a/gnome-settings-daemon/Makefile.am
+++ b/gnome-settings-daemon/Makefile.am
@@ -32,6 +32,8 @@ gnome_settings_daemon_SOURCES = \
xsettings-manager.c \
xsettings-common.h \
xsettings-manager.h \
+ gnome-settings-keybindings.c \
+ gnome-settings-keybindings.h \
$(CORBA_GENERATED)
gnome_settings_daemon_LDADD = \
diff --git a/gnome-settings-daemon/gnome-settings-daemon.c b/gnome-settings-daemon/gnome-settings-daemon.c
index f6928fc87..947f74333 100644
--- a/gnome-settings-daemon/gnome-settings-daemon.c
+++ b/gnome-settings-daemon/gnome-settings-daemon.c
@@ -45,6 +45,7 @@
#include "gnome-settings-accessibility-keyboard.h"
#include "gnome-settings-screensaver.h"
#include "gnome-settings-default-editor.h"
+#include "gnome-settings-keybindings.h"
#include "GNOME_SettingsDaemon.h"
@@ -239,6 +240,7 @@ gnome_settings_daemon_new (void)
gnome_settings_screensaver_init (client);
gnome_settings_default_editor_init (client);
gnome_settings_background_init (client);
+ gnome_settings_keybindings_init (client);
for (list = directories; list; list = list->next)
{
@@ -278,6 +280,7 @@ gnome_settings_daemon_new (void)
gnome_settings_screensaver_load (client);
gnome_settings_default_editor_load (client);
gnome_settings_background_load (client);
+ gnome_settings_keybindings_load (client);
return G_OBJECT (daemon);
}
diff --git a/gnome-settings-daemon/gnome-settings-keybindings.c b/gnome-settings-daemon/gnome-settings-keybindings.c
new file mode 100644
index 000000000..1b459a8cf
--- /dev/null
+++ b/gnome-settings-daemon/gnome-settings-keybindings.c
@@ -0,0 +1,335 @@
+#include <config.h>
+
+#include <string.h>
+#include <X11/keysym.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include "gnome-settings-daemon.h"
+#include "gnome-settings-keybindings.h"
+
+/* we exclude shift, GDK_CONTROL_MASK and GDK_MOD1_MASK since we know what
+ these modifiers mean
+ these are the mods whose combinations are bound by the keygrabbing code */
+#define IGNORED_MODS (0x2000 /*Xkb modifier*/ | GDK_LOCK_MASK | \
+ GDK_MOD2_MASK | GDK_MOD3_MASK | GDK_MOD4_MASK | GDK_MOD5_MASK)
+/* these are the ones we actually use for global keys, we always only check
+ * for these set */
+#define USED_MODS (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK)
+
+#define GCONF_BINDING_DIR "/desktop/gnome/keybindings"
+
+typedef struct {
+ guint keysym;
+ guint state;
+ guint keycode;
+} Key;
+
+typedef struct {
+ char *binding_str;
+ char *action;
+ char *gconf_key;
+ Key key;
+ Key previous_key;
+} Binding;
+
+static GSList *binding_list = NULL;
+
+static gint
+compare_bindings (gconstpointer a, gconstpointer b)
+{
+ Binding *key_a = (Binding*) a;
+ char *key_b = (char*) b;
+
+ return strcmp (key_b, key_a->gconf_key);
+}
+
+static gboolean
+parse_binding (Binding *binding)
+{
+ g_return_val_if_fail (binding != NULL, FALSE);
+
+ binding->key.keysym = 0;
+ binding->key.state = 0;
+
+ if (binding->binding_str == NULL ||
+ binding->binding_str[0] == '\0' ||
+ strcmp (binding->binding_str, "Disabled") == 0)
+ return FALSE;
+
+ gtk_accelerator_parse (binding->binding_str, &binding->key.keysym, &binding->key.state);
+
+ if (binding->key.keysym == 0)
+ return FALSE;
+
+ binding->key.keycode = XKeysymToKeycode (GDK_DISPLAY (), binding->key.keysym);
+
+ return TRUE;
+}
+
+gboolean
+bindings_get_entry (char *subdir)
+{
+ GConfValue *value;
+ Binding *new_binding;
+ GSList *tmp_elem = NULL, *list = NULL, *li;
+ char *gconf_key;
+ char *action = NULL;
+ char *key = NULL;
+
+ g_return_val_if_fail (subdir != NULL, FALSE);
+
+ /* value = gconf_entry_get_value (entry); */
+ gconf_key = g_path_get_basename (subdir);
+
+ if (!gconf_key)
+ return FALSE;
+
+ /* Get entries for this binding */
+ list = gconf_client_all_entries (gconf_client_get_default (), subdir, NULL);
+
+ for (li = list; li != NULL; li = li->next)
+ {
+ GConfEntry *entry = li->data;
+ char *key_name = g_path_get_basename (gconf_entry_get_key (entry));
+ if (strcmp (key_name, "action") == 0)
+ {
+ if (!action)
+ {
+ value = gconf_entry_get_value (entry);
+ if (value->type != GCONF_VALUE_STRING)
+ return FALSE;
+ action = g_strdup (gconf_value_get_string (value));
+ }
+ else
+ g_warning ("Key Binding (%s) has its action defined multiple times\n",
+ gconf_key);
+ }
+ if (strcmp (key_name, "binding") == 0)
+ {
+ if (!key)
+ {
+ value = gconf_entry_get_value (entry);
+ if (value->type != GCONF_VALUE_STRING)
+ return FALSE;
+ key = g_strdup (gconf_value_get_string (value));
+ }
+ else
+ g_warning ("Key Binding (%s) has its binding defined multiple times\n",
+ gconf_key);
+ }
+ }
+ if (!action || !key)
+ {
+ g_warning ("Key Binding (%s) is incomplete\n", gconf_key);
+ return FALSE;
+ }
+
+ tmp_elem = g_slist_find_custom (binding_list, gconf_key,
+ compare_bindings);
+
+ if (!tmp_elem)
+ new_binding = g_new0 (Binding, 1);
+ else
+ {
+ new_binding = (Binding*) tmp_elem->data;
+ g_free (new_binding->binding_str);
+ g_free (new_binding->action);
+ }
+
+ new_binding->binding_str = key;
+ new_binding->action = action;
+ new_binding->gconf_key = gconf_key;
+
+ new_binding->previous_key.keysym = new_binding->key.keysym;
+ new_binding->previous_key.state = new_binding->key.state;
+ new_binding->previous_key.keycode = new_binding->key.keycode;
+
+ if (parse_binding (new_binding))
+ binding_list = g_slist_append (binding_list, new_binding);
+ else
+ {
+ g_warning ("Binding (%s) is invalid\n", gconf_key);
+ g_free (new_binding->binding_str);
+ g_free (new_binding->action);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+key_already_used (Binding *binding)
+{
+ GSList *li;
+
+ for (li = binding_list; li != NULL; li = li->next)
+ {
+ Binding *tmp_binding = (Binding*) li->data;
+
+ if (tmp_binding != binding && tmp_binding->key.keycode == binding->key.keycode &&
+ tmp_binding->key.state == binding->key.state)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* inspired from all_combinations from gnome-panel/gnome-panel/global-keys.c */
+#define N_BITS 32
+static void
+do_grab (gboolean grab,
+ Key *key)
+{
+ int indexes[N_BITS];/*indexes of bits we need to flip*/
+ int i, bit, bits_set_cnt;
+ int uppervalue;
+ guint mask_to_traverse = IGNORED_MODS & ~ key->state;
+
+ bit = 0;
+ for (i = 0; i < N_BITS; i++) {
+ if (mask_to_traverse & (1<<i))
+ indexes[bit++]=i;
+ }
+
+ bits_set_cnt = bit;
+
+ uppervalue = 1<<bits_set_cnt;
+ for (i = 0; i < uppervalue; i++) {
+ int j, result = 0;
+
+ for (j = 0; j < bits_set_cnt; j++) {
+ if (i & (1<<j))
+ result |= (1<<indexes[j]);
+ }
+ /* FIXME need to grab for all root windows for the display */
+ if (grab)
+ XGrabKey (GDK_DISPLAY(), key->keycode, (result | key->state),
+ GDK_ROOT_WINDOW(), True, GrabModeAsync, GrabModeAsync);
+ else
+ XUngrabKey(GDK_DISPLAY(), key->keycode, (result | key->state),
+ GDK_ROOT_WINDOW());
+ }
+}
+
+void
+binding_register_keys ()
+{
+ GSList *li;
+
+ gdk_error_trap_push();
+
+ /* Now check for changes and grab new key if not already used */
+ for (li = binding_list ; li != NULL; li = li->next)
+ {
+ Binding *binding = (Binding *) li->data;
+
+ if (binding->previous_key.keycode != binding->key.keycode ||
+ binding->previous_key.state != binding->key.state)
+ {
+ /* Ungrab key if it changed and not clashing with previously set binding */
+ if (!key_already_used (binding))
+ {
+ if (binding->previous_key.keycode)
+ do_grab (FALSE, &binding->previous_key);
+ do_grab (TRUE, &binding->key);
+
+ binding->previous_key.keysym = binding->key.keysym;
+ binding->previous_key.state = binding->key.state;
+ binding->previous_key.keycode = binding->key.keycode;
+ }
+ else
+ g_warning ("Key Binding (%s) is already in use\n", binding->binding_str);
+ }
+ }
+ gdk_flush ();
+ gdk_error_trap_pop();
+}
+
+static void
+bindings_callback (GConfEntry *entry)
+{
+ /* ensure we get binding dir not a sub component */
+ gchar** key_elems = g_strsplit (gconf_entry_get_key (entry), "/", 15);
+ gchar* binding_entry = g_strdup_printf ("/%s/%s/%s/%s", key_elems[1],
+ key_elems[2], key_elems[3],
+ key_elems[4]);
+ g_strfreev (key_elems);
+
+ bindings_get_entry (binding_entry);
+ g_free (binding_entry);
+
+ binding_register_keys ();
+}
+
+GdkFilterReturn
+keybindings_filter (GdkXEvent *gdk_xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent *xevent = (XEvent *)gdk_xevent;
+ guint keycode, state;
+ GSList *li;
+
+ if(xevent->type != KeyPress)
+ return GDK_FILTER_CONTINUE;
+
+ keycode = xevent->xkey.keycode;
+ state = xevent->xkey.state;
+
+ for (li = binding_list; li != NULL; li = li->next)
+ {
+ Binding *binding = (Binding*) li->data;
+
+ if (keycode == binding->key.keycode &&
+ (state & USED_MODS) == binding->key.state)
+ {
+ GError* error = NULL;
+
+ if (!g_spawn_command_line_async (binding->action, &error))
+ {
+ GtkWidget *dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_CLOSE,
+ "Error while executing key action action\n"
+ " (%s) (%s)",binding->action,
+ binding->binding_str);
+ gtk_signal_connect_object (GTK_OBJECT (dialog),
+ "response",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ GTK_OBJECT (dialog));
+ gtk_signal_connect (GTK_OBJECT (dialog),
+ "destroy",
+ GTK_SIGNAL_FUNC (gtk_widget_destroyed),
+ dialog);
+ gtk_widget_show (dialog);
+ }
+ return GDK_FILTER_REMOVE;
+ }
+ }
+ return GDK_FILTER_CONTINUE;
+}
+
+void
+gnome_settings_keybindings_init (GConfClient *client)
+{
+ gnome_settings_daemon_register_callback (GCONF_BINDING_DIR, bindings_callback);
+ gdk_window_add_filter (gdk_get_default_root_window (),
+ keybindings_filter,
+ NULL);
+
+}
+
+void
+gnome_settings_keybindings_load (GConfClient *client)
+{
+ GSList *list, *li;
+
+ list = gconf_client_all_dirs (client, GCONF_BINDING_DIR, NULL);
+
+ for (li = list; li != NULL; li = li->next)
+ {
+ char *subdir = li->data;
+ li->data = NULL;
+ bindings_get_entry (subdir);
+ }
+ binding_register_keys ();
+}
+
diff --git a/gnome-settings-daemon/gnome-settings-keybindings.h b/gnome-settings-daemon/gnome-settings-keybindings.h
new file mode 100644
index 000000000..3579cd685
--- /dev/null
+++ b/gnome-settings-daemon/gnome-settings-keybindings.h
@@ -0,0 +1,31 @@
+/* gnome-settings-sound.h
+ *
+ * Copyright (C) 2002 Sun Microsystems Inc.
+ *
+ * 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 2, 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Erwann Chénedé <erwann.chenede@sun.com>
+ */
+
+
+#ifndef __GNOME_SETTINGS_KEYBINDINGS_H__
+#define __GNOME_SETTINGS_KEYBINDINGS_H__
+
+#include <gconf/gconf.h>
+void gnome_settings_keybindings_init (GConfClient *client);
+void gnome_settings_keybindings_load (GConfClient *client);
+
+#endif /*__GNOME_SETTINGS_KEYBINDINGS_H__*/