diff options
author | Perberos <[email protected]> | 2011-12-01 23:53:21 -0300 |
---|---|---|
committer | Perberos <[email protected]> | 2011-12-01 23:53:21 -0300 |
commit | 505cabbd3036081f26586cabc64c26e7769c0ec9 (patch) | |
tree | 09e0498bf572128f5c9ab551531cb28d6d75e992 /plugins/keyboard/gsd-keyboard-manager.c | |
download | mate-settings-daemon-505cabbd3036081f26586cabc64c26e7769c0ec9.tar.bz2 mate-settings-daemon-505cabbd3036081f26586cabc64c26e7769c0ec9.tar.xz |
moving from https://github.com/perberos/mate-desktop-environment
Diffstat (limited to 'plugins/keyboard/gsd-keyboard-manager.c')
-rw-r--r-- | plugins/keyboard/gsd-keyboard-manager.c | 570 |
1 files changed, 570 insertions, 0 deletions
diff --git a/plugins/keyboard/gsd-keyboard-manager.c b/plugins/keyboard/gsd-keyboard-manager.c new file mode 100644 index 0000000..343706d --- /dev/null +++ b/plugins/keyboard/gsd-keyboard-manager.c @@ -0,0 +1,570 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright © 2001 Ximian, Inc. + * Copyright (C) 2007 William Jon McCann <[email protected]> + * + * 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 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include <sys/types.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <locale.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <gdk/gdk.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> + +#ifdef HAVE_X11_EXTENSIONS_XF86MISC_H +# include <X11/extensions/xf86misc.h> +#endif +#ifdef HAVE_X11_EXTENSIONS_XKB_H +#include <X11/XKBlib.h> +#include <X11/keysym.h> +#endif + +#include "mate-settings-profile.h" +#include "gsd-keyboard-manager.h" + +#include "gsd-keyboard-xkb.h" +#include "gsd-xmodmap.h" + +#define GSD_KEYBOARD_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_KEYBOARD_MANAGER, GsdKeyboardManagerPrivate)) + +#ifndef HOST_NAME_MAX +# define HOST_NAME_MAX 255 +#endif + +#define GSD_KEYBOARD_KEY "/desktop/mate/peripherals/keyboard" + +#define KEY_REPEAT GSD_KEYBOARD_KEY "/repeat" +#define KEY_CLICK GSD_KEYBOARD_KEY "/click" +#define KEY_RATE GSD_KEYBOARD_KEY "/rate" +#define KEY_DELAY GSD_KEYBOARD_KEY "/delay" +#define KEY_CLICK_VOLUME GSD_KEYBOARD_KEY "/click_volume" + +#define KEY_BELL_VOLUME GSD_KEYBOARD_KEY "/bell_volume" +#define KEY_BELL_PITCH GSD_KEYBOARD_KEY "/bell_pitch" +#define KEY_BELL_DURATION GSD_KEYBOARD_KEY "/bell_duration" +#define KEY_BELL_MODE GSD_KEYBOARD_KEY "/bell_mode" + +struct GsdKeyboardManagerPrivate +{ + gboolean have_xkb; + gint xkb_event_base; + guint notify; +}; + +static void gsd_keyboard_manager_class_init (GsdKeyboardManagerClass *klass); +static void gsd_keyboard_manager_init (GsdKeyboardManager *keyboard_manager); +static void gsd_keyboard_manager_finalize (GObject *object); + +G_DEFINE_TYPE (GsdKeyboardManager, gsd_keyboard_manager, G_TYPE_OBJECT) + +static gpointer manager_object = NULL; + + +#ifdef HAVE_X11_EXTENSIONS_XF86MISC_H +static gboolean +xfree86_set_keyboard_autorepeat_rate (int delay, int rate) +{ + gboolean res = FALSE; + int event_base_return; + int error_base_return; + + if (XF86MiscQueryExtension (GDK_DISPLAY (), + &event_base_return, + &error_base_return) == True) { + /* load the current settings */ + XF86MiscKbdSettings kbdsettings; + XF86MiscGetKbdSettings (GDK_DISPLAY (), &kbdsettings); + + /* assign the new values */ + kbdsettings.delay = delay; + kbdsettings.rate = rate; + XF86MiscSetKbdSettings (GDK_DISPLAY (), &kbdsettings); + res = TRUE; + } + + return res; +} +#endif /* HAVE_X11_EXTENSIONS_XF86MISC_H */ + +#ifdef HAVE_X11_EXTENSIONS_XKB_H +static gboolean +xkb_set_keyboard_autorepeat_rate (int delay, int rate) +{ + int interval = (rate <= 0) ? 1000000 : 1000/rate; + if (delay <= 0) + delay = 1; + return XkbSetAutoRepeatRate (GDK_DISPLAY (), + XkbUseCoreKbd, + delay, + interval); +} +#endif + +static char * +gsd_keyboard_get_hostname_key (const char *subkey) +{ + char hostname[HOST_NAME_MAX + 1]; + + if (gethostname (hostname, sizeof (hostname)) == 0 && + strcmp (hostname, "localhost") != 0 && + strcmp (hostname, "localhost.localdomain") != 0) { + char *escaped; + char *key; + + escaped = mateconf_escape_key (hostname, -1); + key = g_strconcat (GSD_KEYBOARD_KEY + "/host-", + escaped, + "/0/", + subkey, + NULL); + g_free (escaped); + return key; + } else + return NULL; +} + +#ifdef HAVE_X11_EXTENSIONS_XKB_H + +typedef enum { + NUMLOCK_STATE_OFF = 0, + NUMLOCK_STATE_ON = 1, + NUMLOCK_STATE_UNKNOWN = 2 +} NumLockState; + +static void +numlock_xkb_init (GsdKeyboardManager *manager) +{ + Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); + gboolean have_xkb; + int opcode, error_base, major, minor; + + have_xkb = XkbQueryExtension (dpy, + &opcode, + &manager->priv->xkb_event_base, + &error_base, + &major, + &minor) + && XkbUseExtension (dpy, &major, &minor); + + if (have_xkb) { + XkbSelectEventDetails (dpy, + XkbUseCoreKbd, + XkbStateNotifyMask, + XkbModifierLockMask, + XkbModifierLockMask); + } else { + g_warning ("XKB extension not available"); + } + + manager->priv->have_xkb = have_xkb; +} + +static unsigned +numlock_NumLock_modifier_mask (void) +{ + Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); + return XkbKeysymToModifiers (dpy, XK_Num_Lock); +} + +static void +numlock_set_xkb_state (NumLockState new_state) +{ + unsigned int num_mask; + Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); + if (new_state != NUMLOCK_STATE_ON && new_state != NUMLOCK_STATE_OFF) + return; + num_mask = numlock_NumLock_modifier_mask (); + XkbLockModifiers (dpy, XkbUseCoreKbd, num_mask, new_state ? num_mask : 0); +} + +static char * +numlock_mateconf_state_key (void) +{ + char *key = gsd_keyboard_get_hostname_key ("numlock_on"); + if (!key) { + g_message ("NumLock remembering disabled because hostname is set to \"localhost\""); + } + return key; +} + +static NumLockState +numlock_get_mateconf_state (MateConfClient *client) +{ + int curr_state; + GError *err = NULL; + char *key = numlock_mateconf_state_key (); + + if (!key) { + return NUMLOCK_STATE_UNKNOWN; + } + + curr_state = mateconf_client_get_bool (client, key, &err); + if (err) { + curr_state = NUMLOCK_STATE_UNKNOWN; + g_error_free (err); + } + + g_free (key); + return curr_state; +} + +static void +numlock_set_mateconf_state (MateConfClient *client, + NumLockState new_state) +{ + char *key; + + if (new_state != NUMLOCK_STATE_ON && new_state != NUMLOCK_STATE_OFF) { + return; + } + + key = numlock_mateconf_state_key (); + if (key) { + mateconf_client_set_bool (client, key, new_state, NULL); + g_free (key); + } +} + +static GdkFilterReturn +numlock_xkb_callback (GdkXEvent *xev_, + GdkEvent *gdkev_, + gpointer xkb_event_code) +{ + XEvent *xev = (XEvent *) xev_; + + if (xev->type == GPOINTER_TO_INT (xkb_event_code)) { + XkbEvent *xkbev = (XkbEvent *)xev; + if (xkbev->any.xkb_type == XkbStateNotify) + if (xkbev->state.changed & XkbModifierLockMask) { + unsigned num_mask = numlock_NumLock_modifier_mask (); + unsigned locked_mods = xkbev->state.locked_mods; + int numlock_state = !! (num_mask & locked_mods); + MateConfClient *client = mateconf_client_get_default (); + numlock_set_mateconf_state (client, numlock_state); + g_object_unref (client); + } + } + return GDK_FILTER_CONTINUE; +} + +static void +numlock_install_xkb_callback (GsdKeyboardManager *manager) +{ + if (!manager->priv->have_xkb) + return; + + gdk_window_add_filter (NULL, + numlock_xkb_callback, + GINT_TO_POINTER (manager->priv->xkb_event_base)); +} + +#endif /* HAVE_X11_EXTENSIONS_XKB_H */ + +static void +apply_settings (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + GsdKeyboardManager *manager) +{ + XKeyboardControl kbdcontrol; + gboolean repeat; + gboolean click; + int rate; + int delay; + int click_volume; + int bell_volume; + int bell_pitch; + int bell_duration; + char *volume_string; +#ifdef HAVE_X11_EXTENSIONS_XKB_H + gboolean rnumlock; +#endif /* HAVE_X11_EXTENSIONS_XKB_H */ + + repeat = mateconf_client_get_bool (client, KEY_REPEAT, NULL); + click = mateconf_client_get_bool (client, KEY_CLICK, NULL); + rate = mateconf_client_get_int (client, KEY_RATE, NULL); + delay = mateconf_client_get_int (client, KEY_DELAY, NULL); + click_volume = mateconf_client_get_int (client, KEY_CLICK_VOLUME, NULL); +#if 0 + bell_volume = mateconf_client_get_int (client, KEY_BELL_VOLUME, NULL); +#endif + bell_pitch = mateconf_client_get_int (client, KEY_BELL_PITCH, NULL); + bell_duration = mateconf_client_get_int (client, KEY_BELL_DURATION, NULL); + + volume_string = mateconf_client_get_string (client, KEY_BELL_MODE, NULL); + bell_volume = (volume_string && !strcmp (volume_string, "on")) ? 50 : 0; + g_free (volume_string); + +#ifdef HAVE_X11_EXTENSIONS_XKB_H + rnumlock = mateconf_client_get_bool (client, GSD_KEYBOARD_KEY "/remember_numlock_state", NULL); +#endif /* HAVE_X11_EXTENSIONS_XKB_H */ + + gdk_error_trap_push (); + if (repeat) { + gboolean rate_set = FALSE; + + XAutoRepeatOn (GDK_DISPLAY ()); + /* Use XKB in preference */ +#ifdef HAVE_X11_EXTENSIONS_XKB_H + rate_set = xkb_set_keyboard_autorepeat_rate (delay, rate); +#endif +#ifdef HAVE_X11_EXTENSIONS_XF86MISC_H + if (!rate_set) + rate_set = xfree86_set_keyboard_autorepeat_rate (delay, rate); +#endif + if (!rate_set) + g_warning ("Neither XKeyboard not Xfree86's keyboard extensions are available,\n" + "no way to support keyboard autorepeat rate settings"); + } else { + XAutoRepeatOff (GDK_DISPLAY ()); + } + + /* as percentage from 0..100 inclusive */ + if (click_volume < 0) { + click_volume = 0; + } else if (click_volume > 100) { + click_volume = 100; + } + kbdcontrol.key_click_percent = click ? click_volume : 0; + kbdcontrol.bell_percent = bell_volume; + kbdcontrol.bell_pitch = bell_pitch; + kbdcontrol.bell_duration = bell_duration; + XChangeKeyboardControl (GDK_DISPLAY (), + KBKeyClickPercent | KBBellPercent | KBBellPitch | KBBellDuration, + &kbdcontrol); + +#ifdef HAVE_X11_EXTENSIONS_XKB_H + if (manager->priv->have_xkb && rnumlock) { + numlock_set_xkb_state (numlock_get_mateconf_state (client)); + } +#endif /* HAVE_X11_EXTENSIONS_XKB_H */ + + XSync (GDK_DISPLAY (), FALSE); + gdk_error_trap_pop (); +} + +void +gsd_keyboard_manager_apply_settings (GsdKeyboardManager *manager) +{ + MateConfClient *client; + + client = mateconf_client_get_default (); + apply_settings (client, 0, NULL, manager); + g_object_unref (client); +} + +static gboolean +start_keyboard_idle_cb (GsdKeyboardManager *manager) +{ + MateConfClient *client; + + mate_settings_profile_start (NULL); + + g_debug ("Starting keyboard manager"); + + manager->priv->have_xkb = 0; + client = mateconf_client_get_default (); + + mateconf_client_add_dir (client, GSD_KEYBOARD_KEY, MATECONF_CLIENT_PRELOAD_RECURSIVE, NULL); + + /* Essential - xkb initialization should happen before */ + gsd_keyboard_xkb_set_post_activation_callback ((PostActivationCallback) gsd_load_modmap_files, NULL); + gsd_keyboard_xkb_init (client, manager); + +#ifdef HAVE_X11_EXTENSIONS_XKB_H + numlock_xkb_init (manager); +#endif /* HAVE_X11_EXTENSIONS_XKB_H */ + + /* apply current settings before we install the callback */ + gsd_keyboard_manager_apply_settings (manager); + + manager->priv->notify = mateconf_client_notify_add (client, GSD_KEYBOARD_KEY, + (MateConfClientNotifyFunc) apply_settings, manager, + NULL, NULL); + + g_object_unref (client); + +#ifdef HAVE_X11_EXTENSIONS_XKB_H + numlock_install_xkb_callback (manager); +#endif /* HAVE_X11_EXTENSIONS_XKB_H */ + + mate_settings_profile_end (NULL); + + return FALSE; +} + +gboolean +gsd_keyboard_manager_start (GsdKeyboardManager *manager, + GError **error) +{ + mate_settings_profile_start (NULL); + + g_idle_add ((GSourceFunc) start_keyboard_idle_cb, manager); + + mate_settings_profile_end (NULL); + + return TRUE; +} + +void +gsd_keyboard_manager_stop (GsdKeyboardManager *manager) +{ + GsdKeyboardManagerPrivate *p = manager->priv; + + g_debug ("Stopping keyboard manager"); + + if (p->notify != 0) { + MateConfClient *client = mateconf_client_get_default (); + mateconf_client_remove_dir (client, GSD_KEYBOARD_KEY, NULL); + mateconf_client_notify_remove (client, p->notify); + g_object_unref (client); + p->notify = 0; + } + +#if HAVE_X11_EXTENSIONS_XKB_H + if (p->have_xkb) { + gdk_window_remove_filter (NULL, + numlock_xkb_callback, + GINT_TO_POINTER (p->xkb_event_base)); + } +#endif /* HAVE_X11_EXTENSIONS_XKB_H */ + + gsd_keyboard_xkb_shutdown (); +} + +static void +gsd_keyboard_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsdKeyboardManager *self; + + self = GSD_KEYBOARD_MANAGER (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsd_keyboard_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsdKeyboardManager *self; + + self = GSD_KEYBOARD_MANAGER (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GObject * +gsd_keyboard_manager_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GsdKeyboardManager *keyboard_manager; + GsdKeyboardManagerClass *klass; + + klass = GSD_KEYBOARD_MANAGER_CLASS (g_type_class_peek (GSD_TYPE_KEYBOARD_MANAGER)); + + keyboard_manager = GSD_KEYBOARD_MANAGER (G_OBJECT_CLASS (gsd_keyboard_manager_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + return G_OBJECT (keyboard_manager); +} + +static void +gsd_keyboard_manager_dispose (GObject *object) +{ + GsdKeyboardManager *keyboard_manager; + + keyboard_manager = GSD_KEYBOARD_MANAGER (object); + + G_OBJECT_CLASS (gsd_keyboard_manager_parent_class)->dispose (object); +} + +static void +gsd_keyboard_manager_class_init (GsdKeyboardManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gsd_keyboard_manager_get_property; + object_class->set_property = gsd_keyboard_manager_set_property; + object_class->constructor = gsd_keyboard_manager_constructor; + object_class->dispose = gsd_keyboard_manager_dispose; + object_class->finalize = gsd_keyboard_manager_finalize; + + g_type_class_add_private (klass, sizeof (GsdKeyboardManagerPrivate)); +} + +static void +gsd_keyboard_manager_init (GsdKeyboardManager *manager) +{ + manager->priv = GSD_KEYBOARD_MANAGER_GET_PRIVATE (manager); +} + +static void +gsd_keyboard_manager_finalize (GObject *object) +{ + GsdKeyboardManager *keyboard_manager; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSD_IS_KEYBOARD_MANAGER (object)); + + keyboard_manager = GSD_KEYBOARD_MANAGER (object); + + g_return_if_fail (keyboard_manager->priv != NULL); + + G_OBJECT_CLASS (gsd_keyboard_manager_parent_class)->finalize (object); +} + +GsdKeyboardManager * +gsd_keyboard_manager_new (void) +{ + if (manager_object != NULL) { + g_object_ref (manager_object); + } else { + manager_object = g_object_new (GSD_TYPE_KEYBOARD_MANAGER, NULL); + g_object_add_weak_pointer (manager_object, + (gpointer *) &manager_object); + } + + return GSD_KEYBOARD_MANAGER (manager_object); +} |