diff options
author | Jonathan Blandford <jrb@redhat.com> | 2002-05-30 00:25:29 +0000 |
---|---|---|
committer | Jonathan Blandford <jrb@src.gnome.org> | 2002-05-30 00:25:29 +0000 |
commit | 3b070f76d1d145e5bfaa16418c83924c4f0a9d89 (patch) | |
tree | 73127a814aa78cac09d729425eee67f71fbc0838 | |
parent | 6f7158ee6188f237850362f2612a9446d9391999 (diff) | |
download | gnome-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/ChangeLog | 9 | ||||
-rw-r--r-- | gnome-settings-daemon/Makefile.am | 2 | ||||
-rw-r--r-- | gnome-settings-daemon/gnome-settings-daemon.c | 3 | ||||
-rw-r--r-- | gnome-settings-daemon/gnome-settings-keybindings.c | 335 | ||||
-rw-r--r-- | gnome-settings-daemon/gnome-settings-keybindings.h | 31 |
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__*/ |