3
* Copyright (c) 2008 Nick Schermer <nick@xfce.org>
4
* Jannis Pohlmann <jannis@xfce.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU Library General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36
#include <X11/extensions/XI.h>
37
#include <X11/extensions/XInput.h>
38
#include <X11/extensions/XIproto.h>
40
#include <X11/Xcursor/Xcursor.h>
41
#endif /* !HAVE_XCURSOR */
46
#include <xfconf/xfconf.h>
47
#include <libxfce4util/libxfce4util.h>
48
#include <libxfce4ui/libxfce4ui.h>
50
#include "mouse-dialog_ui.h"
52
/* test if the required version of inputproto (1.4.2) is available */
53
#if XI_Add_DevicePresenceNotify_Major >= 1 && defined (DeviceRemoved)
54
#define HAS_DEVICE_HOTPLUGGING
56
#undef HAS_DEVICE_HOTPLUGGING
58
#ifndef IsXExtensionPointer
59
#define IsXExtensionPointer 4
62
/* Xi 1.4 is required */
63
#define MIN_XI_VERS_MAJOR 1
64
#define MIN_XI_VERS_MINOR 4
68
#define PREVIEW_ROWS (3)
69
#define PREVIEW_COLUMNS (6)
70
#define PREVIEW_SIZE (24)
71
#define PREVIEW_SPACING (2)
72
#endif /* !HAVE_XCURSOR */
75
/* global setting channels */
76
XfconfChannel *xsettings_channel;
77
XfconfChannel *pointers_channel;
79
/* lock counter to avoid signals during updates */
80
static gint locked = 0;
82
/* the display for this window */
83
static GdkDisplay *display;
85
/* device update id */
86
static guint timeout_id = 0;
88
#ifdef HAS_DEVICE_HOTPLUGGING
89
/* event id for device add/remove */
90
static gint device_presence_event_type = 0;
94
static GdkNativeWindow opt_socket_id = 0;
95
static gchar *opt_device_name = NULL;
96
static gboolean opt_version = FALSE;
97
static GOptionEntry option_entries[] =
99
{ "device", 'd', 0, G_OPTION_ARG_STRING, &opt_device_name, N_("Active device in the dialog"), N_("DEVICE NAME") },
100
{ "socket-id", 's', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_INT, &opt_socket_id, N_("Settings manager socket"), N_("SOCKET ID") },
101
{ "version", 'v', 0, G_OPTION_ARG_NONE, &opt_version, N_("Version information"), NULL },
106
/* icon names for the preview widget */
107
static const gchar *preview_names[] = {
108
"left_ptr", "left_ptr_watch", "watch", "hand2",
109
"question_arrow", "sb_h_double_arrow", "sb_v_double_arrow", "bottom_left_corner",
110
"bottom_right_corner", "fleur", "pirate", "cross",
111
"X_cursor", "right_ptr", "right_side", "right_tee",
112
"sb_right_arrow", "sb_right_tee", "base_arrow_down", "base_arrow_up",
113
"bottom_side", "bottom_tee", "center_ptr", "circle",
114
"dot", "dot_box_mask", "dot_box_mask", "double_arrow",
115
"draped_box", "left_side", "left_tee", "ll_angle",
116
"top_side", "top_tee"
124
COLUMN_THEME_DISPLAY_NAME,
125
COLUMN_THEME_COMMENT,
128
#endif /* !HAVE_XCURSOR */
134
COLUMN_DEVICE_DISPLAY_NAME,
136
COLUMN_DEVICE_NBUTTONS,
143
mouse_settings_themes_pixbuf_from_filename (const gchar *filename,
147
GdkPixbuf *scaled, *pixbuf = NULL;
149
guchar *buffer, *p, tmp;
150
gdouble wratio, hratio;
151
gint dest_width, dest_height;
154
image = XcursorFilenameLoadImage (filename, size);
155
if (G_LIKELY (image))
158
bsize = image->width * image->height * 4;
160
/* allocate buffer */
161
buffer = g_malloc (bsize);
163
/* copy pixel data to buffer */
164
memcpy (buffer, image->pixels, bsize);
167
for (p = buffer; p < buffer + bsize; p += 4)
175
pixbuf = gdk_pixbuf_new_from_data (buffer, GDK_COLORSPACE_RGB, TRUE,
176
8, image->width, image->height,
178
(GdkPixbufDestroyNotify) g_free, NULL);
180
/* don't leak when creating the pixbuf failed */
181
if (G_UNLIKELY (pixbuf == NULL))
184
/* scale pixbuf if needed */
185
if (pixbuf && (image->height > size || image->width > size))
187
/* calculate the ratio */
188
wratio = (gdouble) image->width / (gdouble) size;
189
hratio = (gdouble) image->height / (gdouble) size;
192
dest_width = dest_height = size;
196
dest_width = rint (image->width / hratio);
198
dest_height = rint (image->height / wratio);
201
scaled = gdk_pixbuf_scale_simple (pixbuf, MAX (dest_width, 1), MAX (dest_height, 1), GDK_INTERP_BILINEAR);
203
/* release and set scaled pixbuf */
204
g_object_unref (G_OBJECT (pixbuf));
209
XcursorImageDestroy (image);
218
mouse_settings_themes_preview_icon (const gchar *path)
220
GdkPixbuf *pixbuf = NULL;
223
/* we only try the normal cursor, it is (most likely) always there */
224
filename = g_build_filename (path, "left_ptr", NULL);
226
/* try to load the pixbuf */
227
pixbuf = mouse_settings_themes_pixbuf_from_filename (filename, PREVIEW_SIZE);
238
mouse_settings_themes_preview_image (const gchar *path,
247
/* create an empty preview image */
248
preview = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
249
(PREVIEW_SIZE + PREVIEW_SPACING) * PREVIEW_COLUMNS - PREVIEW_SPACING,
250
(PREVIEW_SIZE + PREVIEW_SPACING) * PREVIEW_ROWS - PREVIEW_SPACING);
252
if (G_LIKELY (preview))
254
/* make the pixbuf transparent */
255
gdk_pixbuf_fill (preview, 0x00000000);
257
for (i = 0, position = 0; i < G_N_ELEMENTS (preview_names); i++)
259
/* create cursor filename and try to load the pixbuf */
260
filename = g_build_filename (path, preview_names[i], NULL);
261
pixbuf = mouse_settings_themes_pixbuf_from_filename (filename, PREVIEW_SIZE);
264
if (G_LIKELY (pixbuf))
266
/* calculate the icon position */
267
dest_x = (position % PREVIEW_COLUMNS) * (PREVIEW_SIZE + PREVIEW_SPACING);
268
dest_y = (position / PREVIEW_COLUMNS) * (PREVIEW_SIZE + PREVIEW_SPACING);
270
/* render it in the preview */
271
gdk_pixbuf_scale (pixbuf, preview, dest_x, dest_y,
272
gdk_pixbuf_get_width (pixbuf),
273
gdk_pixbuf_get_height (pixbuf),
275
1.00, 1.00, GDK_INTERP_BILINEAR);
278
/* release the pixbuf */
279
g_object_unref (G_OBJECT (pixbuf));
281
/* break if we've added enough icons */
282
if (++position >= PREVIEW_ROWS * PREVIEW_COLUMNS)
288
gtk_image_set_from_pixbuf (GTK_IMAGE (image), preview);
290
/* release the pixbuf */
291
g_object_unref (G_OBJECT (preview));
295
/* clear the image */
296
gtk_image_clear (GTK_IMAGE (image));
303
mouse_settings_themes_selection_changed (GtkTreeSelection *selection,
308
gboolean has_selection;
312
has_selection = gtk_tree_selection_get_selected (selection, &model, &iter);
313
if (G_LIKELY (has_selection))
315
/* get theme information from model */
316
gtk_tree_model_get (model, &iter, COLUMN_THEME_PATH, &path,
317
COLUMN_THEME_NAME, &name, -1);
319
/* update the preview widget */
320
image = gtk_builder_get_object (builder, "mouse-theme-preview");
321
mouse_settings_themes_preview_image (path, GTK_IMAGE (image));
323
/* write configuration (not during a lock) */
325
xfconf_channel_set_string (xsettings_channel, "/Gtk/CursorThemeName", name);
336
mouse_settings_themes_sort_func (GtkTreeModel *model,
341
gchar *name_a, *name_b;
344
/* get the names from the model */
345
gtk_tree_model_get (model, a, COLUMN_THEME_DISPLAY_NAME, &name_a, -1);
346
gtk_tree_model_get (model, b, COLUMN_THEME_DISPLAY_NAME, &name_b, -1);
348
/* make sure the names are not null */
349
if (G_UNLIKELY (name_a == NULL))
350
name_a = g_strdup ("");
351
if (G_UNLIKELY (name_b == NULL))
352
name_b = g_strdup ("");
354
/* sort the names but keep Default on top */
355
if (g_utf8_collate (name_a, _("Default")) == 0)
357
else if (g_utf8_collate (name_b, _("Default")) == 0)
360
retval = g_utf8_collate (name_a, name_b);
372
mouse_settings_themes_populate_store (GtkBuilder *builder)
384
const gchar *comment;
389
GtkTreePath *active_path = NULL;
391
GtkCellRenderer *renderer;
392
GtkTreeViewColumn *column;
394
GtkTreeSelection *selection;
395
gchar *comment_escaped;
397
/* get the cursor paths */
398
#if XCURSOR_LIB_MAJOR == 1 && XCURSOR_LIB_MINOR < 1
399
path = "~/.icons:/usr/share/icons:/usr/share/pixmaps:/usr/X11R6/lib/X11/icons";
401
path = XcursorLibraryPath ();
404
/* split the paths */
405
basedirs = g_strsplit (path, ":", -1);
407
/* get the active theme */
408
active_theme = xfconf_channel_get_string (xsettings_channel, "/Gtk/CursorThemeName", "default");
410
/* create the store */
411
store = gtk_list_store_new (N_THEME_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING,
412
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
415
gtk_list_store_insert_with_values (store, &iter, position++,
416
COLUMN_THEME_NAME, "default",
417
COLUMN_THEME_DISPLAY_NAME, _("Default"), -1);
419
/* store the default path, so we always select a theme */
420
active_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
422
if (G_LIKELY (basedirs))
424
/* walk the base directories */
425
for (i = 0; basedirs[i] != NULL; i++)
430
/* parse the homedir if needed */
431
if (strstr (basedirs[i], "~/") != NULL)
432
path = homedir = g_strconcat (g_get_home_dir (), basedirs[i] + 1, NULL);
437
dir = g_dir_open (path, 0, NULL);
442
/* get the directory name */
443
theme = g_dir_read_name (dir);
444
if (G_UNLIKELY (theme == NULL))
447
/* build the full cursor path */
448
filename = g_build_filename (path, theme, "cursors", NULL);
450
/* check if it looks like a cursor theme */
451
if (g_file_test (filename, G_FILE_TEST_IS_DIR))
453
/* try to load a pixbuf */
454
pixbuf = mouse_settings_themes_preview_icon (filename);
456
/* insert in the store */
457
gtk_list_store_insert_with_values (store, &iter, position++,
458
COLUMN_THEME_PIXBUF, pixbuf,
459
COLUMN_THEME_NAME, theme,
460
COLUMN_THEME_DISPLAY_NAME, theme,
461
COLUMN_THEME_PATH, filename, -1);
463
/* check if this is the active theme, set the path */
464
if (strcmp (active_theme, theme) == 0)
466
gtk_tree_path_free (active_path);
467
active_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
471
if (G_LIKELY (pixbuf))
472
g_object_unref (G_OBJECT (pixbuf));
474
/* check for a index.theme file for additional information */
475
index_file = g_build_filename (path, theme, "index.theme", NULL);
476
if (g_file_test (index_file, G_FILE_TEST_IS_REGULAR))
478
/* open theme desktop file */
479
rc = xfce_rc_simple_open (index_file, TRUE);
482
/* check for the theme group */
483
if (xfce_rc_has_group (rc, "Icon Theme"))
486
xfce_rc_set_group (rc, "Icon Theme");
489
name = xfce_rc_read_entry (rc, "Name", theme);
490
comment = xfce_rc_read_entry (rc, "Comment", NULL);
492
/* escape the comment */
493
comment_escaped = comment ? g_markup_escape_text (comment, -1) : NULL;
496
gtk_list_store_set (store, &iter,
497
COLUMN_THEME_DISPLAY_NAME, name,
498
COLUMN_THEME_COMMENT, comment_escaped, -1);
501
g_free (comment_escaped);
517
/* close directory */
526
g_strfreev (basedirs);
530
g_free (active_theme);
532
/* set the treeview store */
533
treeview = gtk_builder_get_object (builder, "mouse-theme-treeview");
534
gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store));
535
gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (treeview), COLUMN_THEME_COMMENT);
537
/* setup the columns */
538
renderer = gtk_cell_renderer_pixbuf_new ();
539
column = gtk_tree_view_column_new_with_attributes ("", renderer, "pixbuf", COLUMN_THEME_PIXBUF, NULL);
540
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
542
renderer = gtk_cell_renderer_text_new ();
543
column = gtk_tree_view_column_new_with_attributes ("", renderer, "text", COLUMN_THEME_DISPLAY_NAME, NULL);
544
g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
545
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
547
/* setup selection */
548
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
549
gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
550
g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (mouse_settings_themes_selection_changed), builder);
552
/* select the active theme in the treeview */
553
gtk_tree_view_set_cursor (GTK_TREE_VIEW (treeview), active_path, NULL, FALSE);
554
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (treeview), active_path, NULL, TRUE, 0.5, 0.0);
555
gtk_tree_path_free (active_path);
558
gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store), COLUMN_THEME_DISPLAY_NAME, mouse_settings_themes_sort_func, NULL, NULL);
559
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), COLUMN_THEME_DISPLAY_NAME, GTK_SORT_ASCENDING);
561
/* release the store */
562
g_object_unref (G_OBJECT (store));
564
#endif /* !HAVE_XCURSOR */
569
mouse_settings_device_selection_changed (GtkTreeSelection *selection,
575
XFeedbackState *states, *pt;
577
XPtrFeedbackState *state;
580
gint id_1 = 0, id_3 = 0;
581
gint id_4 = 0, id_5 = 0;
582
gdouble acceleration = -1.00;
587
gboolean has_selection;
590
/* lock the dialog */
593
/* flush x and trap errors */
595
gdk_error_trap_push ();
597
/* get the selected item */
598
has_selection = gtk_tree_selection_get_selected (selection, &model, &iter);
599
if (G_LIKELY (has_selection))
601
/* get device id and number of buttons */
602
gtk_tree_model_get (model, &iter, COLUMN_DEVICE_XID, &xid,
603
COLUMN_DEVICE_NBUTTONS, &nbuttons, -1);
605
/* get the x display */
606
xdisplay = gdk_x11_display_get_xdisplay (display);
608
/* open the device */
609
device = XOpenDevice (xdisplay, xid);
611
if (G_LIKELY (device))
613
/* allocate button map */
614
buttonmap = g_new0 (guchar, nbuttons);
616
/* get the button mapping */
617
XGetDeviceButtonMapping (xdisplay, device, buttonmap, nbuttons);
619
/* figure out the position of the first and second/third button in the map */
620
for (i = 0; i < nbuttons; i++)
622
if (buttonmap[i] == 1)
624
else if (buttonmap[i] == (nbuttons < 3 ? 2 : 3))
626
else if (buttonmap[i] == 4)
628
else if (buttonmap[i] == 5)
635
/* get the feedback states for this device */
636
states = XGetFeedbackControl (xdisplay, device, &nstates);
639
/* get the pointer feedback class */
640
for (pt = states, i = 0; i < nstates; i++)
642
if (pt->class == PtrFeedbackClass)
645
state = (XPtrFeedbackState *) pt;
646
acceleration = (gdouble) state->accelNum / (gdouble) state->accelDenom;
647
threshold = state->threshold;
653
/* advance the offset */
654
pt = (XFeedbackState *) ((gchar *) pt + pt->length);
657
XFreeFeedbackList (states);
660
/* close the device */
661
XCloseDevice (xdisplay, device);
665
/* update button order */
666
object = gtk_builder_get_object (builder, id_1 > id_3 ? "mouse-left-handed" : "mouse-right-handed");
667
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (object), TRUE);
669
object = gtk_builder_get_object (builder, "mouse-reverse-scrolling");
670
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (object), !!(id_5 < id_4));
671
gtk_widget_set_sensitive (GTK_WIDGET (object), nbuttons >= 5);
673
/* update acceleration scale */
674
object = gtk_builder_get_object (builder, "mouse-acceleration-scale");
675
gtk_range_set_value (GTK_RANGE (object), acceleration);
676
gtk_widget_set_sensitive (GTK_WIDGET (object), acceleration != -1);
678
/* update threshold scale */
679
object = gtk_builder_get_object (builder, "mouse-threshold-scale");
680
gtk_range_set_value (GTK_RANGE (object), threshold);
681
gtk_widget_set_sensitive (GTK_WIDGET (object), threshold != -1);
683
/* flush and remove the x error trap */
685
gdk_error_trap_pop ();
694
mouse_settings_device_save (GtkBuilder *builder)
697
GtkTreeSelection *selection;
700
gboolean has_selection;
703
gchar property_name[512];
704
gboolean righthanded;
706
gdouble acceleration;
707
gboolean reverse_scrolling;
709
/* leave when locked */
713
/* get the treeview */
714
treeview = gtk_builder_get_object (builder, "mouse-devices-treeview");
716
/* get the selection */
717
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
719
has_selection = gtk_tree_selection_get_selected (selection, &model, &iter);
720
if (G_LIKELY (has_selection))
722
/* get device id and number of buttons */
723
gtk_tree_model_get (model, &iter, COLUMN_DEVICE_NAME, &name, -1);
727
/* store the button order */
728
object = gtk_builder_get_object (builder, "mouse-right-handed");
729
g_snprintf (property_name, sizeof (property_name), "/%s/RightHanded", name);
730
righthanded = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (object));
731
if (!xfconf_channel_has_property (pointers_channel, property_name)
732
|| xfconf_channel_get_bool (pointers_channel, property_name, TRUE) != righthanded)
733
xfconf_channel_set_bool (pointers_channel, property_name, righthanded);
735
/* store reverse scrolling */
736
object = gtk_builder_get_object (builder, "mouse-reverse-scrolling");
737
g_snprintf (property_name, sizeof (property_name), "/%s/ReverseScrolling", name);
738
reverse_scrolling = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (object));
739
if (xfconf_channel_get_bool (pointers_channel, property_name, FALSE) != reverse_scrolling)
740
xfconf_channel_set_bool (pointers_channel, property_name, reverse_scrolling);
742
/* store the threshold */
743
object = gtk_builder_get_object (builder, "mouse-threshold-scale");
744
g_snprintf (property_name, sizeof (property_name), "/%s/Threshold", name);
745
threshold = gtk_range_get_value (GTK_RANGE (object));
746
if (xfconf_channel_get_int (pointers_channel, property_name, -1) != threshold)
747
xfconf_channel_set_int (pointers_channel, property_name, threshold);
749
/* store the acceleration */
750
object = gtk_builder_get_object (builder, "mouse-acceleration-scale");
751
g_snprintf (property_name, sizeof (property_name), "/%s/Acceleration", name);
752
acceleration = gtk_range_get_value (GTK_RANGE (object));
753
if (xfconf_channel_get_double (pointers_channel, property_name, -1) != acceleration)
754
xfconf_channel_set_double (pointers_channel, property_name, acceleration);
765
mouse_settings_device_name_edited (GtkCellRendererText *renderer,
771
GtkTreeSelection *selection;
772
gboolean has_selection;
775
gchar *internal_name;
776
gchar *property_name;
777
gchar *new_name_escaped;
779
/* check if the new name is valid */
780
if (new_name == NULL || *new_name == '\0')
783
/* get the treeview's selection */
784
treeview = gtk_builder_get_object (builder, "mouse-devices-treeview");
785
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
787
/* get the selected item */
788
has_selection = gtk_tree_selection_get_selected (selection, &model, &iter);
789
if (G_LIKELY (has_selection))
791
/* get the internal device name */
792
gtk_tree_model_get (model, &iter, COLUMN_DEVICE_NAME, &internal_name, -1);
794
/* store the new name in the channel */
795
property_name = g_strdup_printf ("/%s", internal_name);
796
xfconf_channel_set_string (pointers_channel, property_name, new_name);
798
/* escape before adding in the store */
799
new_name_escaped = g_markup_escape_text (new_name, -1);
801
/* set the new device name in the store */
802
gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_DEVICE_DISPLAY_NAME, new_name_escaped, -1);
805
g_free (new_name_escaped);
806
g_free (property_name);
807
g_free (internal_name);
814
mouse_settings_device_xfconf_name (const gchar *name)
819
/* NOTE: this function exists in both the dialog and
820
* helper code and they have to identical! */
822
/* allocate a string */
823
string = g_string_sized_new (strlen (name));
825
/* create a name with only valid chars */
826
for (p = name; *p != '\0'; p++)
828
if ((*p >= 'A' && *p <= 'Z')
829
|| (*p >= 'a' && *p <= 'z')
830
|| (*p >= '0' && *p <= '9')
831
|| *p == '_' || *p == '-')
832
string = g_string_append_c (string, *p);
834
string = g_string_append_c (string, '_');
837
/* return the new string */
838
return g_string_free (string, FALSE);
844
mouse_settings_device_populate_store (GtkBuilder *builder,
845
gboolean create_store)
848
XDeviceInfo *device_list, *device_info;
849
gchar *display_name, *usb;
857
GtkTreePath *path = NULL;
858
GtkTreeViewColumn *column;
859
GtkCellRenderer *renderer;
860
GtkTreeSelection *selection;
862
gchar *property_name;
863
gchar *property_value;
868
/* flush x and trap errors */
870
gdk_error_trap_push ();
872
/* get the treeview */
873
treeview = gtk_builder_get_object (builder, "mouse-devices-treeview");
875
/* create or get the store */
876
if (G_LIKELY (create_store))
878
store = gtk_list_store_new (N_DEVICE_COLUMNS, G_TYPE_STRING, G_TYPE_STRING,
879
G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
883
store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)));
884
gtk_list_store_clear (store);
887
/* get the x display */
888
xdisplay = gdk_x11_display_get_xdisplay (display);
890
/* get all the registered devices */
891
device_list = XListInputDevices (xdisplay, &ndevices);
893
for (i = 0; i < ndevices; i++)
896
device_info = &device_list[i];
898
/* filter out the pointer devices */
899
if (device_info->use == IsXExtensionPointer)
901
/* get the device classes */
902
ptr = device_info->inputclassinfo;
904
/* walk all the classes */
905
for (m = 0, num_buttons = 0; m < device_info->num_classes; m++)
907
/* find the button class */
908
if (ptr->class == ButtonClass)
910
/* get the number of buttons */
911
num_buttons = ((XButtonInfoPtr) ptr)->num_buttons;
917
/* advance the offset */
918
ptr = (XAnyClassPtr) ((gchar *) ptr + ptr->length);
921
/* only append devices with buttons */
922
if (G_UNLIKELY (num_buttons <= 0))
925
/* ignore XTEST device */
926
if (g_str_has_prefix (device_info->name, "Virtual core XTEST"))
929
/* create a valid xfconf device name */
930
device_name = mouse_settings_device_xfconf_name (device_info->name);
932
/* check if there is a custom name set by the user */
933
property_name = g_strdup_printf ("/%s", device_name);
934
if (xfconf_channel_has_property (pointers_channel, property_name))
936
/* get the name from the config file, escape it */
937
property_value = xfconf_channel_get_string (pointers_channel, property_name, NULL);
938
display_name = g_markup_escape_text (property_value, -1);
939
g_free (property_value);
943
/* get the device name, escaped */
944
display_name = g_markup_escape_text (device_info->name, -1);
946
/* get rid of usb crap in the name */
947
if ((usb = strstr (display_name, "-usb")) != NULL)
951
/* insert in the store */
952
gtk_list_store_insert_with_values (store, &iter, i,
953
COLUMN_DEVICE_ICON, "input-mouse",
954
COLUMN_DEVICE_NAME, device_name,
955
COLUMN_DEVICE_DISPLAY_NAME, display_name,
956
COLUMN_DEVICE_XID, device_info->id,
957
COLUMN_DEVICE_NBUTTONS, num_buttons, -1);
959
/* check if we should select this device (for user convience also the display name) */
960
if (opt_device_name && (strcmp (opt_device_name, device_info->name) == 0
961
|| (display_name && strcmp (opt_device_name, display_name) == 0)))
963
path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
964
g_free (opt_device_name);
965
opt_device_name = NULL;
969
g_free (property_name);
970
g_free (device_name);
971
g_free (display_name);
976
XFreeDeviceList (device_list);
978
/* flush and remove the x error trap */
980
gdk_error_trap_pop ();
982
/* get the selection */
983
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
985
if (G_LIKELY (create_store))
987
/* set the treeview model */
988
gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store));
989
gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (treeview), COLUMN_DEVICE_DISPLAY_NAME);
992
renderer = gtk_cell_renderer_pixbuf_new ();
993
column = gtk_tree_view_column_new_with_attributes ("", renderer, "icon-name", COLUMN_DEVICE_ICON, NULL);
994
g_object_set (G_OBJECT (renderer), "stock-size", GTK_ICON_SIZE_DND, NULL);
995
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
998
renderer = gtk_cell_renderer_text_new ();
999
column = gtk_tree_view_column_new_with_attributes ("", renderer, "markup", COLUMN_DEVICE_DISPLAY_NAME, NULL);
1000
g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, "editable", TRUE, NULL);
1001
g_signal_connect (G_OBJECT (renderer), "edited", G_CALLBACK (mouse_settings_device_name_edited), builder);
1002
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
1004
/* setup tree selection */
1005
gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
1006
g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (mouse_settings_device_selection_changed), builder);
1009
/* select the mouse in the tree */
1010
if (G_LIKELY (path == NULL))
1011
path = gtk_tree_path_new_first ();
1012
gtk_tree_selection_select_path (selection, path);
1013
gtk_tree_path_free (path);
1015
/* sort after selecting the path */
1016
if (G_LIKELY (create_store))
1018
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), COLUMN_DEVICE_XID, GTK_SORT_ASCENDING);
1019
g_object_unref (G_OBJECT (store));
1029
mouse_settings_device_update_sliders (gpointer user_data)
1031
GtkBuilder *builder = GTK_BUILDER (user_data);
1032
GObject *treeview, *button;
1034
GDK_THREADS_ENTER ();
1036
/* get the treeview */
1037
treeview = gtk_builder_get_object (builder, "mouse-devices-treeview");
1040
mouse_settings_device_selection_changed (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), builder);
1042
/* make the button sensitive again */
1043
button = gtk_builder_get_object (builder, "mouse-reset");
1044
gtk_widget_set_sensitive (GTK_WIDGET (button), TRUE);
1046
GDK_THREADS_LEAVE ();
1054
mouse_settings_device_list_changed_timeout_destroyed (gpointer user_data)
1056
/* reset the timeout id */
1063
mouse_settings_device_reset (GtkWidget *button,
1064
GtkBuilder *builder)
1067
GtkTreeSelection *selection;
1068
gchar *name, *property_name;
1069
gboolean has_selection;
1070
GtkTreeModel *model;
1073
/* leave when locked */
1077
/* get the treeview */
1078
treeview = gtk_builder_get_object (builder, "mouse-devices-treeview");
1080
/* get the selection */
1081
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
1083
has_selection = gtk_tree_selection_get_selected (selection, &model, &iter);
1084
if (G_LIKELY (has_selection))
1086
/* get device id and number of buttons */
1087
gtk_tree_model_get (model, &iter, COLUMN_DEVICE_NAME, &name, -1);
1089
if (G_LIKELY (name && timeout_id == 0))
1091
/* make the button insensitive */
1092
gtk_widget_set_sensitive (button, FALSE);
1094
/* set the threshold to -1 */
1095
property_name = g_strdup_printf ("/%s/Threshold", name);
1096
xfconf_channel_set_int (pointers_channel, property_name, -1);
1097
g_free (property_name);
1099
/* set the acceleration to -1 */
1100
property_name = g_strdup_printf ("/%s/Acceleration", name);
1101
xfconf_channel_set_double (pointers_channel, property_name, -1.00);
1102
g_free (property_name);
1104
/* update the sliders in 500ms */
1105
timeout_id = g_timeout_add_full (G_PRIORITY_LOW, 500, mouse_settings_device_update_sliders,
1106
builder, mouse_settings_device_list_changed_timeout_destroyed);
1116
#ifdef HAS_DEVICE_HOTPLUGGING
1117
static GdkFilterReturn
1118
mouse_settings_event_filter (GdkXEvent *xevent,
1119
GdkEvent *gdk_event,
1122
XEvent *event = xevent;
1123
XDevicePresenceNotifyEvent *dpn_event = xevent;
1125
/* update on device changes */
1126
if (event->type == device_presence_event_type
1127
&& (dpn_event->devchange == DeviceAdded
1128
|| dpn_event->devchange == DeviceRemoved))
1129
mouse_settings_device_populate_store (GTK_BUILDER (user_data), FALSE);
1131
return GDK_FILTER_CONTINUE;
1137
mouse_settings_create_event_filter (GtkBuilder *builder)
1140
XEventClass event_class;
1142
/* flush x and trap errors */
1144
gdk_error_trap_push ();
1146
/* get the default display and root window */
1147
xdisplay = gdk_x11_display_get_xdisplay (display);
1148
if (G_UNLIKELY (!xdisplay))
1151
/* monitor device change events */
1152
DevicePresence (xdisplay, device_presence_event_type, event_class);
1153
XSelectExtensionEvent (xdisplay, RootWindow (xdisplay, DefaultScreen (xdisplay)), &event_class, 1);
1155
/* flush and remove the x error trap */
1157
gdk_error_trap_pop ();
1159
/* add an event filter */
1160
gdk_window_add_filter (NULL, mouse_settings_event_filter, builder);
1167
main (gint argc, gchar **argv)
1171
GObject *plug_child;
1172
GtkBuilder *builder;
1173
GError *error = NULL;
1175
XExtensionVersion *version = NULL;
1177
/* setup translation domain */
1178
xfce_textdomain (GETTEXT_PACKAGE, LOCALEDIR, "UTF-8");
1180
/* initialize Gtk+ */
1181
if (!gtk_init_with_args (&argc, &argv, "", option_entries, GETTEXT_PACKAGE, &error))
1183
if (G_LIKELY (error))
1186
g_print ("%s: %s.\n", G_LOG_DOMAIN, error->message);
1187
g_print (_("Type '%s --help' for usage."), G_LOG_DOMAIN);
1191
g_error_free (error);
1195
g_error ("Unable to open display.");
1198
return EXIT_FAILURE;
1201
/* print version information */
1202
if (G_UNLIKELY (opt_version))
1204
g_print ("%s %s (Xfce %s)\n\n", G_LOG_DOMAIN, PACKAGE_VERSION, xfce_version_string ());
1205
g_print ("%s\n", "Copyright (c) 2004-2011");
1206
g_print ("\t%s\n\n", _("The Xfce development team. All rights reserved."));
1207
g_print (_("Please report bugs to <%s>."), PACKAGE_BUGREPORT);
1210
return EXIT_SUCCESS;
1213
/* initialize xfconf */
1214
if (G_UNLIKELY (!xfconf_init (&error)))
1216
/* print error and leave */
1217
g_critical ("Failed to connect to Xfconf daemon: %s", error->message);
1218
g_error_free (error);
1220
return EXIT_FAILURE;
1224
version = XGetExtensionVersion (GDK_DISPLAY (), INAME);
1225
if (version == NULL || !version->present)
1227
g_critical ("XI is not present.");
1228
return EXIT_FAILURE;
1230
else if (version->major_version < MIN_XI_VERS_MAJOR
1231
|| (version->major_version == MIN_XI_VERS_MAJOR
1232
&& version->minor_version < MIN_XI_VERS_MINOR))
1234
g_critical ("Your XI is too old (%d.%d) version %d.%d is required.",
1235
version->major_version, version->minor_version,
1236
MIN_XI_VERS_MAJOR, MIN_XI_VERS_MINOR);
1237
return EXIT_FAILURE;
1240
/* hook to make sure the libxfce4ui library is linked */
1241
if (xfce_titled_dialog_get_type () == 0)
1242
return EXIT_FAILURE;
1244
/* open the xsettings and pointers channel */
1245
xsettings_channel = xfconf_channel_new ("xsettings");
1246
pointers_channel = xfconf_channel_new ("pointers");
1248
if (G_LIKELY (pointers_channel && xsettings_channel))
1250
/* load the Gtk+ user-interface file */
1251
builder = gtk_builder_new ();
1252
if (gtk_builder_add_from_string (builder, mouse_dialog_ui,
1253
mouse_dialog_ui_length, &error) != 0)
1258
/* set the working display for this instance */
1259
display = gdk_display_get_default ();
1261
/* populate the devices treeview */
1262
mouse_settings_device_populate_store (builder, TRUE);
1264
/* connect signals */
1265
object = gtk_builder_get_object (builder, "mouse-acceleration-scale");
1266
g_signal_connect_swapped (G_OBJECT (object), "value-changed", G_CALLBACK (mouse_settings_device_save), builder);
1268
object = gtk_builder_get_object (builder, "mouse-threshold-scale");
1269
g_signal_connect_swapped (G_OBJECT (object), "value-changed", G_CALLBACK (mouse_settings_device_save), builder);
1271
object = gtk_builder_get_object (builder, "mouse-left-handed");
1272
g_signal_connect_swapped (G_OBJECT (object), "toggled", G_CALLBACK (mouse_settings_device_save), builder);
1274
object = gtk_builder_get_object (builder, "mouse-right-handed");
1275
g_signal_connect_swapped (G_OBJECT (object), "toggled", G_CALLBACK (mouse_settings_device_save), builder);
1277
object = gtk_builder_get_object (builder, "mouse-reverse-scrolling");
1278
g_signal_connect_swapped (G_OBJECT (object), "toggled", G_CALLBACK (mouse_settings_device_save), builder);
1280
object = gtk_builder_get_object (builder, "mouse-reset");
1281
g_signal_connect (G_OBJECT (object), "clicked", G_CALLBACK (mouse_settings_device_reset), builder);
1284
/* populate the themes treeview */
1285
mouse_settings_themes_populate_store (builder);
1287
/* connect the cursor size in the cursor tab */
1288
object = gtk_builder_get_object (builder, "mouse-cursor-size");
1289
xfconf_g_property_bind (xsettings_channel, "/Gtk/CursorThemeSize", G_TYPE_INT, G_OBJECT (object), "value");
1291
/* hide the themes tab */
1292
object = gtk_builder_get_object (builder, "mouse-themes-hbox");
1293
gtk_widget_hide (GTK_WIDGET (object));
1294
#endif /* !HAVE_XCURSOR */
1296
/* connect sliders in the gtk tab */
1297
object = gtk_builder_get_object (builder, "mouse-dnd-threshold");
1298
xfconf_g_property_bind (xsettings_channel, "/Net/DndDragThreshold", G_TYPE_INT, G_OBJECT (object), "value");
1300
object = gtk_builder_get_object (builder, "mouse-double-click-time");
1301
xfconf_g_property_bind (xsettings_channel, "/Net/DoubleClickTime", G_TYPE_INT, G_OBJECT (object), "value");
1303
object = gtk_builder_get_object (builder, "mouse-double-click-distance");
1304
xfconf_g_property_bind (xsettings_channel, "/Net/DoubleClickDistance", G_TYPE_INT, G_OBJECT (object), "value");
1306
#ifdef HAS_DEVICE_HOTPLUGGING
1307
/* create the event filter for device monitoring */
1308
mouse_settings_create_event_filter (builder);
1311
if (G_UNLIKELY (opt_socket_id == 0))
1313
/* get the dialog */
1314
dialog = gtk_builder_get_object (builder, "mouse-dialog");
1319
gtk_widget_show (GTK_WIDGET (dialog));
1320
g_signal_connect (dialog, "response", G_CALLBACK (gtk_main_quit), NULL);
1322
/* To prevent the settings dialog to be saved in the session */
1323
gdk_set_sm_client_id ("FAKE ID");
1327
gtk_widget_destroy (GTK_WIDGET (dialog));
1331
/* Create plug widget */
1332
plug = gtk_plug_new (opt_socket_id);
1333
g_signal_connect (plug, "delete-event", G_CALLBACK (gtk_main_quit), NULL);
1334
gtk_widget_show (plug);
1336
/* Stop startup notification */
1337
gdk_notify_startup_complete ();
1339
/* Get plug child widget */
1340
plug_child = gtk_builder_get_object (builder, "plug-child");
1341
gtk_widget_reparent (GTK_WIDGET (plug_child), plug);
1342
gtk_widget_show (GTK_WIDGET (plug_child));
1347
/* To prevent the settings dialog to be saved in the session */
1348
gdk_set_sm_client_id ("FAKE ID");
1350
/* Enter main loop */
1356
g_error ("Failed to load the UI file: %s.", error->message);
1357
g_error_free (error);
1360
/* release the Gtk+ user-interface file */
1361
g_object_unref (G_OBJECT (builder));
1363
/* release the channels */
1364
g_object_unref (G_OBJECT (xsettings_channel));
1365
g_object_unref (G_OBJECT (pointers_channel));
1368
/* shutdown xfconf */
1372
g_free (opt_device_name);
1374
return EXIT_SUCCESS;