From 1d1b1b00edc6f8ae723b4e536db20fe1db5752fd Mon Sep 17 00:00:00 2001 From: Nick Schermer Date: Sun, 13 Jul 2008 15:14:04 +0000 Subject: [PATCH] * dialogs/mouse-settings/mouse-dialog.glade, dialogs/mouse-settings/main.c, dialogs/mouse-settings/Makefile.am, xfce4-settings-helper/pointers.h, xfce4-settings-helper/pointers.c: Merge multi pointer branch code. * dialogs/keyboard-settings/main.c: Release the channels after running the dialog. * xfce4-settings-helper/accessx.c, xfce4-settings-helper/accessx.h, xfce4-settings-helper/xkb.c, xfce4-settings-helper/xkb.h: Move code into objects. Will make things look nice and allows the code to cleanup when the objects are released in main.c. * xfce4-settings-helper/main.c: Use the objects and do some more checking before running the dialog. * xfce4-settings-helper/accessx.c: Make disabling the mouse keys work; don't call channel_get functions when not needed. * dialogs/keyboard-settings/main.c: Release the channels after running the dialog, also destroy the dialog after using it. (Old svn revision: 27274) --- ChangeLog | 23 + TODO | 8 +- configure.ac.in | 229 +++++- dialogs/keyboard-settings/main.c | 11 +- dialogs/mouse-settings/Makefile.am | 15 +- dialogs/mouse-settings/main.c | 953 ++++++++++++++++++---- dialogs/mouse-settings/mouse-dialog.glade | 723 ++++++++-------- xfce4-settings-helper/Makefile.am | 97 ++- xfce4-settings-helper/accessx.c | 465 +++++++---- xfce4-settings-helper/accessx.h | 18 +- xfce4-settings-helper/main.c | 147 ++-- xfce4-settings-helper/pointers.c | 643 +++++++++++++++ xfce4-settings-helper/pointers.h | 35 + xfce4-settings-helper/xkb.c | 278 ++++--- xfce4-settings-helper/xkb.h | 19 +- 15 files changed, 2682 insertions(+), 982 deletions(-) create mode 100644 xfce4-settings-helper/pointers.c create mode 100644 xfce4-settings-helper/pointers.h diff --git a/ChangeLog b/ChangeLog index c6936d08..4e79d78b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2008-07-13 Nick Schermer + + * dialogs/mouse-settings/mouse-dialog.glade, + dialogs/mouse-settings/main.c, + dialogs/mouse-settings/Makefile.am, + xfce4-settings-helper/pointers.h, + xfce4-settings-helper/pointers.c: Merge multi pointer + branch code. + * dialogs/keyboard-settings/main.c: Release the channels + after running the dialog. + * xfce4-settings-helper/accessx.c, + xfce4-settings-helper/accessx.h, + xfce4-settings-helper/xkb.c, + xfce4-settings-helper/xkb.h: Move code into objects. Will + make things look nice and allows the code to cleanup when + the objects are released in main.c. + * xfce4-settings-helper/main.c: Use the objects and do some + more checking before running the dialog. + * xfce4-settings-helper/accessx.c: Make disabling the mouse + keys work; don't call channel_get functions when not needed. + * dialogs/keyboard-settings/main.c: Release the channels after + running the dialog, also destroy the dialog after using it. + 2008-07-11 Nick Schermer * dialogs/mouse-settings/mouse-dialog.glade: Add some tooltips diff --git a/TODO b/TODO index 569472ee..625c8b07 100644 --- a/TODO +++ b/TODO @@ -9,8 +9,6 @@ Keyboard settings (Jannis) Mouse settings (Nick) ------------------------------------------------------------------------ - * Integrate multi pointer branch. - * Make hal and libxcursor an optional dependency. - * Check dependency versions of libxi and libxcursor. - * Make it work a bit better with onyl 1 pointer. - * Move the device information into a separate xfconf channel. + * Fixup some of the Makefiles. + * Add signal watch to xfce4-setting-helper to quit the main loop when + receiving a sigterm. diff --git a/configure.ac.in b/configure.ac.in index 26a57a8b..e84a389f 100644 --- a/configure.ac.in +++ b/configure.ac.in @@ -1,5 +1,5 @@ dnl -dnl Copyright (c) 2006 +dnl Copyright (c) 2006 - 2008 dnl The Xfce development team. All rights reserved. dnl dnl Originally written for Xfce by Benedikt Meurer @@ -21,52 +21,183 @@ dnl *** Debugging support for SVN snapshots *** dnl ******************************************* m4_define([xfce4_settings_debug_default], [ifelse(xfce4_settings_version_tag(), [svn], [full], [minimum])]) +dnl *************************** +dnl *** Initialize autoconf *** +dnl *************************** AC_COPYRIGHT([Copyright (c) 2008 - The Xfce development team. All rights reserved. - -Written for Xfce by Stephan Arts .]) - + The Xfce development team. All rights reserved.]) AC_INIT([xfce4-settings], [xfce4_settings_version], [http://bugzilla.xfce.org/]) AC_PREREQ([2.50]) +AC_REVISION([$Id]) -XFCE4_SETTINGS_VERSION=xfce4_settings_version -AM_INIT_AUTOMAKE([xfce4-settings], [$XFCE4_SETTINGS_VERSION]) +dnl *************************** +dnl *** Initialize automake *** +dnl *************************** +AM_INIT_AUTOMAKE([1.8 dist-bzip2 tar-ustar]) AM_CONFIG_HEADER([config.h]) -AM_MAINTAINER_MODE - -dnl check for UNIX variants -AC_AIX -AC_ISC_POSIX -AC_MINIX -AM_CONDITIONAL([HAVE_CYGWIN], [test "`uname | grep \"CYGWIN\"`" != ""]) - - -dnl check for standard header files -AC_PROG_CC +AM_MAINTAINER_MODE() + +dnl ******************************* +dnl *** Check for UNIX variants *** +dnl ******************************* +AC_AIX() +AC_ISC_POSIX() +AC_MINIX() + +dnl ******************************** +dnl *** Check for basic programs *** +dnl ******************************** +AC_PROG_CC() +AC_PROG_LD() +AC_PROG_INSTALL() AC_PROG_INTLTOOL([0.31], [no-xml]) -AC_HEADER_STDC -AC_CHECK_HEADERS([string.h math.h stdio.h stdlib.h]) +dnl ************************** +dnl *** Initialize libtool *** +dnl ************************** +AC_PROG_LIBTOOL() -dnl Check for i18n support -XDT_I18N([@LINGUAS@]) +dnl ********************************** +dnl *** Check for standard headers *** +dnl ********************************** +AC_CHECK_HEADERS([errno.h memory.h math.h stdlib.h stdio.h string.h unistd.h]) +dnl ****************************** +dnl *** Check for i18n support *** +dnl ****************************** +XDT_I18N([@LINGUAS@]) +dnl *********************************** +dnl *** Check for required packages *** +dnl *********************************** +XDT_CHECK_PACKAGE([EXO], [exo-0.3], [0.3.0]) XDT_CHECK_PACKAGE([GTK], [gtk+-2.0], [2.10.0]) XDT_CHECK_PACKAGE([GLIB], [glib-2.0], [2.12.0]) -XDT_CHECK_PACKAGE([GOBJECT], [gobject-2.0], [2.12.0]) -XDT_CHECK_PACKAGE([DBUS_GLIB], [dbus-glib-1], [0.34]) -XDT_CHECK_PACKAGE([XFCONF], [libxfconf-0], [0]) -XDT_CHECK_PACKAGE([LIBXFCEGUI4], [libxfcegui4-1.0], [4.4.0]) +XDT_CHECK_PACKAGE([GTHREAD], [gthread-2.0], [2.12.0]) XDT_CHECK_PACKAGE([LIBXFCE4UTIL], [libxfce4util-1.0], [4.4.0]) -XDT_CHECK_PACKAGE([EXO], [exo-0.3], [0.3.0]) -XDT_CHECK_OPTIONAL_PACKAGE([GLADE], [libglade-2.0], [2.0.0]) - -XDT_CHECK_PACKAGE([LIBNOTIFY], [libnotify], [0.1.3]) - - -dnl check for debugging support -XDT_FEATURE_DEBUG +XDT_CHECK_PACKAGE([LIBXFCEGUI4], [libxfcegui4-1.0], [4.4.0]) +XDT_CHECK_PACKAGE([GLADE], [libglade-2.0], [2.0.0]) +XDT_CHECK_PACKAGE([XFCONF], [libxfconf-0], [0]) +XDT_CHECK_PACKAGE([DBUS_GLIB], [dbus-glib-1], [0.34]) +XDT_CHECK_PACKAGE([XI], [xi], [1.1.0]) +XDT_CHECK_PACKAGE([LIBX11], [x11], [1.1.0]) + +dnl ************************************** +dnl *** Optional support for Libnotify *** +dnl ************************************** +XDT_CHECK_OPTIONAL_PACKAGE([LIBNOTIFY], [libnotify], [0.1.3], + [libnotify], [Notification support]) + +dnl ******************************** +dnl *** Optional support for HAL *** +dnl ******************************** +XDT_CHECK_OPTIONAL_PACKAGE([HAL], [hal], [0.5.0], + [hal], [Hotplugging support]) + +dnl ************************************ +dnl *** Optional support for Xcursor *** +dnl ************************************ +XDT_CHECK_OPTIONAL_PACKAGE([XCURSOR], [xcursor], [1.1.0], + [xcursor], [Cursor themes support]) + +dnl ************************************* +dnl *** Optional support for Xf86misc *** +dnl ************************************* +XDT_CHECK_OPTIONAL_PACKAGE([XF86MISC], [libxxf86misc], [0.9.0], + [xf86misc], [Support for the X Miscellaneous extension wire protocol]) + +dnl *********************************** +dnl *** Check for debugging support *** +dnl *********************************** +AC_ARG_ENABLE([debug], +AC_HELP_STRING([--enable-debug=@<:@no/minimum/yes/full@:>@], [Turn on debugging @<:@default=xfce4_settings_debug_default@:>@]), + [], [enable_debug=xfce4_settings_debug_default]) +AC_MSG_CHECKING([whether to enable debugging support]) +if test x"$enable_debug" = x"full" -o x"$enable_debug" = x"yes"; then + dnl Print the result + AC_MSG_RESULT([$enable_debug]) + + dnl Make sure we detect possible errors (if supported) + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -Wall -Werror" + AC_MSG_CHECKING([whether $CC accepts -Wall -Werror]) + AC_COMPILE_IFELSE(AC_LANG_SOURCE([int x;]), [ + AC_MSG_RESULT([yes]) + PLATFORM_CFLAGS="$PLATFORM_CFLAGS -Wall -Werror" + ], [ + AC_MSG_RESULT([no]) + ]) + CFLAGS="$save_CFLAGS" + + dnl Paranoia for --enable-debug=full + if test x"$enable_debug" = x"full"; then + dnl Enable extensive debugging + PLATFORM_CPPFLAGS="$PLATFORM_CPPFLAGS -DG_ENABLE_DEBUG" + + dnl Use -O0 -g3 if the compiler supports it + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -O0 -g3" + AC_MSG_CHECKING([whether $CC accepts -O0 -g3]) + AC_COMPILE_IFELSE(AC_LANG_SOURCE([int x;]), [ + AC_MSG_RESULT([yes]) + PLATFORM_CFLAGS="$PLATFORM_CFLAGS -O0 -g3" + ], [ + AC_MSG_RESULT([no]) + ]) + CFLAGS="$save_CFLAGS" + fi +else + dnl Print the result + AC_MSG_RESULT([$enable_debug]) + + dnl Disable debugging (release build) + PLATFORM_CPPFLAGS="$PLATFORM_CPPFLAGS -DNDEBUG" + + dnl Disable object cast checks + PLATFORM_CPPFLAGS="$PLATFORM_CPPFLAGS -DG_DISABLE_CAST_CHECKS" + + dnl Disable all checks for --enable-debug=no + if test x"$enable_debug" = x"no"; then + PLATFORM_CPPFLAGS="$PLATFORM_CPPFLAGS -DG_DISABLE_ASSERT -DG_DISABLE_CHECKS" + fi +fi + +dnl ************************************** +dnl *** Check for linker optimizations *** +dnl ************************************** +AC_MSG_CHECKING([whether $LD accepts --as-needed]) +case `$LD --as-needed -v 2>&1 &1 $@ endif diff --git a/dialogs/mouse-settings/main.c b/dialogs/mouse-settings/main.c index eb8590ee..99c23814 100644 --- a/dialogs/mouse-settings/main.c +++ b/dialogs/mouse-settings/main.c @@ -1,7 +1,5 @@ /* - * Copyright (c) 2008 Stephan Arts * Copyright (c) 2008 Nick Schermer - * Copyright (c) 2008 Mike Massonnet * * 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 @@ -36,38 +34,61 @@ #endif #include +#include +#ifdef HAVE_XCURSOR #include +#endif /* !HAVE_XCURSOR */ + +#ifdef HAVE_HAL +#include +#include +#include +#endif /* !HAVE_HAL */ #include +#include #include + #include #include #include -#include "mouse-dialog_glade.h" +#include "mouse-dialog-glade.h" + /* settings */ +#ifdef HAVE_XCURSOR #define PREVIEW_ROWS (3) #define PREVIEW_COLUMNS (6) #define PREVIEW_SIZE (24) #define PREVIEW_SPACING (2) +#endif /* !HAVE_XCURSOR */ +/* global setting channels */ +XfconfChannel *xsettings_channel; +XfconfChannel *xdevices_channel; -/* treeview columns */ -enum -{ - COLUMN_THEME_PIXBUF, - COLUMN_THEME_PATH, - COLUMN_THEME_NAME, - COLUMN_THEME_REAL_NAME, - COLUMN_THEME_COMMENT, - N_THEME_COLUMNS -}; +/* lock counter to avoid signals during updates */ +static gint locked = 0; +/* the display for this window */ +static GdkDisplay *display; +/* device update id */ +static guint timeout_id = 0; +/* option entries */ +static gboolean opt_version = FALSE; + +static GOptionEntry option_entries[] = +{ + { "version", 'v', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &opt_version, N_("Version information"), NULL }, + { NULL } +}; + +#ifdef HAVE_XCURSOR /* icon names for the preview widget */ static const gchar *preview_names[] = { "left_ptr", "left_ptr_watch", "watch", "hand2", @@ -81,22 +102,29 @@ static const gchar *preview_names[] = { "top_side", "top_tee" }; - - -/* option entries */ -static gboolean opt_version = FALSE; - -static GOptionEntry option_entries[] = +enum { - { "version", 'v', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &opt_version, N_("Version information"), NULL }, - { NULL } + COLUMN_THEME_PIXBUF, + COLUMN_THEME_PATH, + COLUMN_THEME_NAME, + COLUMN_THEME_DISPLAY_NAME, + COLUMN_THEME_COMMENT, + N_THEME_COLUMNS }; +#endif /* !HAVE_XCURSOR */ -/* global xfconf channel */ -static XfconfChannel *xsettings_channel; - +enum +{ + COLUMN_DEVICE_ICON, + COLUMN_DEVICE_NAME, + COLUMN_DEVICE_DISPLAY_NAME, + COLUMN_DEVICE_XID, + COLUMN_DEVICE_NBUTTONS, + N_DEVICE_COLUMNS +}; +#ifdef HAVE_XCURSOR static GdkPixbuf * mouse_settings_themes_pixbuf_from_filename (const gchar *filename, guint size) @@ -194,7 +222,7 @@ mouse_settings_themes_preview_icon (const gchar *path) static void mouse_settings_themes_preview_image (const gchar *path, - GtkWidget *image) + GtkImage *image) { GdkPixbuf *pixbuf; GdkPixbuf *preview; @@ -257,25 +285,99 @@ mouse_settings_themes_preview_image (const gchar *path, -static GtkTreePath * -mouse_settings_themes_populate_store (GtkListStore *store) +static void +mouse_settings_themes_selection_changed (GtkTreeSelection *selection, + GladeXML *gxml) { - const gchar *path; - gchar **basedirs; - gint i; - gchar *homedir; - GDir *dir; - const gchar *theme; - gchar *filename; - gchar *index_file; - XfceRc *rc; - const gchar *name; - const gchar *comment; + GtkTreeModel *model; GtkTreeIter iter; - gint position = 0; - GdkPixbuf *pixbuf; - gchar *active_theme; - GtkTreePath *active_path = NULL; + gboolean has_selection; + gchar *path, *name; + GtkWidget *image; + + has_selection = gtk_tree_selection_get_selected (selection, &model, &iter); + if (G_LIKELY (has_selection)) + { + /* get theme information from model */ + gtk_tree_model_get (model, &iter, COLUMN_THEME_PATH, &path, + COLUMN_THEME_NAME, &name, -1); + + /* update the preview widget */ + image = glade_xml_get_widget (gxml, "mouse-theme-preview"); + mouse_settings_themes_preview_image (path, GTK_IMAGE (image)); + + /* write configuration (not during a lock) */ + if (locked == 0) + xfconf_channel_set_string (xsettings_channel, "/Gtk/CursorThemeName", name); + + /* cleanup */ + g_free (path); + g_free (name); + } +} + + + +static gint +mouse_settings_themes_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gchar *name_a, *name_b; + gint retval; + + /* get the names from the model */ + gtk_tree_model_get (model, a, COLUMN_THEME_DISPLAY_NAME, &name_a, -1); + gtk_tree_model_get (model, b, COLUMN_THEME_DISPLAY_NAME, &name_b, -1); + + /* make sure the names are not null */ + if (G_UNLIKELY (name_a == NULL)) + name_a = g_strdup (""); + if (G_UNLIKELY (name_b == NULL)) + name_b = g_strdup (""); + + /* sort the names but keep Default on top */ + if (g_utf8_collate (name_a, _("Default")) == 0) + retval = -1; + else if (g_utf8_collate (name_b, _("Default")) == 0) + retval = 1; + else + retval = g_utf8_collate (name_a, name_b); + + /* cleanup */ + g_free (name_a); + g_free (name_b); + + return retval; +} + + + +static void +mouse_settings_themes_populate_store (GladeXML *gxml) +{ + const gchar *path; + gchar **basedirs; + gint i; + gchar *homedir; + GDir *dir; + const gchar *theme; + gchar *filename; + gchar *index_file; + XfceRc *rc; + const gchar *name; + const gchar *comment; + GtkTreeIter iter; + gint position = 0; + GdkPixbuf *pixbuf; + gchar *active_theme; + GtkTreePath *active_path = NULL; + GtkListStore *store; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkWidget *treeview; + GtkTreeSelection *selection; /* get the cursor paths */ #if XCURSOR_LIB_MAJOR == 1 && XCURSOR_LIB_MINOR < 1 @@ -289,15 +391,18 @@ mouse_settings_themes_populate_store (GtkListStore *store) /* get the active theme */ active_theme = xfconf_channel_get_string (xsettings_channel, "/Gtk/CursorThemeName", "default"); - + + /* create the store */ + store = gtk_list_store_new (N_THEME_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + /* insert default */ gtk_list_store_insert_with_values (store, &iter, position++, COLUMN_THEME_NAME, "default", - COLUMN_THEME_REAL_NAME, _("Default"), -1); + COLUMN_THEME_DISPLAY_NAME, _("Default"), -1); - /* check if the users uses the default theme */ - if (strcmp (active_theme, "default") == 0) - active_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter); + /* store the default path, so we always select a theme */ + active_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter); if (G_LIKELY (basedirs)) { @@ -337,12 +442,15 @@ mouse_settings_themes_populate_store (GtkListStore *store) gtk_list_store_insert_with_values (store, &iter, position++, COLUMN_THEME_PIXBUF, pixbuf, COLUMN_THEME_NAME, theme, - COLUMN_THEME_REAL_NAME, theme, + COLUMN_THEME_DISPLAY_NAME, theme, COLUMN_THEME_PATH, filename, -1); /* check if this is the active theme, set the path */ - if (strcmp (active_theme, theme) == 0 && active_path == NULL) + if (strcmp (active_theme, theme) == 0) + { + gtk_tree_path_free (active_path); active_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter); + } /* release pixbuf */ if (G_LIKELY (pixbuf)) @@ -368,7 +476,7 @@ mouse_settings_themes_populate_store (GtkListStore *store) /* update store */ gtk_list_store_set (store, &iter, - COLUMN_THEME_REAL_NAME, name, + COLUMN_THEME_DISPLAY_NAME, name, COLUMN_THEME_COMMENT, comment, -1); } @@ -400,155 +508,536 @@ mouse_settings_themes_populate_store (GtkListStore *store) /* cleanup */ g_free (active_theme); - return active_path; + /* set the treeview store */ + treeview = glade_xml_get_widget (gxml, "mouse-theme-treeview"); + gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store)); +#if GTK_CHECK_VERSION (2, 12, 0) + gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (treeview), COLUMN_THEME_COMMENT); +#endif + + /* setup the columns */ + renderer = gtk_cell_renderer_pixbuf_new (); + column = gtk_tree_view_column_new_with_attributes ("", renderer, "pixbuf", COLUMN_THEME_PIXBUF, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("", renderer, "text", COLUMN_THEME_DISPLAY_NAME, NULL); + g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + + /* setup selection */ + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (mouse_settings_themes_selection_changed), gxml); + + /* select the active theme in the treeview */ + gtk_tree_view_set_cursor (GTK_TREE_VIEW (treeview), active_path, NULL, FALSE); + gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (treeview), active_path, NULL, FALSE, 0.5, 0.0); + gtk_tree_path_free (active_path); + + /* sort the store */ + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store), COLUMN_THEME_DISPLAY_NAME, mouse_settings_themes_sort_func, NULL, NULL); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), COLUMN_THEME_DISPLAY_NAME, GTK_SORT_ASCENDING); + + /* release the store */ + g_object_unref (G_OBJECT (store)); } +#endif /* !HAVE_XCURSOR */ static void -mouse_settings_themes_selection_changed (GtkTreeSelection *selection, +mouse_settings_device_selection_changed (GtkTreeSelection *selection, GladeXML *gxml) { - GtkTreeModel *model; - GtkTreeIter iter; - gboolean has_selection; - gchar *path, *name; + gint nbuttons; + Display *xdisplay; + XDevice *device; + XFeedbackState *states; + gint nstates; + XPtrFeedbackState *state; + gint i; + guchar *buttonmap; + gint id_1 = 0, id_3 = 0; + gint id_4 = 0, id_5 = 0; + gdouble acceleration = -1.00; + gint threshold = -1; + GtkWidget *widget; + GtkTreeModel *model; + GtkTreeIter iter; + gboolean has_selection; + XID xid; + + /* lock the dialog */ + locked++; + /* get the selected item */ has_selection = gtk_tree_selection_get_selected (selection, &model, &iter); if (G_LIKELY (has_selection)) { - /* get theme information from model */ - gtk_tree_model_get (model, &iter, COLUMN_THEME_PATH, &path, - COLUMN_THEME_NAME, &name, -1); + /* get device id and number of buttons */ + gtk_tree_model_get (model, &iter, COLUMN_DEVICE_XID, &xid, + COLUMN_DEVICE_NBUTTONS, &nbuttons, -1); - /* update the preview widget */ - mouse_settings_themes_preview_image (path, glade_xml_get_widget (gxml, "cursor_preview_image")); + /* get the x display */ + xdisplay = gdk_x11_display_get_xdisplay (display); - /* write configuration */ - xfconf_channel_set_string (xsettings_channel, "/Gtk/CursorThemeName", name); + /* open the device */ + device = XOpenDevice (xdisplay, xid); - /* cleanup */ - g_free (path); - g_free (name); + if (G_LIKELY (device)) + { + /* allocate button map */ + buttonmap = g_new0 (guchar, nbuttons); + + /* get the button mapping */ + XGetDeviceButtonMapping (xdisplay, device, buttonmap, nbuttons); + + /* figure out the position of the first and second/third button in the map */ + for (i = 0; i < nbuttons; i++) + { + if (buttonmap[i] == 1) + id_1 = i; + else if (buttonmap[i] == (nbuttons < 3 ? 2 : 3)) + id_3 = i; + else if (buttonmap[i] == 4) + id_4 = i; + else if (buttonmap[i] == 5) + id_5 = i; + } + + /* cleanup */ + g_free (buttonmap); + + /* get the feedback states for this device */ + states = XGetFeedbackControl (xdisplay, device, &nstates); + + /* intial values */ + acceleration = threshold = -1; + + /* get the pointer feedback class */ + for (i = 0; i < nstates; i++) + { + if (states->class == PtrFeedbackClass) + { + /* get the state */ + state = (XPtrFeedbackState *) states; + + /* set values */ + acceleration = (gdouble) state->accelNum / (gdouble) state->accelDenom; + threshold = state->threshold; + + /* done */ + break; + } + + /* advance the offset */ + states = (XFeedbackState *) ((gchar *) states + states->length); + } + + /* close the device */ + XCloseDevice (xdisplay, device); + } } + + /* update button order */ + widget = glade_xml_get_widget (gxml, id_1 > id_3 ? "mouse-left-handed" : "mouse-right-handed"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE); + + widget = glade_xml_get_widget (gxml, "mouse-reverse-scrolling"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), !!(id_5 < id_4)); + gtk_widget_set_sensitive (widget, nbuttons >= 5); + + /* update acceleration scale */ + widget = glade_xml_get_widget (gxml, "mouse-acceleration-scale"); + gtk_range_set_value (GTK_RANGE (widget), acceleration); + gtk_widget_set_sensitive (GTK_WIDGET (widget), acceleration != -1); + + /* update threshold scale */ + widget = glade_xml_get_widget (gxml, "mouse-threshold-scale"); + gtk_range_set_value (GTK_RANGE (widget), threshold); + gtk_widget_set_sensitive (GTK_WIDGET (widget), threshold != -1); + + /* unlock */ + locked--; } -static gint -mouse_settings_themes_sort_func (GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer user_data) +static void +mouse_settings_device_save (GladeXML *gxml) { - gchar *name_a, *name_b; - gint retval; - - /* get the names from the model */ - gtk_tree_model_get (model, a, COLUMN_THEME_REAL_NAME, &name_a, -1); - gtk_tree_model_get (model, b, COLUMN_THEME_REAL_NAME, &name_b, -1); - - /* make sure the names are not null */ - if (G_UNLIKELY (name_a == NULL)) - name_a = g_strdup (""); - if (G_UNLIKELY (name_b == NULL)) - name_b = g_strdup (""); - - /* sort the names but keep Default on top */ - if (g_utf8_collate (name_a, _("Default")) == 0) - retval = -1; - else if (g_utf8_collate (name_b, _("Default")) == 0) - retval = 1; - else - retval = g_utf8_collate (name_a, name_b); - - /* cleanup */ - g_free (name_a); - g_free (name_b); - - return retval; + GtkWidget *treeview; + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + gboolean has_selection; + gchar *name; + GtkWidget *widget; + gchar *property_name; + gboolean righthanded; + gint threshold; + gdouble acceleration; + gboolean reverse_scrolling; + + g_return_if_fail (GLADE_IS_XML (gxml)); + + /* leave when locked */ + if (locked > 0) + return; + + /* get the treeview */ + treeview = glade_xml_get_widget (gxml, "mouse-devices-treeview"); + + /* get the selection */ + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); + + has_selection = gtk_tree_selection_get_selected (selection, &model, &iter); + if (G_LIKELY (has_selection)) + { + /* get device id and number of buttons */ + gtk_tree_model_get (model, &iter, COLUMN_DEVICE_NAME, &name, -1); + + if (G_LIKELY (name)) + { + /* store the button order */ + widget = glade_xml_get_widget (gxml, "mouse-right-handed"); + property_name = g_strdup_printf ("/Pointers/%s/RightHanded", name); + righthanded = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + if (xfconf_channel_get_bool (xdevices_channel, property_name, TRUE) != righthanded) + xfconf_channel_set_bool (xdevices_channel, property_name, righthanded); + g_free (property_name); + + /* store reverse scrolling */ + widget = glade_xml_get_widget (gxml, "mouse-reverse-scrolling"); + property_name = g_strdup_printf ("/Pointers/%s/ReverseScrolling", name); + reverse_scrolling = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + if (xfconf_channel_get_bool (xdevices_channel, property_name, TRUE) != reverse_scrolling) + xfconf_channel_set_bool (xdevices_channel, property_name, reverse_scrolling); + g_free (property_name); + + /* store the threshold */ + widget = glade_xml_get_widget (gxml, "mouse-threshold-scale"); + property_name = g_strdup_printf ("/Pointers/%s/Threshold", name); + threshold = gtk_range_get_value (GTK_RANGE (widget)); + if (xfconf_channel_get_int (xdevices_channel, property_name, -1) != threshold) + xfconf_channel_set_int (xdevices_channel, property_name, threshold); + g_free (property_name); + + /* store the acceleration */ + widget = glade_xml_get_widget (gxml, "mouse-acceleration-scale"); + property_name = g_strdup_printf ("/Pointers/%s/Acceleration", name); + acceleration = gtk_range_get_value (GTK_RANGE (widget)); + if (xfconf_channel_get_double (xdevices_channel, property_name, -1) != acceleration) + xfconf_channel_set_double (xdevices_channel, property_name, acceleration); + g_free (property_name); + + /* cleanup */ + g_free (name); + } + } } -static GtkWidget * -mouse_settings_dialog_new_from_xml (GladeXML *gxml) +static gchar * +mouse_settings_device_xfconf_name (const gchar *name) { + GString *string; + const gchar *p; + + /* NOTE: this function exists in both the dialog and + * helper code and they have to identical! */ + + /* allocate a string */ + string = g_string_sized_new (strlen (name)); + + /* create a name with only valid chars */ + for (p = name; *p != '\0'; p++) + { + if ((*p >= 'A' && *p <= 'Z') + || (*p >= 'a' && *p <= 'z') + || (*p >= '0' && *p <= '9') + || *p == '_' || *p == '-') + string = g_string_append_c (string, *p); + else if (*p == ' ') + string = g_string_append_c (string, '_'); + } + + /* return the new string */ + return g_string_free (string, FALSE); +} + + + +static void +mouse_settings_device_populate_store (GladeXML *gxml, + gboolean create_store) +{ + Display *xdisplay; + XDeviceInfo *device_list, *device_info; + gchar *display_name, *usb; + gshort num_buttons; + gint ndevices; + gint i, m; + XAnyClassPtr ptr; + GtkTreeIter iter; GtkListStore *store; GtkWidget *treeview; + GtkTreePath *path; GtkTreeViewColumn *column; GtkCellRenderer *renderer; GtkTreeSelection *selection; - GtkWidget *widget; - GtkTreePath *path; - GtkWidget *dialog; - GtkAdjustment *adjustment; + gchar *device_name; - /* setup the icon theme treeview */ - store = gtk_list_store_new (N_THEME_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + /* lock */ + locked++; - /* add all the themes to the tree */ - path = mouse_settings_themes_populate_store (store); + /* get the treeview */ + treeview = glade_xml_get_widget (gxml, "mouse-devices-treeview"); - treeview = glade_xml_get_widget (gxml, "treeview_cursor_theme"); - gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store)); -#if GTK_CHECK_VERSION (2, 12, 0) - gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (treeview), COLUMN_THEME_COMMENT); -#endif + /* create or get the store */ + if (G_LIKELY (create_store)) + { + store = gtk_list_store_new (N_DEVICE_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT); + } + else + { + store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (treeview))); + gtk_list_store_clear (store); + } - g_object_unref (G_OBJECT (store)); + /* get the x display */ + xdisplay = gdk_x11_display_get_xdisplay (display); - renderer = gtk_cell_renderer_pixbuf_new (); - column = gtk_tree_view_column_new_with_attributes ("", renderer, "pixbuf", COLUMN_THEME_PIXBUF, NULL); - gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + /* get all the registered devices */ + device_list = XListInputDevices (xdisplay, &ndevices); - renderer = gtk_cell_renderer_text_new (); - column = gtk_tree_view_column_new_with_attributes ("", renderer, "text", COLUMN_THEME_REAL_NAME, NULL); - g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); - gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + for (i = 0; i < ndevices; i++) + { + /* get the device */ + device_info = &device_list[i]; + + /* filter out the pointer devices */ + if (device_info->use == IsXExtensionPointer) + { + /* get the device name */ + display_name = g_strdup (device_info->name); + + /* get rid of usb crap in the name */ + if ((usb = strstr (display_name, "-usb")) != NULL) + *usb = '\0'; + + /* get the device classes */ + ptr = device_info->inputclassinfo; + + /* walk all the classes */ + for (m = 0, num_buttons = 0; m < device_info->num_classes; m++) + { + /* find the button class */ + if (ptr->class == ButtonClass) + { + /* get the number of buttons */ + num_buttons = ((XButtonInfoPtr) ptr)->num_buttons; + + /* done */ + break; + } + + /* advance the offset */ + ptr = (XAnyClassPtr) ((gchar *) ptr + ptr->length); + } + + /* insert the device if it has buttons */ + if (G_LIKELY (num_buttons > 0)) + { + /* create a valid xfconf device name */ + device_name = mouse_settings_device_xfconf_name (device_info->name); + + /* insert in the store */ + gtk_list_store_insert_with_values (store, &iter, i, + COLUMN_DEVICE_ICON, "input-mouse", + COLUMN_DEVICE_NAME, device_name, + COLUMN_DEVICE_DISPLAY_NAME, display_name, + COLUMN_DEVICE_XID, device_info->id, + COLUMN_DEVICE_NBUTTONS, num_buttons, -1); + + /* cleanup */ + g_free (device_name); + } + + /* cleanup */ + g_free (display_name); + } + } + /* cleanup */ + XFreeDeviceList (device_list); + + /* get the selection */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); - gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); - g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (mouse_settings_themes_selection_changed), gxml); - /* select the active item */ - if (G_LIKELY (path != NULL)) + if (G_LIKELY (create_store)) { - gtk_tree_view_set_cursor (GTK_TREE_VIEW (treeview), path, NULL, FALSE); - gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (treeview), path, NULL, FALSE, 0.5, 0.0); - gtk_tree_path_free (path); + /* set the treeview model */ + gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store)); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), COLUMN_DEVICE_XID, GTK_SORT_ASCENDING); + g_object_unref (G_OBJECT (store)); + + /* icon renderer */ + renderer = gtk_cell_renderer_pixbuf_new (); + column = gtk_tree_view_column_new_with_attributes ("", renderer, "icon-name", COLUMN_DEVICE_ICON, NULL); + g_object_set (G_OBJECT (renderer), "stock-size", GTK_ICON_SIZE_DND, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + + /* text renderer */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("", renderer, "text", COLUMN_DEVICE_DISPLAY_NAME, NULL); + g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + + /* setup tree selection */ + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (mouse_settings_device_selection_changed), gxml); } - /* sort the tree, after setting the active item */ - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store), COLUMN_THEME_REAL_NAME, - mouse_settings_themes_sort_func, NULL, NULL); - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), COLUMN_THEME_REAL_NAME, - GTK_SORT_ASCENDING); + /* select the first mouse in the tree */ + path = gtk_tree_path_new_first (); + gtk_tree_selection_select_path (selection, path); + gtk_tree_path_free (path); + + /* unlock */ + locked--; +} + + - /* connect xfconf properties */ - widget = glade_xml_get_widget (gxml, "button_right_handed"); - xfconf_g_property_bind(xsettings_channel, "/Mouse/RightHanded", G_TYPE_INT, G_OBJECT (widget), "active"); +static gboolean +mouse_settings_device_update_sliders (gpointer user_data) +{ + GladeXML *gxml = GLADE_XML (user_data); + GtkWidget *treeview; + GtkWidget *button; + + GDK_THREADS_ENTER (); + + /* get the treeview */ + treeview = glade_xml_get_widget (gxml, "mouse-devices-treeview"); - adjustment = gtk_range_get_adjustment (GTK_RANGE (glade_xml_get_widget (gxml, "mouse_motion_acceleration"))); - xfconf_g_property_bind(xsettings_channel, "/Mouse/Acceleration", G_TYPE_INT, G_OBJECT (adjustment), "value"); + /* update */ + mouse_settings_device_selection_changed (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), gxml); - adjustment = gtk_range_get_adjustment (GTK_RANGE (glade_xml_get_widget (gxml, "mouse_motion_threshold"))); - xfconf_g_property_bind(xsettings_channel, "/Mouse/Threshold", G_TYPE_INT, G_OBJECT (adjustment), "value"); + /* make the button sensitive again */ + button = glade_xml_get_widget (gxml, "mouse-reset"); + gtk_widget_set_sensitive (button, TRUE); - adjustment = gtk_range_get_adjustment (GTK_RANGE (glade_xml_get_widget (gxml, "mouse_dnd_threshold"))); - xfconf_g_property_bind(xsettings_channel, "/Net/DndDragThreshold", G_TYPE_INT, G_OBJECT (adjustment), "value"); + GDK_THREADS_LEAVE (); - adjustment = gtk_range_get_adjustment (GTK_RANGE (glade_xml_get_widget (gxml, "mouse_double_click_speed"))); - xfconf_g_property_bind(xsettings_channel, "/Net/DoubleClickTime", G_TYPE_INT, G_OBJECT (adjustment), "value"); + return FALSE; +} - widget = glade_xml_get_widget (gxml, "spin_cursor_size"); - xfconf_g_property_bind (xsettings_channel, "/Gtk/CursorThemeSize", G_TYPE_INT, G_OBJECT (widget), "value"); - /* show all the dialog widgets */ - dialog = glade_xml_get_widget (gxml, "mouse-settings-dialog"); - gtk_widget_show_all (GTK_DIALOG (dialog)->vbox); - return dialog; +static void +mouse_settings_device_list_changed_timeout_destroyed (gpointer user_data) +{ + /* reset the timeout id */ + timeout_id = 0; +} + + + +#ifdef HAVE_HAL +static gboolean +mouse_settings_device_list_changed_timeout (gpointer user_data) +{ + GladeXML *gxml = GLADE_XML (user_data); + + GDK_THREADS_ENTER (); + + /* update the list */ + mouse_settings_device_populate_store (gxml, FALSE); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + + + +static void +mouse_settings_device_list_changed (LibHalContext *context, + const gchar *udi) +{ + GladeXML *gxml; + + /* queue a new timeout if none is set */ + if (timeout_id == 0) + { + /* get the user data */ + gxml = libhal_ctx_get_user_data (context); + + /* update the dialog in 1 second */ + timeout_id = g_timeout_add_full (G_PRIORITY_LOW, 1000, mouse_settings_device_list_changed_timeout, + gxml, mouse_settings_device_list_changed_timeout_destroyed); + } +} +#endif /* !HAVE_HAL */ + + +static void +mouse_settings_device_reset (GtkWidget *button, + GladeXML *gxml) +{ + GtkWidget *treeview; + GtkTreeSelection *selection; + gchar *name, *property_name; + gboolean has_selection; + GtkTreeModel *model; + GtkTreeIter iter; + + /* leave when locked */ + if (locked > 0) + return; + + /* get the treeview */ + treeview = glade_xml_get_widget (gxml, "mouse-devices-treeview"); + + /* get the selection */ + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); + + has_selection = gtk_tree_selection_get_selected (selection, &model, &iter); + if (G_LIKELY (has_selection)) + { + /* get device id and number of buttons */ + gtk_tree_model_get (model, &iter, COLUMN_DEVICE_NAME, &name, -1); + + if (G_LIKELY (name && timeout_id == 0)) + { + /* make the button insensitive */ + gtk_widget_set_sensitive (button, FALSE); + + /* set the threshold to -1 */ + property_name = g_strdup_printf ("/Pointers/%s/Threshold", name); + xfconf_channel_set_int (xdevices_channel, property_name, -1); + g_free (property_name); + + /* set the acceleration to -1 */ + property_name = g_strdup_printf ("/Pointers/%s/Acceleration", name); + xfconf_channel_set_double (xdevices_channel, property_name, -1.00); + g_free (property_name); + + /* update the sliders in 500ms */ + timeout_id = g_timeout_add_full (G_PRIORITY_LOW, 500, mouse_settings_device_update_sliders, + gxml, mouse_settings_device_list_changed_timeout_destroyed); + + + } + + /* cleanup */ + g_free (name); + } } @@ -556,9 +1045,16 @@ mouse_settings_dialog_new_from_xml (GladeXML *gxml) gint main(gint argc, gchar **argv) { - GtkWidget *dialog; - GladeXML *gxml; - GError *error = NULL; + GtkWidget *dialog; + GladeXML *gxml; + GError *error = NULL; + GtkAdjustment *adjustment; + GtkWidget *widget; +#ifdef HAVE_HAL + DBusConnection *connection; + LibHalContext *context = NULL; + DBusError derror; +#endif /* !HAVE_HAL */ /* setup translation domain */ xfce_textdomain (GETTEXT_PACKAGE, LOCALEDIR, "UTF-8"); @@ -595,31 +1091,161 @@ main(gint argc, gchar **argv) } /* initialize xfconf */ - xfconf_init (NULL); + if (G_UNLIKELY (!xfconf_init (&error))) + { + /* print error and leave */ + g_critical ("Failed to connect to Xfconf daemon: %s", error->message); + g_error_free (error); + + return EXIT_FAILURE; + } - /* open the xsettings channel */ + /* open the xsettings and xdevices channel */ xsettings_channel = xfconf_channel_new ("xsettings"); - if (G_LIKELY (xsettings_channel)) + xdevices_channel = xfconf_channel_new ("xdevices"); + + if (G_LIKELY (xdevices_channel && xsettings_channel)) { - /* load the dialog glade xml */ + /* load the glade xml file */ gxml = glade_xml_new_from_buffer (mouse_dialog_glade, mouse_dialog_glade_length, NULL, NULL); if (G_LIKELY (gxml)) { - /* build the dialog */ - dialog = mouse_settings_dialog_new_from_xml (gxml); + /* lock */ + locked++; + + /* set the working display for this instance */ + display = gdk_display_get_default (); - /* run the dialog */ + /* populate the devices treeview */ + mouse_settings_device_populate_store (gxml, TRUE); + + /* connect signals */ + widget = glade_xml_get_widget (gxml, "mouse-acceleration-scale"); + g_signal_connect_swapped (G_OBJECT (widget), "value-changed", G_CALLBACK (mouse_settings_device_save), gxml); + + widget = glade_xml_get_widget (gxml, "mouse-threshold-scale"); + g_signal_connect_swapped (G_OBJECT (widget), "value-changed", G_CALLBACK (mouse_settings_device_save), gxml); + + widget = glade_xml_get_widget (gxml, "mouse-left-handed"); + g_signal_connect_swapped (G_OBJECT (widget), "toggled", G_CALLBACK (mouse_settings_device_save), gxml); + + widget = glade_xml_get_widget (gxml, "mouse-right-handed"); + g_signal_connect_swapped (G_OBJECT (widget), "toggled", G_CALLBACK (mouse_settings_device_save), gxml); + + widget = glade_xml_get_widget (gxml, "mouse-reverse-scrolling"); + g_signal_connect_swapped (G_OBJECT (widget), "toggled", G_CALLBACK (mouse_settings_device_save), gxml); + + widget = glade_xml_get_widget (gxml, "mouse-reset"); + g_signal_connect (G_OBJECT (widget), "clicked", G_CALLBACK (mouse_settings_device_reset), gxml); + +#ifdef HAVE_XCURSOR + /* populate the themes treeview */ + mouse_settings_themes_populate_store (gxml); + + /* connect the cursor size in the cursor tab */ + widget = glade_xml_get_widget (gxml, "mouse-cursor-size"); + xfconf_g_property_bind (xsettings_channel, "/Gtk/CursorThemeSize", G_TYPE_INT, G_OBJECT (widget), "value"); +#else + /* hide the themes tab */ + widget = glade_xml_get_widget (gxml, "mouse-themes-hbox"); + gtk_widget_hide (widget); +#endif /* !HAVE_XCURSOR */ + + /* connect sliders in the gtk tab */ + adjustment = gtk_range_get_adjustment (GTK_RANGE (glade_xml_get_widget (gxml, "mouse-dnd-threshold"))); + xfconf_g_property_bind (xsettings_channel, "/Net/DndDragThreshold", G_TYPE_INT, G_OBJECT (adjustment), "value"); + + adjustment = gtk_range_get_adjustment (GTK_RANGE (glade_xml_get_widget (gxml, "mouse-double-click-time"))); + xfconf_g_property_bind (xsettings_channel, "/Net/DoubleClickTime", G_TYPE_INT, G_OBJECT (adjustment), "value"); + + adjustment = gtk_range_get_adjustment (GTK_RANGE (glade_xml_get_widget (gxml, "mouse-double-click-distance"))); + xfconf_g_property_bind (xsettings_channel, "/Net/DoubleClickDistance", G_TYPE_INT, G_OBJECT (adjustment), "value"); + +#ifdef HAVE_HAL + /* initialize the dbus error variable */ + dbus_error_init (&derror); + + /* connect to the dbus system bus */ + connection = dbus_bus_get (DBUS_BUS_SYSTEM, &derror); + if (G_LIKELY (connection)) + { + /* connect dbus to the main loop */ + dbus_connection_setup_with_g_main (connection, NULL); + + /* create hal context */ + context = libhal_ctx_new (); + if (G_LIKELY (context)) + { + /* set user data for the callbacks */ + libhal_ctx_set_user_data (context, gxml); + + /* set the dbus connection */ + if (G_LIKELY (libhal_ctx_set_dbus_connection (context, connection))) + { + /* connect to hal */ + if (G_LIKELY (libhal_ctx_init (context, &derror))) + { + /* add callbacks for device changes */ + libhal_ctx_set_device_added (context, mouse_settings_device_list_changed); + libhal_ctx_set_device_removed (context, mouse_settings_device_list_changed); + } + else + { + /* print warning */ + g_warning ("Failed to connect to hald: %s", derror.message); + + /* cleanup */ + LIBHAL_FREE_DBUS_ERROR (&derror); + } + } + } + } + else + { + /* print warning */ + g_warning ("Failed to connect to DBus: %s", derror.message); + + /* cleanup */ + LIBHAL_FREE_DBUS_ERROR (&derror); + } +#endif /* !HAVE_HAL */ + + /* gtk the dialog */ + dialog = glade_xml_get_widget (gxml, "mouse-dialog"); + + /* unlock */ + locked--; + + /* show the dialog */ gtk_dialog_run (GTK_DIALOG (dialog)); - /* release the glade xml */ - g_object_unref (G_OBJECT (gxml)); +#ifdef HAVE_HAL + /* close the hal connection */ + if (G_LIKELY (context)) + { + libhal_ctx_shutdown (context, NULL); + libhal_ctx_free (context); + } + + /* close the dbus connection */ + if (G_LIKELY (connection)) + dbus_connection_unref (connection); + + /* stop any running sources */ + if (G_UNLIKELY (timeout_id != 0)) + g_source_remove (timeout_id); +#endif /* !HAVE_HAL */ /* destroy the dialog */ - gtk_widget_destroy (dialog); + gtk_widget_destroy (GTK_WIDGET (dialog)); + + /* release the glade xml */ + g_object_unref (G_OBJECT (gxml)); } - /* release the channel */ + /* release the channels */ g_object_unref (G_OBJECT (xsettings_channel)); + g_object_unref (G_OBJECT (xdevices_channel)); } /* shutdown xfconf */ @@ -627,4 +1253,3 @@ main(gint argc, gchar **argv) return EXIT_SUCCESS; } - diff --git a/dialogs/mouse-settings/mouse-dialog.glade b/dialogs/mouse-settings/mouse-dialog.glade index 345cd0f9..481104f2 100644 --- a/dialogs/mouse-settings/mouse-dialog.glade +++ b/dialogs/mouse-settings/mouse-dialog.glade @@ -1,70 +1,105 @@ - + - - Xfce4 Mouse Settings + + Xfce Mouse Settings GTK_WIN_POS_CENTER_ON_PARENT - 400 preferences-desktop-peripherals GDK_WINDOW_TYPE_HINT_DIALOG False + Configure the look and feeling of the pointer devices True 2 - + True + True 6 - 6 - + True - True + 6 + 12 - + True - 12 - 6 + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + False + + + + + + + True + 12 - + True 0 GTK_SHADOW_NONE True - 6 - 12 + 6 + 18 + 6 - + True - 6 12 - True - + True - True - _Left handed - True - 0 - True - True + 2 + + + True + True + _Right handed + True + 0 + True + True + + + + + True + True + _Left handed + True + 0 + True + True + mouse-right-handed + + + 1 + + - + True True - _Right handed + When enabled, the scroll wheel will work in the opposite direction + Re_verse scroll direction True 0 - True True - button_left_handed 1 @@ -75,9 +110,9 @@ - + True - <b>Button Settings</b> + <b>Button Order</b> True @@ -90,134 +125,99 @@ - + True 0 GTK_SHADOW_NONE True - 6 - 12 + 6 + 18 + 6 - + True - 6 - 6 - - - True - 0 - _Acceleration: - True - mouse_motion_acceleration - - + 12 - + True 2 - + True - <small><i>Slow</i></small> - True + 0 + _Acceleration: + True + mouse-acceleration-scale - - False - False - - + True True The pointer will go 'acceleration' times as fast when it travels more than 'threshold' pixels in a short time - 1 1 30 1 5 0 - 0 - False + GTK_UPDATE_DELAYED + 2 0.10000000000000001 10 0.10000000000000001 1 1 + GTK_POS_RIGHT 1 - - - True - <small><i>Fast</i></small> - True - - - False - False - 2 - - - - - False - False - 1 - - - - - True - 0 - _Threshold: - True - mouse_motion_threshold - - 2 - - + True 2 - + True - <small><i>Low</i></small> - True + 0 + _Threshold: + True + mouse-threshold-scale - - False - False - - + True True The number of pixels the pointer has to moved in a short time before the acceleration starts - 1 1 20 1 5 0 - 0 + GTK_UPDATE_DELAYED + 4 1 30 1 5 5 0 - False + GTK_POS_RIGHT 1 + + + 1 + + + + + True + 0 + 0 - + True - <small><i>High</i></small> - True + True + True + Set the acceleration and threshold for the selected device to the default values + Re_set to default + True + 0 - - False - False - 2 - - False - False - 3 + 2 @@ -225,9 +225,9 @@ - + True - <b>Motion Settings</b> + <b>Feedback</b> True @@ -240,323 +240,308 @@ 1 + + + 1 + + + + + + + True + Devices + + + tab + False + + + + + True + 6 + 12 + + + True + 0 + GTK_SHADOW_NONE + + + True + 6 + 18 + 6 + + + True + 2 + + + True + 0 + Thr_eshold: + True + mouse-dnd-threshold + + + + + True + True + The number of pixels the pointer can move before dragging + GTK_UPDATE_DELAYED + 8 1 50 1 10 10 + 0 + GTK_POS_RIGHT + + + 1 + + + + + + + + + True + <b>Drag and Drop</b> + True + + + label_item + + + + + False + + + + + True + 0 + GTK_SHADOW_NONE - + True - 0 - GTK_SHADOW_NONE + 6 + 18 + 6 - + True - 6 - 12 + 12 - + True - 6 - 6 + 2 - + True 0 - Thr_eshold: + Ti_me: True - mouse_dnd_threshold + mouse-double-click-time - + True - 2 - - - True - <small><i>Low</i></small> - True - - - False - False - - - - - True - True - The number of pixels the pointer can move before dragging - 1 1 50 1 5 0 - 0 - False - - - 1 - - - - - True - <small><i>High</i></small> - True - - - False - False - 2 - - + True + The maximum time allowed between two clicks, in milliseconds, for them to be considered a double click + GTK_UPDATE_DELAYED + 250 100 2000 10 100 100 + 0 + GTK_POS_RIGHT - False - False 1 - - - - - True - <b>Drag and Drop</b> - True - - - label_item - - - - - False - 2 - - - - - True - 0 - GTK_SHADOW_NONE - - - True - 12 - + True - 6 - 6 + 2 - + True 0 - _Speed: + _Distance: True - mouse_double_click_speed + mouse-double-click-distance - + True - 2 - - - True - <small><i>Slow</i></small> - True - - - False - False - - - - - True - True - The maximum time allowed between two clicks for them to be considered a double click - 100 100 2000 10 100 0 - 0 - False - - - 1 - - - - - True - <small><i>Fast</i></small> - True - - - False - False - 2 - - + True + The maximum distance allowed between two clicks, in pixels, for them to be considered a double click + GTK_UPDATE_DELAYED + 4 0 20 1 5 5 + 0 + GTK_POS_RIGHT - False - False 1 + + 1 + - - - True - <b>Double Click</b> - True - - - label_item - - + + + + + True + <b>Double Click</b> + True - False - 3 + label_item - - - - True - Behaviour - - tab - False + False + 1 + + + 1 + + + + + True + Behaviour + + + tab + 1 + False + + + + + True + 6 + 12 - + True - 12 - 12 + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_ETCHED_IN - - 100 + True True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - - - True - True - False - 0 - - + False + False + + + + + True + 12 - + True - 6 + 0 + GTK_SHADOW_NONE - + True - 0 - GTK_SHADOW_NONE + 6 + 18 + 6 - + True - 6 - 12 + 12 - + True - 6 - 0 - GTK_SHADOW_NONE - 0 - - - True - 12 - - - True - Cursor _size: - True - spin_cursor_size - - - - - True - True - False - 2 - 24 16 48 1 10 10 - True - - - 1 - - - - + Cursor _Size: + True + mouse-cursor-size + + False + + + + + True + True + 24 16 48 1 11 10 + True + True + + + False + 1 + - - - True - <b>Size</b> - True - - - label_item - - + + + + + True + <b>Size</b> + True - False + label_item + + + False + + + + + True + 0 + GTK_SHADOW_NONE - + True - 0 - GTK_SHADOW_NONE + 6 + 18 + 6 - + True - 6 - 12 - - - True - 0 - gtk-missing-image - 5 - - - - - - - True - <b>Preview</b> - True + gtk-missing-image - - label_item - + + + + True + <b>Preview</b> + True + - False - 1 + label_item @@ -567,23 +552,24 @@ + False 1 - - - True - Cursor - - - tab - 1 - False - - - 1 + 2 + + + + + True + Theme + + + tab + 2 + False @@ -594,19 +580,9 @@ True - GTK_BUTTONBOX_EDGE + GTK_BUTTONBOX_END - True - True - True - gtk-help - True - 0 - - - - True True True @@ -614,9 +590,6 @@ True 0 - - 1 - diff --git a/xfce4-settings-helper/Makefile.am b/xfce4-settings-helper/Makefile.am index 0bd4ed43..e8fedc4e 100644 --- a/xfce4-settings-helper/Makefile.am +++ b/xfce4-settings-helper/Makefile.am @@ -1,28 +1,69 @@ -bin_PROGRAMS = xfce4-settings-helper - -xfce4_settings_helper_SOURCES = \ - main.c \ - accessx.c accessx.h \ - xkb.c xkb.h - -xfce4_settings_helper_CFLAGS = \ - $(GTK_CFLAGS) \ - $(GLIB_CFLAGS) \ - $(DBUS_GLIB_CFLAGS) \ - $(LIBNOTIFY_CFLAGS) \ - $(XFCONF_CFLAGS) \ - $(LIBXFCE4UTIL_CFLAGS) \ - -DDATADIR=\"$(datadir)\" \ - -DSRCDIR=\"$(top_srcdir)\" \ - -DLOCALEDIR=\"$(localedir)\" - -xfce4_settings_helper_LDADD = \ - $(GTK_LIBS) \ - $(GLIB_LIBS) \ - $(DBUS_GLIB_LIBS) \ - $(LIBNOTIFY_LIBS) \ - $(XFCONF_LIBS) \ - $(LIBXFCE4UTIL_LIBS) - -INCLUDES = \ - -I$(top_srcdir) +# $Id$ + +INCLUDES = \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -DBINDIR=\"$(bindir)\" \ + -DDATADIR=\"$(datadir)\" \ + -DG_LOG_DOMAIN=\"Xfce4-settings-helper\" \ + -DLIBDIR=\"$(libdir)\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DPACKAGE_LOCALE_DIR=\"$(localedir)\" \ + $(PLATFORM_CPPFLAGS) + +bin_PROGRAMS = \ + xfce4-settings-helper + +xfce4_settings_helper_SOURCES = \ + main.c \ + accessx.c \ + accessx.h \ + xkb.c \ + xkb.h \ + pointers.c \ + pointers.h + +xfce4_settings_helper_CFLAGS = \ + $(GTK_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(GTHREAD_CFLAGS) \ + $(DBUS_GLIB_CFLAGS) \ + $(XFCONF_CFLAGS) \ + $(LIBXFCE4UTIL_CFLAGS) \ + $(XI_CFLAGS) \ + $(LIBX11_CFLAGS) + +xfce4_settings_helper_LDFLAGS = \ + -no-undefined \ + $(PLATFORM_LDFLAGS) + +xfce4_settings_helper_LDADD = \ + $(GTK_LIBS) \ + $(GLIB_LIBS) \ + $(GTHREAD_LIBS) \ + $(DBUS_GLIB_LIBS) \ + $(XFCONF_LIBS) \ + $(LIBXFCE4UTIL_LIBS) \ + $(XI_LIBS) \ + $(LIBX11_LIBS) + +if HAVE_LIBNOTIFY +xfce4_settings_helper_CFLAGS += \ + $(LIBNOTIFY_CFLAGS) + +xfce4_settings_helper_LDADD += \ + $(LIBNOTIFY_LIBS) +endif + +if HAVE_HAL +xfce4_settings_helper_CFLAGS += \ + $(HAL_CFLAGS) + +xfce4_settings_helper_LDADD += \ + $(HAL_LIBS) +endif + +clean-local: + rm -f *.core core core.* + +# vi:set ts=8 sw=8 noet ai nocindent syntax=automake: diff --git a/xfce4-settings-helper/accessx.c b/xfce4-settings-helper/accessx.c index bec89091..a71bf358 100644 --- a/xfce4-settings-helper/accessx.c +++ b/xfce4-settings-helper/accessx.c @@ -28,236 +28,355 @@ #endif #include - #include -#ifdef HAVE_XF86MISC -#include -#endif - #include - #include #include - -#include #include +#include + +#ifdef HAVE_LIBNOTIFY #include +#endif /* !HAVE_LIBNOTIFY */ + +#include -#include "xkb.h" -static NotifyNotification *accessx_notification = NULL; -static XfconfChannel *accessx_channel; +static void xfce_accessx_helper_class_init (XfceAccessxHelperClass *klass); +static void xfce_accessx_helper_init (XfceAccessxHelper *helper); +static void xfce_accessx_helper_finalize (GObject *object); +static void xfce_accessx_helper_set_xkb (XfceAccessxHelper *helper); +static void xfce_accessx_helper_channel_property_changed (XfconfChannel *channel, + const gchar *property_name, + const GValue *value, + XfceAccessxHelper *helper); +#ifdef HAVE_LIBNOTIFY +static GdkFilterReturn xfce_accessx_helper_event_filter (GdkXEvent *xevent, + GdkEvent *gdk_event, + gpointer user_data); +static void xfce_accessx_helper_notification_closed (NotifyNotification *notification, + XfceAccessxHelper *helper); +static void xfce_accessx_helper_notification_show (XfceAccessxHelper *helper, + const gchar *summary, + const gchar *body); +#endif /* !HAVE_LIBNOTIFY */ + + + +struct _XfceAccessxHelperClass +{ + GObjectClass __parent__; +}; + +struct _XfceAccessxHelper +{ + GObject __parent__; + + /* xfconf channel */ + XfconfChannel *channel; + +#ifdef HAVE_LIBNOTIFY + NotifyNotification *notification; +#endif /* !HAVE_LIBNOTIFY */ +}; + + + +G_DEFINE_TYPE (XfceAccessxHelper, xfce_accessx_helper, G_TYPE_OBJECT); + -static gboolean accessx_initialized = FALSE; static void -toggle_accessx (XfconfChannel *channel); +xfce_accessx_helper_class_init (XfceAccessxHelperClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = xfce_accessx_helper_finalize; +} + -GdkFilterReturn -accessx_event_filter (GdkXEvent *xevent, - GdkEvent *g_event, - gpointer data) + +static void +xfce_accessx_helper_init (XfceAccessxHelper *helper) { - XkbEvent *event = xevent; + gint dummy; - switch(event->any.xkb_type) + helper->channel = NULL; +#ifdef HAVE_LIBNOTIFY + helper->notification = NULL; +#endif /* !HAVE_LIBNOTIFY */ + + if (XkbQueryExtension (GDK_DISPLAY (), &dummy, &dummy, &dummy, &dummy, &dummy)) { - case XkbBellNotify: - break; - case XkbControlsNotify: - { - if(event->ctrls.enabled_ctrl_changes & XkbStickyKeysMask) - { - if(event->ctrls.enabled_ctrls & XkbStickyKeysMask) - { - notify_notification_update(accessx_notification, - _("Sticky keys"), - _("Sticky keys are enabled"), - "keyboard"); - } - else - { - notify_notification_update(accessx_notification, - _("Sticky keys"), - _("Sticky keys are disabled"), - "keyboard"); - } - - notify_notification_show(accessx_notification, NULL); - } - - if(event->ctrls.enabled_ctrl_changes & XkbSlowKeysMask) - { - if(event->ctrls.enabled_ctrls & XkbSlowKeysMask) - { - notify_notification_update(accessx_notification, - _("Slow keys"), - _("Slow keys are enabled"), - "keyboard"); - } - else - { - notify_notification_update(accessx_notification, - _("Slow keys"), - _("Slow keys are disabled"), - "keyboard"); - } - - notify_notification_show(accessx_notification, NULL); - } - - if(event->ctrls.enabled_ctrl_changes & XkbBounceKeysMask) - { - if(event->ctrls.enabled_ctrls & XkbBounceKeysMask) - { - notify_notification_update(accessx_notification, - _("Bounce keys"), - _("Bounce keys are enabled"), - "keyboard"); - } - else - { - notify_notification_update(accessx_notification, - _("Bounce keys"), - _("Bounce keys are disabled"), - "keyboard"); - } - - notify_notification_show(accessx_notification, NULL); - } - } - break; - default: - break; + /* open the channel */ + helper->channel = xfconf_channel_new ("accessx"); + + /* monitor channel changes */ + g_signal_connect (G_OBJECT (helper->channel), "property-changed", G_CALLBACK (xfce_accessx_helper_channel_property_changed), helper); + + /* restore the xbd configuration */ + xfce_accessx_helper_set_xkb (helper); + +#ifdef HAVE_LIBNOTIFY + /* setup a connection with the notification daemon */ + if (!notify_init ("xfce4-settings-helper")) + g_critical ("Failed to connect to the notification daemon."); + + /* add event filter */ + XkbSelectEvents (GDK_DISPLAY (), XkbUseCoreKbd, XkbControlsNotifyMask, XkbControlsNotifyMask); + + /* monitor all window events */ + gdk_window_add_filter (NULL, xfce_accessx_helper_event_filter, helper); +#endif /* !HAVE_LIBNOTIFY */ + } + else + { + /* warning */ + g_critical ("Failed to initialize the Xkb extension."); } - return GDK_FILTER_CONTINUE; } + + static void -cb_accessx_channel_property_changed(XfconfChannel *channel, const gchar *name, const GValue *value, gpointer user_data) +xfce_accessx_helper_finalize (GObject *object) { - toggle_accessx(channel); + XfceAccessxHelper *helper = XFCE_ACCESSX_HELPER (object); + +#ifdef HAVE_LIBNOTIFY + /* close an opened notification */ + if (G_UNLIKELY (helper->notification)) + notify_notification_close (helper->notification, NULL); +#endif /* !HAVE_LIBNOTIFY */ + + /* release the channel */ + if (G_LIKELY (helper->channel)) + g_object_unref (G_OBJECT (helper->channel)); + + (*G_OBJECT_CLASS (xfce_accessx_helper_parent_class)->finalize) (object); } + + static void -toggle_accessx (XfconfChannel *channel) +xfce_accessx_helper_set_xkb (XfceAccessxHelper *helper) { - gboolean mouse_keys = xfconf_channel_get_bool (channel, "/AccessX/MouseKeys", FALSE); - gint mouse_keys_delay = xfconf_channel_get_int (channel, "/AccessX/MouseKeys/Delay", 100); - gint mouse_keys_interval = xfconf_channel_get_int (channel, "/AccessX/MouseKeys/Interval", 100); - gint mouse_keys_ttm = xfconf_channel_get_int (channel, "/AccessX/MouseKeys/TimeToMax", 100); - gint mouse_keys_max_speed = xfconf_channel_get_int (channel, "/AccessX/MouseKeys/Speed", 100); - gboolean slow_keys = xfconf_channel_get_bool (channel, "/AccessX/SlowKeys", FALSE); - gint slow_keys_delay = xfconf_channel_get_int (channel, "/AccessX/SlowKeys/Delay", 100); + XkbDescPtr xkb; - gboolean bounce_keys = xfconf_channel_get_bool (channel, "/AccessX/BounceKeys", FALSE); - gint debounce_delay = xfconf_channel_get_int (channel, "/AccessX/BounceKeys/Delay", 100); + /* allocate */ + xkb = XkbAllocKeyboard (); - gboolean sticky_keys = xfconf_channel_get_bool (channel, "/AccessX/StickyKeys", FALSE); - gboolean sticky_keys_ltl = xfconf_channel_get_bool (channel, "/AccessX/StickyKeys/LatchToLock", FALSE); - gboolean sticky_keys_tk = xfconf_channel_get_bool (channel, "/AccessX/StickyKeys/TwoKeysDisable", FALSE); - - /* assume xkb is present, this has laready been checked */ - if (TRUE) + if (G_LIKELY (xkb)) { - XkbDescPtr xkb = XkbAllocKeyboard (); - if (xkb) - { - gdk_error_trap_push (); - XkbGetControls (GDK_DISPLAY (), XkbAllControlsMask, xkb); + /* flush and avoid crashes on x errors */ + gdk_flush (); + gdk_error_trap_push (); - /* Mouse keys */ - if (mouse_keys) - { - xkb->ctrls->enabled_ctrls |= XkbMouseKeysMask; - xkb->ctrls->mk_delay = mouse_keys_delay; - xkb->ctrls->mk_interval = 1000 / mouse_keys_interval; - xkb->ctrls->mk_time_to_max = mouse_keys_ttm; - xkb->ctrls->mk_max_speed = mouse_keys_max_speed; + /* load the xkb controls into the structure */ + XkbGetControls (GDK_DISPLAY (), XkbAllControlsMask, xkb); - } - else - xkb->ctrls->enabled_ctrls &= XkbMouseKeysMask; + /* Mouse keys */ + if (xfconf_channel_get_bool (helper->channel, "/AccessX/MouseKeys", FALSE)) + { + xkb->ctrls->enabled_ctrls |= XkbMouseKeysMask; + xkb->ctrls->mk_delay = xfconf_channel_get_int (helper->channel, "/AccessX/MouseKeys/Delay", 100); + xkb->ctrls->mk_interval = 1000 / xfconf_channel_get_int (helper->channel, "/AccessX/MouseKeys/Interval", 100); + xkb->ctrls->mk_time_to_max = xfconf_channel_get_int (helper->channel, "/AccessX/MouseKeys/TimeToMax", 100); + xkb->ctrls->mk_max_speed = xfconf_channel_get_int (helper->channel, "/AccessX/MouseKeys/Speed", 100); + } + else + { + xkb->ctrls->enabled_ctrls &= ~XkbMouseKeysMask; + } - /* Slow keys */ - if(slow_keys) - { - xkb->ctrls->enabled_ctrls |= XkbSlowKeysMask; - xkb->ctrls->slow_keys_delay = slow_keys_delay; - } - else - xkb->ctrls->enabled_ctrls &= ~XkbSlowKeysMask; + /* Slow keys */ + if (xfconf_channel_get_bool (helper->channel, "/AccessX/SlowKeys", FALSE)) + { + xkb->ctrls->enabled_ctrls |= XkbSlowKeysMask; + xkb->ctrls->slow_keys_delay = xfconf_channel_get_int (helper->channel, "/AccessX/SlowKeys/Delay", 100); + } + else + { + xkb->ctrls->enabled_ctrls &= ~XkbSlowKeysMask; + } - /* Bounce keys */ - if(bounce_keys) - { - xkb->ctrls->enabled_ctrls |= XkbBounceKeysMask; - xkb->ctrls->debounce_delay = debounce_delay; - } - else - xkb->ctrls->enabled_ctrls &= ~XkbBounceKeysMask; + /* Bounce keys */ + if (xfconf_channel_get_bool (helper->channel, "/AccessX/BounceKeys", FALSE)) + { + xkb->ctrls->enabled_ctrls |= XkbBounceKeysMask; + xkb->ctrls->debounce_delay = xfconf_channel_get_int (helper->channel, "/AccessX/BounceKeys/Delay", 100); + } + else + { + xkb->ctrls->enabled_ctrls &= ~XkbBounceKeysMask; + } - /* Sticky keys */ - if(sticky_keys) - xkb->ctrls->enabled_ctrls |= XkbStickyKeysMask; - else - xkb->ctrls->enabled_ctrls &= ~XkbStickyKeysMask; + /* Sticky keys */ + if (xfconf_channel_get_bool (helper->channel, "/AccessX/StickyKeys", FALSE)) + { + xkb->ctrls->enabled_ctrls |= XkbStickyKeysMask; - if(sticky_keys_ltl) + if (xfconf_channel_get_bool (helper->channel, "/AccessX/StickyKeys/LatchToLock", FALSE)) xkb->ctrls->ax_options |= XkbAX_LatchToLockMask; else xkb->ctrls->ax_options &= ~XkbAX_LatchToLockMask; - if(sticky_keys_tk) + if (xfconf_channel_get_bool (helper->channel, "/AccessX/StickyKeys/TwoKeysDisable", FALSE)) xkb->ctrls->ax_options |= XkbAX_TwoKeysMask; else xkb->ctrls->ax_options &= ~XkbAX_TwoKeysMask; - - /* If any option is set, enable AccessXKeys, otherwise: don't */ - if(sticky_keys || bounce_keys || slow_keys) - xkb->ctrls->enabled_ctrls |= XkbAccessXKeysMask; - else - xkb->ctrls->enabled_ctrls &= ~XkbAccessXKeysMask; - - XkbSetControls (GDK_DISPLAY (), XkbControlsEnabledMask | XkbStickyKeysMask | XkbBounceKeysMask | XkbSlowKeysMask, xkb); - XFree (xkb); - gdk_flush (); - gdk_error_trap_pop (); } else { - g_warning ("XkbAllocKeyboard() returned null pointer"); + xkb->ctrls->enabled_ctrls &= ~XkbStickyKeysMask; } + + /* If any option is set, enable AccessXKeys, otherwise: don't */ + if ((xkb->ctrls->enabled_ctrls & (XkbStickyKeysMask | XkbBounceKeysMask | XkbSlowKeysMask)) != 0) + xkb->ctrls->enabled_ctrls |= XkbAccessXKeysMask; + else + xkb->ctrls->enabled_ctrls &= ~XkbAccessXKeysMask; + + /* set the new controls */ + XkbSetControls (GDK_DISPLAY (), XkbControlsEnabledMask | XkbStickyKeysMask | XkbBounceKeysMask | XkbSlowKeysMask | XkbMouseKeysMask, xkb); + + /* free the structure */ + XFree (xkb); + + /* flush errors and pop trap */ + gdk_flush (); + gdk_error_trap_pop (); } + else + { + /* warning */ + g_error ("XkbAllocKeyboard() returned a null pointer"); + } +} + + + +static void +xfce_accessx_helper_channel_property_changed (XfconfChannel *channel, + const gchar *property_name, + const GValue *value, + XfceAccessxHelper *helper) +{ + g_return_if_fail (helper->channel == channel); + + /* update the xkb settings */ + xfce_accessx_helper_set_xkb (helper); } -void -accessx_notification_init (XfconfChannel *channel) + +#ifdef HAVE_LIBNOTIFY +static GdkFilterReturn +xfce_accessx_helper_event_filter (GdkXEvent *xevent, + GdkEvent *gdk_event, + gpointer user_data) { - g_return_if_fail (accessx_initialized == FALSE); + XkbEvent *event = xevent; + XfceAccessxHelper *helper = XFCE_ACCESSX_HELPER (user_data); + const gchar *body; + + switch (event->any.xkb_type) + { + case XkbControlsNotify: + if ((event->ctrls.enabled_ctrl_changes & XkbStickyKeysMask) != 0) + { + if ((event->ctrls.enabled_ctrls & XkbStickyKeysMask) != 0) + body = _("Sticky keys are enabled"); + else + body = _("Sticky keys are disabled"); + + xfce_accessx_helper_notification_show (helper, _("Sticky keys"), body); + } + else if ((event->ctrls.enabled_ctrl_changes & XkbSlowKeysMask) != 0) + { + if ((event->ctrls.enabled_ctrls & XkbSlowKeysMask) != 0) + body = _("Slow keys are enabled"); + else + body = _("Slow keys are disabled"); + + xfce_accessx_helper_notification_show (helper, _("Slow keys"), body); + } + else if ((event->ctrls.enabled_ctrl_changes & XkbBounceKeysMask) != 0) + { + if ((event->ctrls.enabled_ctrls & XkbBounceKeysMask) != 0) + body = _("Bounce keys are enabled"); + else + body = _("Bounce keys are disabled"); - accessx_channel = channel; + xfce_accessx_helper_notification_show (helper, _("Bounce keys"), body); + } + + break; - g_signal_connect(G_OBJECT(channel), "property-changed", (GCallback)cb_accessx_channel_property_changed, NULL); + default: + break; + } - accessx_notification = notify_notification_new( - _("Accessibility Notification"), - _("Accessibility settings changed"), - "preferences-desktop-accessibility", - NULL); + return GDK_FILTER_CONTINUE; +} - XkbSelectEvents(gdk_display, - XkbUseCoreKbd, - XkbControlsNotifyMask, - XkbControlsNotifyMask); - gdk_window_add_filter(NULL, accessx_event_filter, NULL); - toggle_accessx (channel); +static void +xfce_accessx_helper_notification_closed (NotifyNotification *notification, + XfceAccessxHelper *helper) +{ + g_return_if_fail (helper->notification == notification); - accessx_initialized = TRUE; + /* set to null */ + helper->notification = NULL; } + + +static void +xfce_accessx_helper_notification_show (XfceAccessxHelper *helper, + const gchar *summary, + const gchar *body) +{ + /* early leave the avoid dbus errors, we already + * told we were unable to connect during init */ + if (notify_is_initted () == FALSE) + return; + + /* close the running notification */ + if (helper->notification == NULL) + { + /* create a new notification */ + helper->notification = notify_notification_new (summary, body, "keyboard", NULL); + + /* close signal */ + g_signal_connect (G_OBJECT (helper->notification), "closed", G_CALLBACK (xfce_accessx_helper_notification_closed), helper); + } + else + { + /* update the current notification */ + notify_notification_update (helper->notification, summary, body, "keyboard"); + } + + if (G_LIKELY (helper->notification)) + { + /* show the notification for (another) 2 seconds */ + notify_notification_set_timeout (helper->notification, 2000); + + /* show the notification */ + if (!notify_notification_show (helper->notification, NULL)) + { + /* show warning with the notification information */ + g_warning ("Failed to show notification: %s (%s).", summary, body); + + /* failed to show the notification */ + notify_notification_close (helper->notification, NULL); + helper->notification = NULL; + } + } +} +#endif /* !HAVE_LIBNOTIFY */ diff --git a/xfce4-settings-helper/accessx.h b/xfce4-settings-helper/accessx.h index c390778a..268de4ac 100644 --- a/xfce4-settings-helper/accessx.h +++ b/xfce4-settings-helper/accessx.h @@ -1,3 +1,4 @@ +/* $Id$ */ /* * Copyright (c) 2008 Stephan Arts * @@ -19,6 +20,19 @@ * by Olivier Fourdan. */ +#ifndef __ACCESSX_H__ +#define __ACCESSX_H__ -void -accessx_notification_init (XfconfChannel *channel); +typedef struct _XfceAccessxHelperClass XfceAccessxHelperClass; +typedef struct _XfceAccessxHelper XfceAccessxHelper; + +#define XFCE_TYPE_ACCESSX_HELPER (xfce_accessx_helper_get_type ()) +#define XFCE_ACCESSX_HELPER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), XFCE_TYPE_ACCESSX_HELPER, XfceAccessxHelper)) +#define XFCE_ACCESSX_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), XFCE_TYPE_ACCESSX_HELPER, XfceAccessxHelperClass)) +#define XFCE_IS_ACCESSX_HELPER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), XFCE_TYPE_ACCESSX_HELPER)) +#define XFCE_IS_ACCESSX_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), XFCE_TYPE_ACCESSX_HELPER)) +#define XFCE_ACCESSX_HELPER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), XFCE_TYPE_ACCESSX_HELPER, XfceAccessxHelperClass)) + +GType xfce_accessx_helper_get_type (void) G_GNUC_CONST; + +#endif /* !__ACCESSX_H__ */ diff --git a/xfce4-settings-helper/main.c b/xfce4-settings-helper/main.c index e7708982..611f4a13 100644 --- a/xfce4-settings-helper/main.c +++ b/xfce4-settings-helper/main.c @@ -1,5 +1,7 @@ +/* $Id$ */ /* * Copyright (c) 2008 Stephan Arts + * Copyright (c) 2008 Nick Schermer * * 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 @@ -20,100 +22,123 @@ #include #endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif #ifdef HAVE_UNISTD_H #include #endif -#include - #include - #include -#include -#include #include -#include - -#include "xkb.h" -#include "accessx.h" +#include -#define XF_DEBUG(str) \ - if (debug) g_print (str) +#include +#include +#include -static gboolean version = FALSE; -static gboolean debug = FALSE; -static GOptionEntry entries[] = +static gboolean opt_version = FALSE; +static gboolean opt_debug = FALSE; +static GOptionEntry option_entries[] = { - { "version", 'v', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &version, - N_("Version information"), - NULL - }, - { "debug", 'd', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &debug, - N_("Start in debug mode (don't fork to the background)"), - NULL - }, + { "version", 'v', 0, G_OPTION_ARG_NONE, &opt_version, N_("Version information"), NULL }, + { "debug", 'd', 0, G_OPTION_ARG_NONE, &opt_debug, N_("Start in debug mode (don't fork to the background)"), NULL }, { NULL } }; -int -main(int argc, char **argv) + +gint +main (gint argc, gchar **argv) { - GError *cli_error = NULL; - XfconfChannel *accessx_channel, *xkb_channel; + GError *error = NULL; + GObject *pointer_helper; + GObject *xkb_helper; + GObject *accessx_helper; + pid_t pid; - xfce_textdomain(GETTEXT_PACKAGE, LOCALEDIR, "UTF-8"); + /* setup translation domain */ + xfce_textdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR, "UTF-8"); - if(!gtk_init_with_args(&argc, &argv, _(""), entries, PACKAGE, &cli_error)) - { - if (cli_error != NULL) - { - g_print (_("%s: %s\nTry %s --help to see a full list of available command line options.\n"), PACKAGE, cli_error->message, PACKAGE_NAME); - g_error_free (cli_error); - return 1; - } - } + /* initialize the gthread system */ + if (!g_thread_supported ()) + g_thread_init (NULL); - if(version) + /* initialize gtk */ + if(!gtk_init_with_args (&argc, &argv, "", option_entries, GETTEXT_PACKAGE, &error)) { - g_print("xfce-xkbd %s\n", PACKAGE_VERSION); - return 0; - } + /* print error */ + g_error ("Failed to initialize GTK+: %s.", (error && error->message) ? error->message : "Unable to open display"); - if(!xfconf_init(&cli_error)) - { - g_printerr("Failed to connect to Xfconf daemon: %s\n", - cli_error->message); - return 1; - } - - notify_init("xfce4-settings-helper"); + /* cleanup */ + if (G_LIKELY (error)) + g_error_free (error); - xkb_channel = xfconf_channel_new("xkb"); - accessx_channel = xfconf_channel_new("accessx"); + return EXIT_FAILURE; + } + /* check if we should print version information */ + if (G_UNLIKELY (opt_version)) + { + g_print ("xfce4-settings-helper %s\n\n", PACKAGE_VERSION); + g_print ("%s\n", "Copyright (c) 2008"); + g_print ("\t%s\n\n", _("The Xfce development team. All rights reserved.")); + g_print (_("Please report bugs to <%s>."), PACKAGE_BUGREPORT); + g_print ("\n"); - if (xkb_notification_init(xkb_channel)) - accessx_notification_init(accessx_channel); + return EXIT_SUCCESS; + } - if(!debug) /* If not in debug mode, fork to background */ + /* daemonize the process when not running in debug mode */ + if (!opt_debug) { - if(!fork()) + /* try to fork the process */ + pid = fork (); + + if (G_UNLIKELY (pid == -1)) { - gtk_main(); - - xfconf_shutdown(); + /* show message and continue in normal mode */ + g_warning ("Failed to fork the process, starting in non-daemon mode"); + } + else if (pid > 0) + { + /* succesfully created a fork, leave this instance */ + return EXIT_SUCCESS; } } - else + + /* initialize xfconf */ + if (!xfconf_init (&error)) { - gtk_main(); + /* print error and exit */ + g_error ("Failed to connect to xfconf daemon: %s.", error->message); + g_error_free (error); - xfconf_shutdown(); + return EXIT_FAILURE; } - return 0; + /* create the sub daemons */ + pointer_helper = g_object_new (XFCE_TYPE_POINTERS_HELPER, NULL); + xkb_helper = g_object_new (XFCE_TYPE_XKB_HELPER, NULL); + accessx_helper = g_object_new (XFCE_TYPE_ACCESSX_HELPER, NULL); + + /* enter the main loop */ + gtk_main(); + + /* release the sub daemons */ + g_object_unref (G_OBJECT (pointer_helper)); + g_object_unref (G_OBJECT (xkb_helper)); + g_object_unref (G_OBJECT (accessx_helper)); + + /* shutdown xfconf */ + xfconf_shutdown (); + + return EXIT_SUCCESS; } diff --git a/xfce4-settings-helper/pointers.c b/xfce4-settings-helper/pointers.c new file mode 100644 index 00000000..62d2140b --- /dev/null +++ b/xfce4-settings-helper/pointers.c @@ -0,0 +1,643 @@ +/* $Id$ */ +/* + * Copyright (c) 2008 Nick Schermer + * + * 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 Library 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#include +#include + +#include +#include +#include +#include + +#ifdef HAVE_HAL +#include +#include +#include +#endif /* !HAVE_HAL */ + +#include + + + +#define MAX_DENOMINATOR (100.00) + + + +static void xfce_pointers_helper_class_init (XfcePointersHelperClass *klass); +static void xfce_pointers_helper_init (XfcePointersHelper *helper); +static void xfce_pointers_helper_finalize (GObject *object); +static void xfce_pointers_helper_change_button_mapping_swap (guchar *buttonmap, + gshort num_buttons, + gint id_1, + gint id_2, + gboolean reverse); +static void xfce_pointers_helper_change_button_mapping (XDeviceInfo *device_info, + XDevice *device, + Display *xdisplay, + gint right_handed, + gint reverse_scrolling); +static gint xfce_pointers_helper_gcd (gint num, + gint denom); +static void xfce_pointers_helper_change_feedback (XDevice *device, + Display *xdisplay, + gint threshold, + gdouble acceleration); +static gchar *xfce_pointers_helper_device_xfconf_name (const gchar *name); +static void xfce_pointers_helper_restore_devices (XfcePointersHelper *helper); +static void xfce_pointers_helper_channel_property_changed (XfconfChannel *channel, + const gchar *property_name, + const GValue *value); +#ifdef HAVE_HAL +static gboolean xfce_pointers_helper_device_added_timeout (gpointer user_data); +static void xfce_pointers_helper_device_added_timeout_destroyed (gpointer user_data); +static void xfce_pointers_helper_device_added (LibHalContext *context, + const gchar *udi); +#endif /* !HAVE_HAL */ + + + +struct _XfcePointersHelperClass +{ + GObjectClass __parent__; +}; + +struct _XfcePointersHelper +{ + GObject __parent__; + + /* xfconf channel */ + XfconfChannel *channel; + +#ifdef HAVE_HAL + /* timeout for adding hal devices */ + guint timeout_id; + + /* dbus connection */ + DBusConnection *connection; + + /* hal context */ + LibHalContext *context; +#endif /* !HAVE_HAL */ +}; + + + +G_DEFINE_TYPE (XfcePointersHelper, xfce_pointers_helper, G_TYPE_OBJECT); + + + +static void +xfce_pointers_helper_class_init (XfcePointersHelperClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = xfce_pointers_helper_finalize; +} + + + +static void +xfce_pointers_helper_init (XfcePointersHelper *helper) +{ + gint dummy; + +#ifdef HAVE_HAL + DBusError derror; + + /* initialize */ + helper->timeout_id = 0; + helper->context = NULL; + helper->connection = NULL; +#endif /* !HAVE_HAL */ + + if (XQueryExtension (GDK_DISPLAY (), "XInputExtension", &dummy, &dummy, &dummy)) + { + /* open the channel */ + helper->channel = xfconf_channel_new ("xdevices"); + + /* restore the pointer devices */ + xfce_pointers_helper_restore_devices (helper); + + /* monitor the channel */ + g_signal_connect (G_OBJECT (helper->channel), "property-changed", G_CALLBACK (xfce_pointers_helper_channel_property_changed), NULL); + +#ifdef HAVE_HAL + /* initialize the dbus error variable */ + dbus_error_init (&derror); + + /* connect to the dbus system bus */ + helper->connection = dbus_bus_get (DBUS_BUS_SYSTEM, &derror); + if (G_LIKELY (helper->connection)) + { + /* connect dbus to the main loop */ + dbus_connection_setup_with_g_main (helper->connection, NULL); + + /* create hal context */ + helper->context = libhal_ctx_new (); + if (G_LIKELY (helper->context)) + { + /* set user data for the callbacks */ + libhal_ctx_set_user_data (helper->context, helper); + + /* set the dbus connection */ + if (G_LIKELY (libhal_ctx_set_dbus_connection (helper->context, helper->connection))) + { + /* connect to hal */ + if (G_LIKELY (libhal_ctx_init (helper->context, &derror))) + { + /* add callbacks for device changes */ + libhal_ctx_set_device_added (helper->context, xfce_pointers_helper_device_added); + } + else + { + /* print warning */ + g_warning ("Failed to connect to the hal daemon: %s.", derror.message); + + /* cleanup */ + LIBHAL_FREE_DBUS_ERROR (&derror); + } + } + } + } + else + { + /* print warning */ + g_warning ("Failed to connect to DBus: %s.", derror.message); + + /* cleanup */ + LIBHAL_FREE_DBUS_ERROR (&derror); + } +#endif /* !HAVE_HAL */ + } + else + { + /* print error */ + g_critical ("Failed to query the XInput extension."); + + /* no channel */ + helper->channel = NULL; + } +} + + + +static void +xfce_pointers_helper_finalize (GObject *object) +{ + XfcePointersHelper *helper = XFCE_POINTERS_HELPER (object); + +#ifdef HAVE_HAL + if (G_LIKELY (helper->context)) + { + /* shutdown and free context */ + libhal_ctx_shutdown (helper->context, NULL); + libhal_ctx_free (helper->context); + } + + /* release the dbus connection */ + if (G_LIKELY (helper->connection)) + dbus_connection_unref (helper->connection); +#endif /* !HAVE_HAL */ + + /* release the channel */ + if (G_LIKELY (helper->channel)) + g_object_unref (G_OBJECT (helper->channel)); + + (*G_OBJECT_CLASS (xfce_pointers_helper_parent_class)->finalize) (object); +} + + + +static void +xfce_pointers_helper_change_button_mapping_swap (guchar *buttonmap, + gshort num_buttons, + gint id_1, + gint id_2, + gboolean reverse) +{ + gint n; + gint id_a; + gint id_b; + + /* figure out the position of the id_1 and id_2 buttons in the map */ + for (n = 0, id_a = id_b = -1; n < num_buttons; n++) + { + if (buttonmap[n] == id_1) + id_a = n; + else if (buttonmap[n] == id_2) + id_b = n; + } + + /* only change the map when id_a and id_b where found */ + if (G_LIKELY (id_a != -1 && id_b != -1)) + { + /* check if we need to change the buttonmap */ + if ((!reverse && (id_a < id_b)) || (reverse && (id_a > id_b))) + { + /* swap the buttons in the button map */ + buttonmap[id_a] = id_2; + buttonmap[id_b] = id_1; + } + } +} + + + +static void +xfce_pointers_helper_change_button_mapping (XDeviceInfo *device_info, + XDevice *device, + Display *xdisplay, + gint right_handed, + gint reverse_scrolling) +{ + XAnyClassPtr ptr; + gshort num_buttons; + guchar *buttonmap; + gint n; + gint right_button; + + /* get the device classes */ + ptr = device_info->inputclassinfo; + + /* search the classes for the number of buttons */ + for (n = 0, num_buttons = 0; n < device_info->num_classes; n++) + { + /* find the button class */ + if (ptr->class == ButtonClass) + { + /* get the number of buttons */ + num_buttons = ((XButtonInfoPtr) ptr)->num_buttons; + + /* done */ + break; + } + + /* advance the offset */ + ptr = (XAnyClassPtr) ((gchar *) ptr + ptr->length); + } + + if (G_LIKELY (num_buttons > 0)) + { + /* allocate the button map */ + buttonmap = g_new0 (guchar, num_buttons); + + /* get the button mapping */ + XGetDeviceButtonMapping (xdisplay, device, buttonmap, num_buttons); + + if (right_handed != -1) + { + /* get the right button number */ + right_button = num_buttons < 3 ? 2 : 3; + + /* check the buttons and swap them if needed */ + xfce_pointers_helper_change_button_mapping_swap (buttonmap, num_buttons, 1, right_button, !!right_handed); + } + + if (reverse_scrolling != -1 && num_buttons >= 5) + { + /* check the buttons and swap them if needed */ + xfce_pointers_helper_change_button_mapping_swap (buttonmap, num_buttons, 4, 5, !reverse_scrolling); + } + + /* set the new button mapping */ + XSetDeviceButtonMapping (xdisplay, device, buttonmap, num_buttons); + + /* cleanup */ + g_free (buttonmap); + } +} + + + +static gint +xfce_pointers_helper_gcd (gint num, + gint denom) +{ + /* calc the greatest common divisor using euclidean's algorithm */ + return (denom != 0 ? xfce_pointers_helper_gcd (denom, num % denom) : num); +} + + + +static void +xfce_pointers_helper_change_feedback (XDevice *device, + Display *xdisplay, + gint threshold, + gdouble acceleration) +{ + XFeedbackState *states; + gint num_feedbacks; + XPtrFeedbackControl feedback; + gint n; + gulong mask = 0; + gint num = -1, denom = -1, gcd; + + /* get the feedback states for this device */ + states = XGetFeedbackControl (xdisplay, device, &num_feedbacks); + + if (G_LIKELY (states)) + { + /* get the pointer feedback class */ + for (n = 0; n < num_feedbacks; n++) + { + /* find the pointer feedback class */ + if (states->class == PtrFeedbackClass) + { + if (acceleration > 0 || acceleration == -1) + { + if (acceleration > 0) + { + /* calculate the faction of the acceleration */ + num = acceleration * MAX_DENOMINATOR; + denom = MAX_DENOMINATOR; + gcd = xfce_pointers_helper_gcd (num, denom); + num /= gcd; + denom /= gcd; + } + + /* set the mask */ + mask |= DvAccelNum | DvAccelDenom; + } + + /* setup the mask for the threshold */ + if (threshold > 0 || threshold == -1) + mask |= DvThreshold; + + /* create a new feedback */ + feedback.class = PtrFeedbackClass; + feedback.length = sizeof (XPtrFeedbackControl); + feedback.id = states->id; + feedback.threshold = threshold; + feedback.accelNum = num; + feedback.accelDenom = denom; + + /* change feedback for this device */ + XChangeFeedbackControl (xdisplay, device, mask, (XFeedbackControl *) &feedback); + + /* done */ + break; + } + + /* advance the offset */ + states = (XFeedbackState *) ((gchar *) states + states->length); + } + + /* cleanup */ + XFreeFeedbackList (states); + } +} + + + +static gchar * +xfce_pointers_helper_device_xfconf_name (const gchar *name) +{ + GString *string; + const gchar *p; + + /* NOTE: this function exists in both the dialog and + * helper code and they have to identical! */ + + /* allocate a string */ + string = g_string_sized_new (strlen (name)); + + /* create a name with only valid chars */ + for (p = name; *p != '\0'; p++) + { + if ((*p >= 'A' && *p <= 'Z') + || (*p >= 'a' && *p <= 'z') + || (*p >= '0' && *p <= '9') + || *p == '_' || *p == '-') + g_string_append_c (string, *p); + else if (*p == ' ') + string = g_string_append_c (string, '_'); + } + + /* return the new string */ + return g_string_free (string, FALSE); +} + + + +static void +xfce_pointers_helper_restore_devices (XfcePointersHelper *helper) +{ + Display *xdisplay = GDK_DISPLAY (); + XDeviceInfo *device_list, *device_info; + XDevice *device; + gint n, ndevices; + gchar *righthanded_str; + gchar *threshold_str; + gchar *acceleration_str; + gchar *device_name; + gchar *reverse_scrolling_str; + + /* get all the registered devices */ + device_list = XListInputDevices (xdisplay, &ndevices); + + for (n = 0; n < ndevices; n++) + { + /* get the device info */ + device_info = &device_list[n]; + + /* filter out the pointer devices */ + if (device_info->use == IsXExtensionPointer) + { + /* open the device */ + device = XOpenDevice (xdisplay, device_info->id); + if (G_LIKELY (device)) + { + /* get a clean device name */ + device_name = xfce_pointers_helper_device_xfconf_name (device_info->name); + + /* create righthanded property string */ + righthanded_str = g_strdup_printf ("/Pointers/%s/RightHanded", device_name); + + /* check if we have a property for this device, else continue */ + if (xfconf_channel_has_property (helper->channel, righthanded_str)) + { + /* create property names */ + reverse_scrolling_str = g_strdup_printf ("/Pointers/%s/ReverseScrolling", device_name); + threshold_str = g_strdup_printf ("/Pointers/%s/Threshold", device_name); + acceleration_str = g_strdup_printf ("/Pointers/%s/Acceleration", device_name); + + /* restore the button mapping */ + xfce_pointers_helper_change_button_mapping (device_info, device, xdisplay, + xfconf_channel_get_bool (helper->channel, righthanded_str, TRUE) ? 1 : 0, + xfconf_channel_get_bool (helper->channel, reverse_scrolling_str, FALSE) ? 1 : 0); + + /* restore the pointer feedback */ + xfce_pointers_helper_change_feedback (device, xdisplay, + xfconf_channel_get_int (helper->channel, threshold_str, -1), + xfconf_channel_get_double (helper->channel, acceleration_str, -1.00)); + + /* cleanup */ + g_free (reverse_scrolling_str); + g_free (threshold_str); + g_free (acceleration_str); + } + + /* cleanup */ + g_free (righthanded_str); + g_free (device_name); + + /* close the device */ + XCloseDevice (xdisplay, device); + } + } + } + + /* cleanup */ + XFreeDeviceList (device_list); +} + + + +static void +xfce_pointers_helper_channel_property_changed (XfconfChannel *channel, + const gchar *property_name, + const GValue *value) +{ + Display *xdisplay = GDK_DISPLAY (); + XDeviceInfo *device_list, *device_info; + XDevice *device; + gint n, ndevices; + gchar **names; + gchar *device_name; + + /* check if this looks like a pointer property */ + if (strncmp (property_name, "/Pointers/", 10) != 0) + return; + + /* split the property name */ + names = g_strsplit (property_name + 10, "/", -1); + + /* check if splitting worked */ + if (names && g_strv_length (names) == 2) + { + /* get all the registered devices */ + device_list = XListInputDevices (xdisplay, &ndevices); + + for (n = 0; n < ndevices; n++) + { + /* get the device info */ + device_info = &device_list[n]; + + /* find the pointer device */ + if (device_info->use == IsXExtensionPointer) + { + /* create a valid xfconf device name */ + device_name = xfce_pointers_helper_device_xfconf_name (device_info->name); + + /* check if this is the device that's been changed */ + if (strcmp (names[0], device_name) == 0) + { + /* open the device */ + device = XOpenDevice (xdisplay, device_info->id); + if (G_LIKELY (device)) + { + /* update the right property */ + if (strcmp (names[1], "RightHanded") == 0) + xfce_pointers_helper_change_button_mapping (device_info, device, xdisplay, !!g_value_get_boolean (value), -1); + else if (strcmp (names[1], "ReverseScrolling") == 0) + xfce_pointers_helper_change_button_mapping (device_info, device, xdisplay, -1, !!g_value_get_boolean (value)); + else if (strcmp (names[1], "Threshold") == 0) + xfce_pointers_helper_change_feedback (device, xdisplay, g_value_get_int (value), -2.00); + else if (strcmp (names[1], "Acceleration") == 0) + xfce_pointers_helper_change_feedback (device, xdisplay, -2, g_value_get_double (value)); + + /* close the device */ + XCloseDevice (xdisplay, device); + } + + /* stop searching */ + n = ndevices; + } + + /* cleanup */ + g_free (device_name); + } + } + + /* cleanup */ + XFreeDeviceList (device_list); + } + + /* cleanup */ + g_strfreev (names); +} + + + +#ifdef HAVE_HAL +static gboolean +xfce_pointers_helper_device_added_timeout (gpointer user_data) +{ + XfcePointersHelper *helper = XFCE_POINTERS_HELPER (user_data); + + GDK_THREADS_ENTER (); + + /* restore the devices */ + xfce_pointers_helper_restore_devices (helper); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + + + +static void +xfce_pointers_helper_device_added_timeout_destroyed (gpointer user_data) +{ + /* reset the timeout id */ + XFCE_POINTERS_HELPER (user_data)->timeout_id = 0; +} + + + +static void +xfce_pointers_helper_device_added (LibHalContext *context, + const gchar *udi) +{ + XfcePointersHelper *helper; + + /* get the helper */ + helper = libhal_ctx_get_user_data (context); + + /* check if an input device has been added and no timeout is running */ + if (libhal_device_query_capability (context, udi, "input", NULL) + && helper->timeout_id == 0) + { + /* queue a new timeout */ + helper->timeout_id = g_timeout_add_full (G_PRIORITY_LOW, 1000, xfce_pointers_helper_device_added_timeout, + helper, xfce_pointers_helper_device_added_timeout_destroyed); + } +} +#endif + diff --git a/xfce4-settings-helper/pointers.h b/xfce4-settings-helper/pointers.h new file mode 100644 index 00000000..af1bb814 --- /dev/null +++ b/xfce4-settings-helper/pointers.h @@ -0,0 +1,35 @@ +/* $Id$ */ +/* + * Copyright (c) 2008 Nick Schermer + * + * 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 Library 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. + */ + +#ifndef __POINTERS_H__ +#define __POINTERS_H__ + +typedef struct _XfcePointersHelperClass XfcePointersHelperClass; +typedef struct _XfcePointersHelper XfcePointersHelper; + +#define XFCE_TYPE_POINTERS_HELPER (xfce_pointers_helper_get_type ()) +#define XFCE_POINTERS_HELPER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), XFCE_TYPE_POINTERS_HELPER, XfcePointersHelper)) +#define XFCE_POINTERS_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), XFCE_TYPE_POINTERS_HELPER, XfcePointersHelperClass)) +#define XFCE_IS_POINTERS_HELPER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), XFCE_TYPE_POINTERS_HELPER)) +#define XFCE_IS_POINTERS_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), XFCE_TYPE_POINTERS_HELPER)) +#define XFCE_POINTERS_HELPER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), XFCE_TYPE_POINTERS_HELPER, XfcePointersHelperClass)) + +GType xfce_pointers_helper_get_type (void) G_GNUC_CONST; + +#endif /* !__POINTERS_H__ */ diff --git a/xfce4-settings-helper/xkb.c b/xfce4-settings-helper/xkb.c index 02d15d9e..2fd3d123 100644 --- a/xfce4-settings-helper/xkb.c +++ b/xfce4-settings-helper/xkb.c @@ -1,3 +1,4 @@ +/* $Id$ */ /* * Copyright (c) 2008 Stephan Arts * @@ -23,191 +24,220 @@ #include #endif +#ifdef HAVE_STRING_H +#include +#endif #ifdef HAVE_UNISTD_H #include #endif #include - #include #ifdef HAVE_XF86MISC #include #endif -#define HAVE_XKB - -#include - #include - #include #include - -#include #include -#include +#include + +#include + -#include "xkb.h" -static gboolean xkbpresent = FALSE; +static void xfce_xkb_helper_class_init (XfceXkbHelperClass *klass); +static void xfce_xkb_helper_init (XfceXkbHelper *helper); +static void xfce_xkb_helper_finalize (GObject *object); +static void xfce_xkb_helper_set_auto_repeat_mode (XfceXkbHelper *helper); +static void xfce_xkb_helper_set_repeat_rate (XfceXkbHelper *helper); +static void xfce_xkb_helper_channel_property_changed (XfconfChannel *channel, + const gchar *property_name, + const GValue *value, + XfceXkbHelper *helper); -static XfconfChannel *xkb_channel; -static gboolean xkb_initialized = FALSE; -#define ALL -1 +struct _XfceXkbHelperClass +{ + GObjectClass __parent__; +}; + +struct _XfceXkbHelper +{ + GObject __parent__; + + /* xfconf channel */ + XfconfChannel *channel; + + /* if xf86misc is present */ + guint has_xf86misc : 1; +}; + + + +G_DEFINE_TYPE (XfceXkbHelper, xfce_xkb_helper, G_TYPE_OBJECT); + -static gboolean -load_xkb_settings (XfconfChannel *channel); static void -set_repeat (int key, int auto_repeat_mode) +xfce_xkb_helper_class_init (XfceXkbHelperClass *klass) { - XKeyboardControl values; - values.auto_repeat_mode = auto_repeat_mode; + GObjectClass *gobject_class; - gdk_flush (); - gdk_error_trap_push (); - if (key != ALL) + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = xfce_xkb_helper_finalize; +} + + + +static void +xfce_xkb_helper_init (XfceXkbHelper *helper) +{ + gint dummy; + + /* init */ + helper->channel = NULL; + helper->has_xf86misc = FALSE; + + if (XkbQueryExtension (GDK_DISPLAY (), &dummy, &dummy, &dummy, &dummy, &dummy)) { - values.key = key; - XChangeKeyboardControl (GDK_DISPLAY (), KBKey | KBAutoRepeatMode, &values); +#ifdef HAVE_XF86MISC + /* chek for xf86misc */ + helper->has_xf86misc = XF86MiscQueryVersion (GDK_DISPLAY (), &dummy, &dummy); +#endif + + /* open the channel */ + helper->channel = xfconf_channel_new ("xkb"); + + /* monitor channel changes */ + g_signal_connect (G_OBJECT (helper->channel), "property-changed", G_CALLBACK (xfce_xkb_helper_channel_property_changed), helper); + + /* load settings */ + xfce_xkb_helper_set_auto_repeat_mode (helper); + xfce_xkb_helper_set_repeat_rate (helper); } else { - XChangeKeyboardControl (GDK_DISPLAY (), KBAutoRepeatMode, &values); + /* warning */ + g_critical ("Failed to initialize the Xkb extension."); } +} + + + +static void +xfce_xkb_helper_finalize (GObject *object) +{ + XfceXkbHelper *helper = XFCE_XKB_HELPER (object); + + /* release the channel */ + if (G_LIKELY (helper->channel)) + g_object_unref (G_OBJECT (helper->channel)); + + (*G_OBJECT_CLASS (xfce_xkb_helper_parent_class)->finalize) (object); +} + + + +static void +xfce_xkb_helper_set_auto_repeat_mode (XfceXkbHelper *helper) +{ + XKeyboardControl values; + gboolean repeat; + + /* load setting */ + repeat = xfconf_channel_get_bool (helper->channel, "/Xkb/KeyRepeat", FALSE); + + /* flush and avoid crashes on x errors */ + gdk_flush (); + gdk_error_trap_push (); + + /* set key repeat */ + values.auto_repeat_mode = repeat ? 1 : 0; + + /* set key repeat */ + XChangeKeyboardControl (GDK_DISPLAY (), KBAutoRepeatMode, &values); + + /* flush errors and pop trap */ gdk_flush (); gdk_error_trap_pop (); } + + static void -set_repeat_rate (int delay, int rate) +xfce_xkb_helper_set_repeat_rate (XfceXkbHelper *helper) { #ifdef HAVE_XF86MISC XF86MiscKbdSettings values; #endif + XkbDescPtr xkb; + gint delay, rate; + + /* load settings */ + delay = xfconf_channel_get_int (helper->channel, "/Xkb/KeyRepeat/Delay", 0); + rate = xfconf_channel_get_int (helper->channel, "/Xkb/KeyRepeat/Rate", 0); + + /* flush and avoid crashes on x errors */ + gdk_flush (); + gdk_error_trap_push (); #ifdef HAVE_XF86MISC - if (miscpresent) + /* update the xkb misc keyboard delay and rate */ + if (G_LIKELY (helper->has_xf86misc )) { - gdk_flush (); - gdk_error_trap_push (); XF86MiscGetKbdSettings (GDK_DISPLAY (), &values); values.delay = delay; values.rate = rate; XF86MiscSetKbdSettings (GDK_DISPLAY (), &values); - gdk_flush (); - gdk_error_trap_pop (); } #endif -#ifdef HAVE_XKB - if (xkbpresent) + /* allocate xkb structure */ + xkb = XkbAllocKeyboard (); + if (G_LIKELY (xkb)) { - XkbDescPtr xkb = XkbAllocKeyboard (); - if (xkb) - { - gdk_error_trap_push (); - XkbGetControls (GDK_DISPLAY (), XkbRepeatKeysMask, xkb); - xkb->ctrls->repeat_delay = delay; - xkb->ctrls->repeat_interval = 1000 / rate; - XkbSetControls (GDK_DISPLAY (), XkbRepeatKeysMask, xkb); - XFree (xkb); - gdk_flush (); - gdk_error_trap_pop (); - } - else - { - g_warning ("XkbAllocKeyboard() returned null pointer"); - } - } -#endif -} + /* load controls */ + XkbGetControls (GDK_DISPLAY (), XkbRepeatKeysMask, xkb); + /* set new values */ + xkb->ctrls->repeat_delay = delay; + xkb->ctrls->repeat_interval = 1000 / rate; + /* set updated controls */ + XkbSetControls (GDK_DISPLAY (), XkbRepeatKeysMask, xkb); -static void -cb_xkb_channel_property_changed(XfconfChannel *channel, const gchar *name, const GValue *value, gpointer user_data) -{ - gint rate = 0; - if (!strcmp (name, "/Xkb/KeyRepeat")) - { - gboolean key_repeat = g_value_get_boolean (value); - set_repeat (ALL, key_repeat == TRUE?1:0); + /* cleanup */ + XFree (xkb); } - /* TODO */ - if (!strcmp (name, "/Xkb/KeyRepeat/Delay")) - { - rate = xfconf_channel_get_int (channel, "/Xkb/KeyRepeat/Rate", 0); - set_repeat_rate (g_value_get_int (value), rate); - } - if (!strcmp (name, "/Xkb/KeyRepeat/Rate")) - { - rate = xfconf_channel_get_int (channel, "/Xkb/KeyRepeat/Delay", 0); - set_repeat_rate (rate, g_value_get_int (value)); - } + /* flush errors and pop trap */ + gdk_flush (); + gdk_error_trap_pop (); } -gint -xkb_notification_init (XfconfChannel *channel) -{ - g_return_val_if_fail (xkb_initialized == FALSE, 1); - int xkbmajor = XkbMajorVersion, xkbminor = XkbMinorVersion; - int xkbopcode, xkbevent, xkberror; - xkb_channel = channel; -#ifdef DEBUG - g_message ("Querying Xkb extension"); -#endif - if (XkbQueryExtension (GDK_DISPLAY (), &xkbopcode, &xkbevent, &xkberror, &xkbmajor, &xkbminor)) +static void +xfce_xkb_helper_channel_property_changed (XfconfChannel *channel, + const gchar *property_name, + const GValue *value, + XfceXkbHelper *helper) +{ + g_return_if_fail (helper->channel == channel); + + if (strcmp (property_name, "/Xkb/KeyRepeat") == 0) { -#ifdef DEBUG - g_message ("Xkb extension found"); -#endif - xkbpresent = TRUE; + /* update auto repeat mode */ + xfce_xkb_helper_set_auto_repeat_mode (helper); } - else + else if (strcmp (property_name, "/Xkb/KeyRepeat/Delay") == 0 + || strcmp (property_name, "/Xkb/KeyRepeat/Rate") == 0) { -#ifdef DEBUG - g_message ("Your X server does not support Xkb extension"); -#endif - xkbpresent = FALSE; + /* update repeat rate */ + xfce_xkb_helper_set_repeat_rate (helper); } -#ifdef DEBUG - g_warning ("This build doesn't include support for Xkb extension"); -#endif - - g_signal_connect(G_OBJECT(channel), "property-changed", (GCallback)cb_xkb_channel_property_changed, NULL); - - xkb_initialized = TRUE; - - /* Load the xkb-settings - * (this is done from inside the main loop because it does - * not seem to work otherwise, probably caused by the gdk_error_* - * functions called in 'set_repeat' and 'set_repeat_rate') - */ - g_timeout_add (100, (GSourceFunc)load_xkb_settings, channel); - - return xkbpresent; -} - - -static gboolean -load_xkb_settings (XfconfChannel *channel) -{ - gboolean repeat; - gint rate, delay; - - repeat = xfconf_channel_get_bool (channel, "/Xkb/KeyRepeat", FALSE); - rate = xfconf_channel_get_int (channel, "/Xkb/KeyRepeat/Rate", 0); - delay = xfconf_channel_get_int (channel, "/Xkb/KeyRepeat/Delay", 0); - - set_repeat (ALL, repeat == TRUE?1:0); - set_repeat_rate (delay, rate); - - return FALSE; } diff --git a/xfce4-settings-helper/xkb.h b/xfce4-settings-helper/xkb.h index d5bc65a6..f33050b8 100644 --- a/xfce4-settings-helper/xkb.h +++ b/xfce4-settings-helper/xkb.h @@ -1,3 +1,4 @@ +/* $Id$ */ /* * Copyright (c) 2008 Stephan Arts * @@ -19,5 +20,19 @@ * by Olivier Fourdan. */ -gint -xkb_notification_init (XfconfChannel *channel); +#ifndef __XKB_H__ +#define __XKB_H__ + +typedef struct _XfceXkbHelperClass XfceXkbHelperClass; +typedef struct _XfceXkbHelper XfceXkbHelper; + +#define XFCE_TYPE_XKB_HELPER (xfce_xkb_helper_get_type ()) +#define XFCE_XKB_HELPER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), XFCE_TYPE_XKB_HELPER, XfceXkbHelper)) +#define XFCE_XKB_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), XFCE_TYPE_XKB_HELPER, XfceXkbHelperClass)) +#define XFCE_IS_XKB_HELPER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), XFCE_TYPE_XKB_HELPER)) +#define XFCE_IS_XKB_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), XFCE_TYPE_XKB_HELPER)) +#define XFCE_XKB_HELPER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), XFCE_TYPE_XKB_HELPER, XfceXkbHelperClass)) + +GType xfce_xkb_helper_get_type (void) G_GNUC_CONST; + +#endif /* !__XKB_H__ */ -- GitLab