2005-05-22 Sebastien Bacher <seb128@debian.org> * ChangeLog capplets/about-me/e-image-chooser.c capplets/accessibility/at-properties/at-startup-session.h capplets/accessibility/keyboard/accessibility-keyboard.c capplets/background/gnome-wp-info.c capplets/common/gconf-property-editor.c capplets/common/gnome-theme-apply.c capplets/default-applications/gnome-default-applications-properties.c capplets/keybindings/gnome-keybinding-properties.c capplets/keyboard/gnome-keyboard-properties.c capplets/mouse/gnome-mouse-properties.c capplets/network/gnome-network-preferences.c capplets/sound/sound-properties-capplet.c capplets/theme-switcher/gnome-theme-details.c capplets/theme-switcher/gnome-theme-manager.c capplets/ui-properties/gnome-ui-properties.c capplets/windows/gnome-window-properties.c gnome-settings-daemon/factory.c gnome-settings-daemon/gnome-settings-accessibility-keyboard.c gnome-settings-daemon/gnome-settings-background.c gnome-settings-daemon/gnome-settings-daemon.c gnome-settings-daemon/gnome-settings-daemon.h gnome-settings-daemon/gnome-settings-font.c gnome-settings-daemon/gnome-settings-keybindings.c gnome-settings-daemon/gnome-settings-keybindings.h gnome-settings-daemon/gnome-settings-keyboard-xkb.c gnome-settings-daemon/gnome-settings-keyboard.c gnome-settings-daemon/gnome-settings-locate-pointer.h gnome-settings-daemon/gnome-settings-mouse.c gnome-settings-daemon/gnome-settings-multimedia-keys.c gnome-settings-daemon/gnome-settings-screensaver.c gnome-settings-daemon/gnome-settings-sound.c gnome-settings-daemon/gnome-settings-xmodmap.c gnome-settings-daemon/gnome-settings-xrdb.c gnome-settings-daemon/gnome-settings-xsettings.c libbackground/applier.c libbackground/applier.h libbackground/preferences.c libsounds/sound-properties.c libsounds/sound-view.h libwindow-settings/gnome-wm-manager.c libwindow-settings/metacity-window-manager.c typing-break/drw-break-window.c typing-break/drw-utils.h typing-break/drwright.c vfs-methods/fontilus/font-view.c vfs-methods/themus/themus-theme-applier.c: Cleanup of gconf and a few other things, patch from Kjartan Maraas <kmaraas@gnome.org> (Closes: #301945).
488 lines
12 KiB
C
488 lines
12 KiB
C
#include <config.h>
|
|
|
|
#include <string.h>
|
|
#include <X11/keysym.h>
|
|
#include <glib/gi18n.h>
|
|
#include <gdk/gdk.h>
|
|
#include <gtk/gtk.h>
|
|
#include <gdk/gdkx.h>
|
|
#include "gnome-settings-daemon.h"
|
|
#include "gnome-settings-keybindings.h"
|
|
#include "eggaccelerators.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 GSList *screens = NULL;
|
|
|
|
static GSList *
|
|
get_screens_list (void)
|
|
{
|
|
GdkDisplay *display = gdk_display_get_default();
|
|
GSList *list = NULL;
|
|
int i;
|
|
|
|
if (gdk_display_get_n_screens (display) == 1) {
|
|
list = g_slist_append (list, gdk_screen_get_default ());
|
|
} else {
|
|
for (i = 0; i < gdk_display_get_n_screens (display); i++) {
|
|
GdkScreen *screen;
|
|
|
|
screen = gdk_display_get_screen (display, i);
|
|
if (screen != NULL) {
|
|
list = g_slist_append (list, screen);
|
|
}
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
extern char **environ;
|
|
|
|
static char *
|
|
screen_exec_display_string (GdkScreen *screen)
|
|
{
|
|
GString *str;
|
|
const char *old_display;
|
|
char *retval;
|
|
char *p;
|
|
|
|
g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
|
|
|
|
old_display = gdk_display_get_name (gdk_screen_get_display (screen));
|
|
|
|
str = g_string_new ("DISPLAY=");
|
|
g_string_append (str, old_display);
|
|
|
|
p = strrchr (str->str, '.');
|
|
if (p && p > strchr (str->str, ':'))
|
|
g_string_truncate (str, p - str->str);
|
|
|
|
g_string_append_printf (str, ".%d", gdk_screen_get_number (screen));
|
|
|
|
retval = str->str;
|
|
|
|
g_string_free (str, FALSE);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* get_exec_environment:
|
|
*
|
|
* Description: Modifies the current program environment to
|
|
* ensure that $DISPLAY is set such that a launched application
|
|
* inheriting this environment would appear on screen.
|
|
*
|
|
* Returns: a newly-allocated %NULL-terminated array of strings or
|
|
* %NULL on error. Use g_strfreev() to free it.
|
|
*
|
|
* mainly ripped from egg_screen_exec_display_string in
|
|
* gnome-panel/egg-screen-exec.c
|
|
**/
|
|
char **
|
|
get_exec_environment (XEvent *xevent)
|
|
{
|
|
char **retval = NULL;
|
|
int i;
|
|
int display_index = -1;
|
|
|
|
GdkScreen *screen = NULL;
|
|
|
|
GdkWindow *window = gdk_xid_table_lookup (xevent->xkey.root);
|
|
|
|
if (window)
|
|
screen = gdk_drawable_get_screen (GDK_DRAWABLE (window));
|
|
|
|
g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
|
|
|
|
for (i = 0; environ [i]; i++)
|
|
if (!strncmp (environ [i], "DISPLAY", 7))
|
|
display_index = i;
|
|
|
|
if (display_index == -1)
|
|
display_index = i++;
|
|
|
|
retval = g_new (char *, i + 1);
|
|
|
|
for (i = 0; environ [i]; i++)
|
|
if (i == display_index)
|
|
retval [i] = screen_exec_display_string (screen);
|
|
else
|
|
retval [i] = g_strdup (environ [i]);
|
|
|
|
retval [i] = NULL;
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
if (egg_accelerator_parse_virtual (binding->binding_str, &binding->key.keysym, &binding->key.keycode, &binding->key.state) == FALSE)
|
|
return FALSE;
|
|
|
|
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;
|
|
GConfClient *client = gconf_client_get_default();
|
|
|
|
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 (client, subdir, NULL);
|
|
g_object_unref (client);
|
|
|
|
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 (_("Key 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;
|
|
}
|
|
|
|
static void
|
|
grab_key (GdkWindow *root, Key *key, int result, gboolean grab)
|
|
{
|
|
gdk_error_trap_push ();
|
|
if (grab)
|
|
XGrabKey (GDK_DISPLAY(), key->keycode, (result | key->state),
|
|
GDK_WINDOW_XID (root), True, GrabModeAsync, GrabModeAsync);
|
|
else
|
|
XUngrabKey(GDK_DISPLAY(), key->keycode, (result | key->state),
|
|
GDK_WINDOW_XID (root));
|
|
gdk_flush ();
|
|
if (gdk_error_trap_pop ()) {
|
|
g_warning (_("It seems that another application already has"
|
|
" access to key '%d'."), key->keycode);
|
|
}
|
|
}
|
|
|
|
/* 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++) {
|
|
GSList *l;
|
|
int j, result = 0;
|
|
|
|
for (j = 0; j < bits_set_cnt; j++) {
|
|
if (i & (1<<j))
|
|
result |= (1<<indexes[j]);
|
|
}
|
|
|
|
for (l = screens; l ; l = l->next) {
|
|
GdkScreen *screen = l->data;
|
|
grab_key (gdk_screen_get_root_window (screen), key, result,
|
|
grab);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
binding_register_keys (void)
|
|
{
|
|
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;
|
|
gboolean retval;
|
|
gchar **argv = NULL;
|
|
gchar **envp = NULL;
|
|
|
|
g_return_val_if_fail (binding->action != NULL, GDK_FILTER_CONTINUE);
|
|
|
|
if (!g_shell_parse_argv (binding->action,
|
|
NULL, &argv,
|
|
&error))
|
|
return GDK_FILTER_CONTINUE;
|
|
|
|
envp = get_exec_environment (xevent);
|
|
|
|
|
|
retval = g_spawn_async (NULL,
|
|
argv,
|
|
envp,
|
|
G_SPAWN_SEARCH_PATH,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&error);
|
|
g_strfreev (argv);
|
|
g_strfreev (envp);
|
|
|
|
if (!retval)
|
|
{
|
|
GtkWidget *dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_WARNING,
|
|
GTK_BUTTONS_CLOSE,
|
|
_("Error while trying to run (%s)\n"\
|
|
"which is linked to the key (%s)"),
|
|
binding->action,
|
|
binding->binding_str);
|
|
g_signal_connect (dialog, "response",
|
|
G_CALLBACK (gtk_widget_destroy),
|
|
NULL);
|
|
gtk_widget_show (dialog);
|
|
}
|
|
return GDK_FILTER_REMOVE;
|
|
}
|
|
}
|
|
return GDK_FILTER_CONTINUE;
|
|
}
|
|
|
|
void
|
|
gnome_settings_keybindings_init (GConfClient *client)
|
|
{
|
|
GdkDisplay *dpy = gdk_display_get_default ();
|
|
GdkScreen *screen;
|
|
int screen_num = gdk_display_get_n_screens (dpy);
|
|
int i;
|
|
|
|
gnome_settings_daemon_register_callback (GCONF_BINDING_DIR, bindings_callback);
|
|
|
|
gdk_window_add_filter (gdk_get_default_root_window (),
|
|
keybindings_filter,
|
|
NULL);
|
|
for (i = 0; i < screen_num; i++)
|
|
{
|
|
screen = gdk_display_get_screen (dpy, i);
|
|
gdk_window_add_filter (gdk_screen_get_root_window (screen),
|
|
keybindings_filter, NULL);
|
|
}
|
|
}
|
|
|
|
void
|
|
gnome_settings_keybindings_load (GConfClient *client)
|
|
{
|
|
GSList *list, *li;
|
|
|
|
list = gconf_client_all_dirs (client, GCONF_BINDING_DIR, NULL);
|
|
screens = get_screens_list ();
|
|
|
|
for (li = list; li != NULL; li = li->next)
|
|
{
|
|
char *subdir = li->data;
|
|
li->data = NULL;
|
|
bindings_get_entry (subdir);
|
|
}
|
|
binding_register_keys ();
|
|
}
|
|
|