1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
4 * Copyright (C) 2010 Red Hat, Inc.
5 *
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.
10 *
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 General Public License for more details.
15 *
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., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA.
19 *
20 */
21
22 #include "config.h"
23
24 #include <sys/types.h>
25 #include <sys/wait.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <errno.h>
31
32 #include <locale.h>
33
34 #include <glib.h>
35 #include <gtk/gtk.h>
36 #include <gdk/gdkx.h>
37 #include <X11/Xatom.h>
38 #include <X11/extensions/XTest.h>
39 #include <X11/keysym.h>
40 #include <Xwacom.h>
41 #define GNOME_DESKTOP_USE_UNSTABLE_API
42 #include <libcinnamon-desktop/gnome-rr.h>
43
44 #include "csd-enums.h"
45 #include "csd-input-helper.h"
46 #include "csd-keygrab.h"
47 #include "cinnamon-settings-profile.h"
48 #include "csd-wacom-manager.h"
49 #include "csd-wacom-device.h"
50 #include "csd-wacom-osd-window.h"
51
52 #define CSD_WACOM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CSD_TYPE_WACOM_MANAGER, CsdWacomManagerPrivate))
53
54 #define KEY_ROTATION "rotation"
55 #define KEY_TOUCH "touch"
56 #define KEY_TPCBUTTON "tablet-pc-button"
57 #define KEY_IS_ABSOLUTE "is-absolute"
58 #define KEY_AREA "area"
59 #define KEY_DISPLAY "display"
60 #define KEY_KEEP_ASPECT "keep-aspect"
61 #define KEY_KEEP_ROTATION "keep-rotation"
62
63 /* Stylus and Eraser settings */
64 #define KEY_BUTTON_MAPPING "buttonmapping"
65 #define KEY_PRESSURETHRESHOLD "pressurethreshold"
66 #define KEY_PRESSURECURVE "pressurecurve"
67
68 /* Button settings */
69 #define KEY_ACTION_TYPE "action-type"
70 #define KEY_CUSTOM_ACTION "custom-action"
71 #define KEY_CUSTOM_ELEVATOR_ACTION "custom-elevator-action"
72
73 /* See "Wacom Pressure Threshold" */
74 #define DEFAULT_PRESSURE_THRESHOLD 27
75
76 struct CsdWacomManagerPrivate
77 {
78 guint start_idle_id;
79 GdkDeviceManager *device_manager;
80 guint device_added_id;
81 guint device_removed_id;
82 GHashTable *devices; /* key = GdkDevice, value = CsdWacomDevice */
83 GList *rr_screens;
84
85 /* button capture */
86 GSList *screens;
87 int opcode;
88
89 /* Help OSD window */
90 GtkWidget *osd_window;
91 };
92
93 static void csd_wacom_manager_class_init (CsdWacomManagerClass *klass);
94 static void csd_wacom_manager_init (CsdWacomManager *wacom_manager);
95 static void csd_wacom_manager_finalize (GObject *object);
96
97 G_DEFINE_TYPE (CsdWacomManager, csd_wacom_manager, G_TYPE_OBJECT)
98
99 static gpointer manager_object = NULL;
100
101 static GObject *
csd_wacom_manager_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)102 csd_wacom_manager_constructor (GType type,
103 guint n_construct_properties,
104 GObjectConstructParam *construct_properties)
105 {
106 CsdWacomManager *wacom_manager;
107
108 wacom_manager = CSD_WACOM_MANAGER (G_OBJECT_CLASS (csd_wacom_manager_parent_class)->constructor (type,
109 n_construct_properties,
110 construct_properties));
111
112 return G_OBJECT (wacom_manager);
113 }
114
115 static void
csd_wacom_manager_dispose(GObject * object)116 csd_wacom_manager_dispose (GObject *object)
117 {
118 G_OBJECT_CLASS (csd_wacom_manager_parent_class)->dispose (object);
119 }
120
121 static void
csd_wacom_manager_class_init(CsdWacomManagerClass * klass)122 csd_wacom_manager_class_init (CsdWacomManagerClass *klass)
123 {
124 GObjectClass *object_class = G_OBJECT_CLASS (klass);
125
126 object_class->constructor = csd_wacom_manager_constructor;
127 object_class->dispose = csd_wacom_manager_dispose;
128 object_class->finalize = csd_wacom_manager_finalize;
129
130 g_type_class_add_private (klass, sizeof (CsdWacomManagerPrivate));
131 }
132
133 static int
get_device_id(CsdWacomDevice * device)134 get_device_id (CsdWacomDevice *device)
135 {
136 GdkDevice *gdk_device;
137 int id;
138
139 g_object_get (device, "gdk-device", &gdk_device, NULL);
140 if (gdk_device == NULL)
141 return -1;
142 g_object_get (gdk_device, "device-id", &id, NULL);
143 return id;
144 }
145
146 static XDevice *
open_device(CsdWacomDevice * device)147 open_device (CsdWacomDevice *device)
148 {
149 XDevice *xdev;
150 int id;
151
152 id = get_device_id (device);
153 if (id < 0)
154 return NULL;
155
156 gdk_x11_display_error_trap_push (gdk_display_get_default ());
157 xdev = XOpenDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), id);
158 if (gdk_x11_display_error_trap_pop (gdk_display_get_default ()) || (xdev == NULL))
159 return NULL;
160
161 return xdev;
162 }
163
164
165 static void
wacom_set_property(CsdWacomDevice * device,PropertyHelper * property)166 wacom_set_property (CsdWacomDevice *device,
167 PropertyHelper *property)
168 {
169 XDevice *xdev;
170
171 xdev = open_device (device);
172 device_set_property (xdev, csd_wacom_device_get_tool_name (device), property);
173 xdevice_close (xdev);
174 }
175
176 static void
set_rotation(CsdWacomDevice * device,CsdWacomRotation rotation)177 set_rotation (CsdWacomDevice *device,
178 CsdWacomRotation rotation)
179 {
180 gchar rot = rotation;
181 PropertyHelper property = {
182 .name = "Wacom Rotation",
183 .nitems = 1,
184 .format = 8,
185 .type = XA_INTEGER,
186 .data.c = &rot,
187 };
188
189 wacom_set_property (device, &property);
190 }
191
192 static void
set_pressurecurve(CsdWacomDevice * device,GVariant * value)193 set_pressurecurve (CsdWacomDevice *device,
194 GVariant *value)
195 {
196 PropertyHelper property = {
197 .name = "Wacom Pressurecurve",
198 .nitems = 4,
199 .type = XA_INTEGER,
200 .format = 32,
201 };
202 gsize nvalues;
203
204 property.data.i = g_variant_get_fixed_array (value, &nvalues, sizeof (gint32));
205
206 if (nvalues != 4) {
207 g_error ("Pressurecurve requires 4 values.");
208 return;
209 }
210
211 wacom_set_property (device, &property);
212 g_variant_unref (value);
213 }
214
215 /* Area handling. Each area is defined as top x/y, bottom x/y and limits the
216 * usable area of the physical device to the given area (in device coords)
217 */
218 static void
set_area(CsdWacomDevice * device,GVariant * value)219 set_area (CsdWacomDevice *device,
220 GVariant *value)
221 {
222 PropertyHelper property = {
223 .name = "Wacom Tablet Area",
224 .nitems = 4,
225 .type = XA_INTEGER,
226 .format = 32,
227 };
228 gsize nvalues;
229
230 property.data.i = g_variant_get_fixed_array (value, &nvalues, sizeof (gint32));
231
232 if (nvalues != 4) {
233 g_error ("Area configuration requires 4 values.");
234 return;
235 }
236
237 wacom_set_property (device, &property);
238 g_variant_unref (value);
239 }
240
241 /* Returns the rotation to apply a device relative to the current rotation of the output */
242 static CsdWacomRotation
get_relative_rotation(CsdWacomRotation device_rotation,CsdWacomRotation output_rotation)243 get_relative_rotation (CsdWacomRotation device_rotation,
244 CsdWacomRotation output_rotation)
245 {
246 CsdWacomRotation rotations[] = { CSD_WACOM_ROTATION_HALF,
247 CSD_WACOM_ROTATION_CW,
248 CSD_WACOM_ROTATION_NONE,
249 CSD_WACOM_ROTATION_CCW };
250 guint i;
251
252 if (device_rotation == output_rotation)
253 return CSD_WACOM_ROTATION_NONE;
254
255 if (output_rotation == CSD_WACOM_ROTATION_NONE)
256 return device_rotation;
257
258 for (i = 0; i < G_N_ELEMENTS (rotations); i++){
259 if (device_rotation == rotations[i])
260 break;
261 }
262
263 if (output_rotation == CSD_WACOM_ROTATION_HALF)
264 return rotations[(i + G_N_ELEMENTS (rotations) - 2) % G_N_ELEMENTS (rotations)];
265
266 if (output_rotation == CSD_WACOM_ROTATION_CW)
267 return rotations[(i + G_N_ELEMENTS (rotations) - 1) % G_N_ELEMENTS (rotations)];
268
269 if (output_rotation == CSD_WACOM_ROTATION_CCW)
270 return rotations[(i + 1) % G_N_ELEMENTS (rotations)];
271
272 /* fallback */
273 return CSD_WACOM_ROTATION_NONE;
274 }
275
276 static void
set_display(CsdWacomDevice * device,GVariant * value)277 set_display (CsdWacomDevice *device,
278 GVariant *value)
279 {
280 CsdWacomRotation device_rotation;
281 CsdWacomRotation output_rotation;
282 GSettings *settings;
283 float matrix[NUM_ELEMS_MATRIX];
284 PropertyHelper property = {
285 .name = "Coordinate Transformation Matrix",
286 .nitems = NUM_ELEMS_MATRIX,
287 .format = 32,
288 .type = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "FLOAT", True),
289 };
290
291 csd_wacom_device_get_display_matrix (device, matrix);
292
293 property.data.i = (gint*)(&matrix);
294 g_debug ("Applying matrix to device...");
295 wacom_set_property (device, &property);
296
297 /* Apply display rotation to device */
298 settings = csd_wacom_device_get_settings (device);
299 if (g_settings_get_boolean (settings, KEY_KEEP_ROTATION)) {
300 device_rotation = g_settings_get_enum (settings, KEY_ROTATION);
301 output_rotation = csd_wacom_device_get_display_rotation (device);
302 set_rotation (device, get_relative_rotation (device_rotation, output_rotation));
303 }
304
305 g_variant_unref (value);
306 }
307
308 static void
set_absolute(CsdWacomDevice * device,gint is_absolute)309 set_absolute (CsdWacomDevice *device,
310 gint is_absolute)
311 {
312 XDevice *xdev;
313
314 xdev = open_device (device);
315 gdk_x11_display_error_trap_push (gdk_display_get_default ());
316 XSetDeviceMode (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdev, is_absolute ? Absolute : Relative);
317 if (gdk_x11_display_error_trap_pop (gdk_display_get_default ()))
318 g_error ("Failed to set mode \"%s\" for \"%s\".",
319 is_absolute ? "Absolute" : "Relative", csd_wacom_device_get_tool_name (device));
320 xdevice_close (xdev);
321 }
322
323 static void
compute_aspect_area(gint monitor,gint * area,CsdWacomRotation rotation)324 compute_aspect_area (gint monitor,
325 gint *area,
326 CsdWacomRotation rotation)
327 {
328 gint width = area[2] - area[0];
329 gint height = area[3] - area[1];
330 GdkScreen *screen;
331 GdkRectangle monitor_geometry;
332 float aspect;
333
334 screen = gdk_screen_get_default ();
335 if (monitor < 0) {
336 monitor_geometry.width = gdk_screen_get_width (screen);
337 monitor_geometry.height = gdk_screen_get_height (screen);
338 } else {
339 gdk_screen_get_monitor_geometry (screen, monitor, &monitor_geometry);
340 }
341
342 if (rotation == CSD_WACOM_ROTATION_CW || rotation == CSD_WACOM_ROTATION_CCW)
343 aspect = (float) monitor_geometry.height / (float) monitor_geometry.width;
344 else
345 aspect = (float) monitor_geometry.width / (float) monitor_geometry.height;
346
347 if ((float) width / (float) height > aspect)
348 width = height * aspect;
349 else
350 height = width / aspect;
351
352 switch (rotation)
353 {
354 case CSD_WACOM_ROTATION_NONE:
355 area[2] = area[0] + width;
356 area[3] = area[1] + height;
357 break;
358 case CSD_WACOM_ROTATION_CW:
359 area[0] = area[2] - width;
360 area[3] = area[1] + height;
361 break;
362 case CSD_WACOM_ROTATION_HALF:
363 area[0] = area[2] - width;
364 area[1] = area[3] - height;
365 break;
366 case CSD_WACOM_ROTATION_CCW:
367 area[2] = area[0] + width;
368 area[1] = area[3] - height;
369 break;
370 default:
371 break;
372 }
373 }
374
375 static void
set_keep_aspect(CsdWacomDevice * device,gboolean keep_aspect)376 set_keep_aspect (CsdWacomDevice *device,
377 gboolean keep_aspect)
378 {
379 GVariant *values[4], *variant;
380 guint i;
381
382 gint *area;
383 gint monitor = CSD_WACOM_SET_ALL_MONITORS;
384 CsdWacomRotation rotation;
385 GSettings *settings;
386
387 settings = csd_wacom_device_get_settings (device);
388
389 /* Set area to default values for the device */
390 for (i = 0; i < G_N_ELEMENTS (values); i++)
391 values[i] = g_variant_new_int32 (-1);
392 variant = g_variant_new_array (G_VARIANT_TYPE_INT32, values, G_N_ELEMENTS (values));
393
394 /* If keep_aspect is not set, just reset the area to default and let
395 * gsettings notification call set_area() for us...
396 */
397 if (!keep_aspect) {
398 g_settings_set_value (settings, KEY_AREA, variant);
399 return;
400 }
401
402 /* Reset the device area to get the default area */
403 set_area (device, variant);
404
405 /* Get current rotation */
406 rotation = g_settings_get_enum (settings, KEY_ROTATION);
407
408 /* Get current area */
409 area = csd_wacom_device_get_area (device);
410 if (!area) {
411 g_warning("Device area not available.\n");
412 return;
413 }
414
415 /* Get corresponding monitor size */
416 monitor = csd_wacom_device_get_display_monitor (device);
417
418 /* Adjust area to match the monitor aspect ratio */
419 g_debug ("Initial device area: (%d,%d) (%d,%d)", area[0], area[1], area[2], area[3]);
420 compute_aspect_area (monitor, area, rotation);
421 g_debug ("Adjusted device area: (%d,%d) (%d,%d)", area[0], area[1], area[2], area[3]);
422
423 for (i = 0; i < G_N_ELEMENTS (values); i++)
424 values[i] = g_variant_new_int32 (area[i]);
425 variant = g_variant_new_array (G_VARIANT_TYPE_INT32, values, G_N_ELEMENTS (values));
426 g_settings_set_value (settings, KEY_AREA, variant);
427
428 g_free (area);
429 }
430
431
432 static void
set_device_buttonmap(CsdWacomDevice * device,GVariant * value)433 set_device_buttonmap (CsdWacomDevice *device,
434 GVariant *value)
435 {
436 XDevice *xdev;
437 gsize nmap;
438 const gint *intmap;
439 unsigned char *map;
440 int i, j, rc;
441
442 xdev = open_device (device);
443
444 intmap = g_variant_get_fixed_array (value, &nmap, sizeof (gint32));
445 map = g_new0 (unsigned char, nmap);
446 for (i = 0; i < nmap; i++)
447 map[i] = intmap[i];
448 g_variant_unref (value);
449
450 gdk_x11_display_error_trap_push (gdk_display_get_default ());
451
452 /* X refuses to change the mapping while buttons are engaged,
453 * so if this is the case we'll retry a few times
454 */
455 for (j = 0;
456 j < 20 && (rc = XSetDeviceButtonMapping (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdev, map, nmap)) == MappingBusy;
457 ++j) {
458 g_usleep (300);
459 }
460
461 if ((gdk_x11_display_error_trap_pop (gdk_display_get_default ()) && rc != MappingSuccess) ||
462 rc != MappingSuccess)
463 g_warning ("Error in setting button mapping for \"%s\"", csd_wacom_device_get_tool_name (device));
464
465 g_free (map);
466
467 xdevice_close (xdev);
468 }
469
470 static void
set_touch(CsdWacomDevice * device,gboolean touch)471 set_touch (CsdWacomDevice *device,
472 gboolean touch)
473 {
474 gchar data = touch;
475 PropertyHelper property = {
476 .name = "Wacom Enable Touch",
477 .nitems = 1,
478 .format = 8,
479 .type = XA_INTEGER,
480 .data.c = &data,
481 };
482
483 wacom_set_property (device, &property);
484 }
485
486 static void
set_tpcbutton(CsdWacomDevice * device,gboolean tpcbutton)487 set_tpcbutton (CsdWacomDevice *device,
488 gboolean tpcbutton)
489 {
490 /* Wacom's TPCButton option which this setting emulates is to enable
491 * Tablet PC stylus behaviour when on. The property "Hover Click"
492 * works the other way round, i.e. if Hover Click is enabled this
493 * is the equivalent of TPC behaviour disabled. */
494 gchar data = tpcbutton ? 0 : 1;
495 PropertyHelper property = {
496 .name = "Wacom Hover Click",
497 .nitems = 1,
498 .format = 8,
499 .type = XA_INTEGER,
500 .data.c = &data,
501 };
502
503 wacom_set_property (device, &property);
504 }
505
506 static void
set_pressurethreshold(CsdWacomDevice * device,gint threshold)507 set_pressurethreshold (CsdWacomDevice *device,
508 gint threshold)
509 {
510 PropertyHelper property = {
511 .name = "Wacom Pressure Threshold",
512 .nitems = 1,
513 .format = 32,
514 .type = XA_INTEGER,
515 .data.i = &threshold,
516 };
517
518 wacom_set_property (device, &property);
519 }
520
521 static void
apply_stylus_settings(CsdWacomDevice * device)522 apply_stylus_settings (CsdWacomDevice *device)
523 {
524 GSettings *stylus_settings;
525 CsdWacomStylus *stylus;
526 int threshold;
527
528 g_object_get (device, "last-stylus", &stylus, NULL);
529 if (stylus == NULL) {
530 g_warning ("Last stylus is not set");
531 return;
532 }
533
534 g_debug ("Applying setting for stylus '%s' on device '%s'",
535 csd_wacom_stylus_get_name (stylus),
536 csd_wacom_device_get_name (device));
537
538 stylus_settings = csd_wacom_stylus_get_settings (stylus);
539 set_pressurecurve (device, g_settings_get_value (stylus_settings, KEY_PRESSURECURVE));
540 set_device_buttonmap (device, g_settings_get_value (stylus_settings, KEY_BUTTON_MAPPING));
541
542 threshold = g_settings_get_int (stylus_settings, KEY_PRESSURETHRESHOLD);
543 if (threshold == -1)
544 threshold = DEFAULT_PRESSURE_THRESHOLD;
545 set_pressurethreshold (device, threshold);
546 }
547
548 static void
set_led(CsdWacomDevice * device,CsdWacomTabletButton * button,int index)549 set_led (CsdWacomDevice *device,
550 CsdWacomTabletButton *button,
551 int index)
552 {
553 GError *error = NULL;
554 const char *path;
555 char *command;
556 gint status_led;
557 gboolean ret;
558
559 #ifndef HAVE_GUDEV
560 /* Not implemented on non-Linux systems */
561 return;
562 #endif
563 g_return_if_fail (index >= 1);
564
565 path = csd_wacom_device_get_path (device);
566 status_led = button->status_led;
567
568 if (status_led == CSD_WACOM_NO_LED) {
569 g_debug ("Ignoring unhandled group ID %d for device %s",
570 button->group_id, csd_wacom_device_get_name (device));
571 return;
572 }
573 g_debug ("Switching group ID %d to index %d for device %s", button->group_id, index, path);
574
575 command = g_strdup_printf ("pkexec " LIBEXECDIR "/csd-wacom-led-helper --path %s --group %d --led %d",
576 path, status_led, index - 1);
577 ret = g_spawn_command_line_sync (command,
578 NULL,
579 NULL,
580 NULL,
581 &error);
582
583 if (ret == FALSE) {
584 g_debug ("Failed to launch '%s': %s", command, error->message);
585 g_error_free (error);
586 }
587
588 g_free (command);
589 }
590
591 struct DefaultButtons {
592 const char *button;
593 int num;
594 };
595
596 struct DefaultButtons def_touchrings_buttons[] = {
597 /* Touchrings */
598 { "AbsWheelUp", 90 },
599 { "AbsWheelDown", 91 },
600 { "RelWheelUp", 90 },
601 { "RelWheelDown", 91 },
602 { "AbsWheel2Up", 92 },
603 { "AbsWheel2Down", 93 },
604 { NULL, 0 }
605 };
606
607 struct DefaultButtons def_touchstrip_buttons[] = {
608 /* Touchstrips */
609 { "StripLeftUp", 94 },
610 { "StripLeftDown", 95 },
611 { "StripRightUp", 96 },
612 { "StripRightDown", 97 },
613 { NULL, 0 }
614 };
615
616 static void
reset_touch_buttons(XDevice * xdev,struct DefaultButtons * buttons,const char * device_property)617 reset_touch_buttons (XDevice *xdev,
618 struct DefaultButtons *buttons,
619 const char *device_property)
620 {
621 Atom actions[6];
622 Atom action_prop;
623 guint i;
624
625 /* Create a device property with the action for button i */
626 for (i = 0; buttons[i].button != NULL; i++)
627 {
628 char *propname;
629 glong action[2]; /* press + release */
630 Atom prop;
631 int mapped_button = buttons[i].num;
632
633 action[0] = AC_BUTTON | AC_KEYBTNPRESS | mapped_button;
634 action[1] = AC_BUTTON | mapped_button;
635
636 propname = g_strdup_printf ("Button %s action", buttons[i].button);
637 prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), propname, False);
638 g_free (propname);
639 XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdev,
640 prop, XA_INTEGER, 32, PropModeReplace,
641 (const guchar *) &action, 2);
642
643 /* prop now contains press + release for the mapped button */
644 actions[i] = prop;
645 }
646
647 /* Now set the actual action property to contain references to the various
648 * actions */
649 action_prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device_property, True);
650 XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdev,
651 action_prop, XA_ATOM, 32, PropModeReplace,
652 (const guchar *) actions, i);
653 }
654
655 static void
reset_pad_buttons(CsdWacomDevice * device)656 reset_pad_buttons (CsdWacomDevice *device)
657 {
658 XDevice *xdev;
659 int nmap;
660 unsigned char *map;
661 int i, j, rc;
662 GList *buttons, *l;
663
664 /* Normal buttons */
665 xdev = open_device (device);
666
667 gdk_x11_display_error_trap_push (gdk_display_get_default ());
668
669 nmap = 256;
670 map = g_new0 (unsigned char, nmap);
671 for (i = 0; i < nmap && i < sizeof (map); i++)
672 map[i] = i + 1;
673
674 /* X refuses to change the mapping while buttons are engaged,
675 * so if this is the case we'll retry a few times */
676 for (j = 0;
677 j < 20 && (rc = XSetDeviceButtonMapping (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdev, map, nmap)) == MappingBusy;
678 ++j) {
679 g_usleep (300);
680 }
681
682 if ((gdk_x11_display_error_trap_pop (gdk_display_get_default ()) && rc != MappingSuccess) ||
683 rc != MappingSuccess)
684 g_warning ("Error in resetting button mapping for \"%s\" (rc=%d)", csd_wacom_device_get_tool_name (device), rc);
685
686 g_free (map);
687
688 gdk_x11_display_error_trap_push (gdk_display_get_default ());
689 reset_touch_buttons (xdev, def_touchrings_buttons, "Wacom Wheel Buttons");
690 reset_touch_buttons (xdev, def_touchstrip_buttons, "Wacom Strip Buttons");
691 gdk_x11_display_error_trap_pop_ignored (gdk_display_get_default ());
692
693 xdevice_close (xdev);
694
695 /* Reset all the LEDs */
696 buttons = csd_wacom_device_get_buttons (device);
697 for (l = buttons; l != NULL; l = l->next) {
698 CsdWacomTabletButton *button = l->data;
699 if (button->type == WACOM_TABLET_BUTTON_TYPE_HARDCODED &&
700 button->status_led != CSD_WACOM_NO_LED) {
701 set_led (device, button, 1);
702 }
703 }
704 g_list_free (buttons);
705 }
706
707 static void
set_wacom_settings(CsdWacomManager * manager,CsdWacomDevice * device)708 set_wacom_settings (CsdWacomManager *manager,
709 CsdWacomDevice *device)
710 {
711 CsdWacomDeviceType type;
712 GSettings *settings;
713
714 g_debug ("Applying settings for device '%s' (type: %s)",
715 csd_wacom_device_get_tool_name (device),
716 csd_wacom_device_type_to_string (csd_wacom_device_get_device_type (device)));
717
718 settings = csd_wacom_device_get_settings (device);
719 set_rotation (device, g_settings_get_enum (settings, KEY_ROTATION));
720 set_touch (device, g_settings_get_boolean (settings, KEY_TOUCH));
721
722 type = csd_wacom_device_get_device_type (device);
723
724 if (type == WACOM_TYPE_TOUCH &&
725 csd_wacom_device_is_screen_tablet (device) == FALSE) {
726 set_absolute (device, FALSE);
727 return;
728 }
729
730 if (type == WACOM_TYPE_CURSOR) {
731 GVariant *values[4], *variant;
732 guint i;
733
734 set_absolute (device, FALSE);
735
736 for (i = 0; i < G_N_ELEMENTS (values); i++)
737 values[i] = g_variant_new_int32 (-1);
738
739 variant = g_variant_new_array (G_VARIANT_TYPE_INT32, values, G_N_ELEMENTS (values));
740 set_area (device, variant);
741 return;
742 }
743
744 if (type == WACOM_TYPE_PAD) {
745 int id;
746
747 id = get_device_id (device);
748 reset_pad_buttons (device);
749 grab_button (id, TRUE, manager->priv->screens);
750 return;
751 }
752
753 if (type == WACOM_TYPE_STYLUS)
754 set_tpcbutton (device, g_settings_get_boolean (settings, KEY_TPCBUTTON));
755
756 set_absolute (device, g_settings_get_boolean (settings, KEY_IS_ABSOLUTE));
757
758 /* Ignore touch devices as they do not share the same range of values for area */
759 if (type != WACOM_TYPE_TOUCH) {
760 if (csd_wacom_device_is_screen_tablet (device) == FALSE)
761 set_keep_aspect (device, g_settings_get_boolean (settings, KEY_KEEP_ASPECT));
762 set_area (device, g_settings_get_value (settings, KEY_AREA));
763 }
764 set_display (device, g_settings_get_value (settings, KEY_DISPLAY));
765
766 /* only pen and eraser have pressure threshold and curve settings */
767 if (type == WACOM_TYPE_STYLUS ||
768 type == WACOM_TYPE_ERASER) {
769 apply_stylus_settings (device);
770 }
771 }
772
773 static void
wacom_settings_changed(GSettings * settings,gchar * key,CsdWacomDevice * device)774 wacom_settings_changed (GSettings *settings,
775 gchar *key,
776 CsdWacomDevice *device)
777 {
778 CsdWacomDeviceType type;
779
780 type = csd_wacom_device_get_device_type (device);
781
782 if (g_str_equal (key, KEY_ROTATION)) {
783 if (type != WACOM_TYPE_PAD)
784 set_rotation (device, g_settings_get_enum (settings, key));
785 } else if (g_str_equal (key, KEY_TOUCH)) {
786 set_touch (device, g_settings_get_boolean (settings, key));
787 } else if (g_str_equal (key, KEY_TPCBUTTON)) {
788 set_tpcbutton (device, g_settings_get_boolean (settings, key));
789 } else if (g_str_equal (key, KEY_IS_ABSOLUTE)) {
790 if (type != WACOM_TYPE_CURSOR &&
791 type != WACOM_TYPE_PAD &&
792 type != WACOM_TYPE_TOUCH)
793 set_absolute (device, g_settings_get_boolean (settings, key));
794 } else if (g_str_equal (key, KEY_AREA)) {
795 if (type != WACOM_TYPE_CURSOR &&
796 type != WACOM_TYPE_PAD &&
797 type != WACOM_TYPE_TOUCH)
798 set_area (device, g_settings_get_value (settings, key));
799 } else if (g_str_equal (key, KEY_DISPLAY)) {
800 if (type != WACOM_TYPE_CURSOR &&
801 type != WACOM_TYPE_PAD)
802 set_display (device, g_settings_get_value (settings, key));
803 } else if (g_str_equal (key, KEY_KEEP_ASPECT)) {
804 if (type != WACOM_TYPE_CURSOR &&
805 type != WACOM_TYPE_PAD &&
806 type != WACOM_TYPE_TOUCH &&
807 !csd_wacom_device_is_screen_tablet (device))
808 set_keep_aspect (device, g_settings_get_boolean (settings, key));
809 } else {
810 g_warning ("Unhandled tablet-wide setting '%s' changed", key);
811 }
812 }
813
814 static void
stylus_settings_changed(GSettings * settings,gchar * key,CsdWacomStylus * stylus)815 stylus_settings_changed (GSettings *settings,
816 gchar *key,
817 CsdWacomStylus *stylus)
818 {
819 CsdWacomDevice *device;
820 CsdWacomStylus *last_stylus;
821
822 device = csd_wacom_stylus_get_device (stylus);
823
824 g_object_get (device, "last-stylus", &last_stylus, NULL);
825 if (last_stylus != stylus) {
826 g_debug ("Not applying changed settings because '%s' is the current stylus, not '%s'",
827 last_stylus ? csd_wacom_stylus_get_name (last_stylus) : "NONE",
828 csd_wacom_stylus_get_name (stylus));
829 return;
830 }
831
832 if (g_str_equal (key, KEY_PRESSURECURVE)) {
833 set_pressurecurve (device, g_settings_get_value (settings, key));
834 } else if (g_str_equal (key, KEY_PRESSURETHRESHOLD)) {
835 int threshold;
836
837 threshold = g_settings_get_int (settings, KEY_PRESSURETHRESHOLD);
838 if (threshold == -1)
839 threshold = DEFAULT_PRESSURE_THRESHOLD;
840 set_pressurethreshold (device, threshold);
841 } else if (g_str_equal (key, KEY_BUTTON_MAPPING)) {
842 set_device_buttonmap (device, g_settings_get_value (settings, key));
843 } else {
844 g_warning ("Unhandled stylus setting '%s' changed", key);
845 }
846 }
847
848 static void
last_stylus_changed(CsdWacomDevice * device,GParamSpec * pspec,CsdWacomManager * manager)849 last_stylus_changed (CsdWacomDevice *device,
850 GParamSpec *pspec,
851 CsdWacomManager *manager)
852 {
853 g_debug ("Stylus for device '%s' changed, applying settings",
854 csd_wacom_device_get_name (device));
855 apply_stylus_settings (device);
856 }
857
858 static void
osd_window_destroy(CsdWacomManager * manager)859 osd_window_destroy (CsdWacomManager *manager)
860 {
861 g_return_if_fail (manager != NULL);
862
863 g_clear_pointer (&manager->priv->osd_window, gtk_widget_destroy);
864 }
865
866 static gboolean
osd_window_on_key_release_event(GtkWidget * widget,GdkEventKey * event,CsdWacomManager * manager)867 osd_window_on_key_release_event (GtkWidget *widget,
868 GdkEventKey *event,
869 CsdWacomManager *manager)
870 {
871
872 if (event->type != GDK_KEY_RELEASE)
873 return FALSE;
874 if (event->keyval != GDK_KEY_Escape)
875 return FALSE;
876
877 osd_window_destroy (manager);
878
879 return FALSE;
880 }
881
882 static gboolean
osd_window_on_focus_out_event(GtkWidget * widget,GdkEvent * event,CsdWacomManager * manager)883 osd_window_on_focus_out_event (GtkWidget *widget,
884 GdkEvent *event,
885 CsdWacomManager *manager)
886 {
887 /* If the OSD window loses focus, hide it */
888 osd_window_destroy (manager);
889
890 return FALSE;
891 }
892
893 static gboolean
osd_window_toggle_visibility(CsdWacomManager * manager,CsdWacomDevice * device)894 osd_window_toggle_visibility (CsdWacomManager *manager,
895 CsdWacomDevice *device)
896 {
897 GtkWidget *widget;
898 const gchar *layout_path;
899
900 if (manager->priv->osd_window) {
901 osd_window_destroy (manager);
902 return FALSE;
903 }
904
905 layout_path = csd_wacom_device_get_layout_path (device);
906 if (layout_path == NULL) {
907 g_warning ("Cannot display the on-screen help window as the tablet "
908 "definition for %s is missing the layout\n"
909 "Please consider contributing the layout for your "
910 "tablet to libwacom at linuxwacom-devel@lists.sourceforge.net\n",
911 csd_wacom_device_get_name (device));
912 return FALSE;
913 }
914
915 if (g_file_test (layout_path, G_FILE_TEST_EXISTS) == FALSE) {
916 g_warning ("Cannot display the on-screen help window as the "
917 "layout file %s cannot be found on disk\n"
918 "Please check your libwacom installation\n",
919 layout_path);
920 return FALSE;
921 }
922
923 widget = csd_wacom_osd_window_new (device, NULL);
924
925 /* Connect some signals to the OSD window */
926 g_signal_connect (widget, "key-release-event",
927 G_CALLBACK(osd_window_on_key_release_event), manager);
928 g_signal_connect (widget, "focus-out-event",
929 G_CALLBACK(osd_window_on_focus_out_event), manager);
930 g_object_add_weak_pointer (G_OBJECT (widget), (gpointer *) &manager->priv->osd_window);
931
932 gtk_window_present (GTK_WINDOW(widget));
933 manager->priv->osd_window = widget;
934
935 return TRUE;
936 }
937
938 static gboolean
osd_window_update_viewable(CsdWacomManager * manager,CsdWacomTabletButton * button,GtkDirectionType dir,XIEvent * xiev)939 osd_window_update_viewable (CsdWacomManager *manager,
940 CsdWacomTabletButton *button,
941 GtkDirectionType dir,
942 XIEvent *xiev)
943 {
944 if (manager->priv->osd_window == NULL)
945 return FALSE;
946
947 csd_wacom_osd_window_set_active (CSD_WACOM_OSD_WINDOW (manager->priv->osd_window),
948 button,
949 dir,
950 xiev->evtype == XI_ButtonPress);
951
952 return TRUE;
953 }
954
955 static void
device_added_cb(GdkDeviceManager * device_manager,GdkDevice * gdk_device,CsdWacomManager * manager)956 device_added_cb (GdkDeviceManager *device_manager,
957 GdkDevice *gdk_device,
958 CsdWacomManager *manager)
959 {
960 CsdWacomDevice *device;
961 GSettings *settings;
962
963 device = csd_wacom_device_new (gdk_device);
964 if (csd_wacom_device_get_device_type (device) == WACOM_TYPE_INVALID) {
965 g_object_unref (device);
966 return;
967 }
968 g_debug ("Adding device '%s' (type: '%s') to known devices list",
969 csd_wacom_device_get_tool_name (device),
970 csd_wacom_device_type_to_string (csd_wacom_device_get_device_type (device)));
971 g_hash_table_insert (manager->priv->devices, (gpointer) gdk_device, device);
972
973 settings = csd_wacom_device_get_settings (device);
974 g_signal_connect (G_OBJECT (settings), "changed",
975 G_CALLBACK (wacom_settings_changed), device);
976
977 if (csd_wacom_device_get_device_type (device) == WACOM_TYPE_STYLUS ||
978 csd_wacom_device_get_device_type (device) == WACOM_TYPE_ERASER) {
979 GList *styli, *l;
980
981 styli = csd_wacom_device_list_styli (device);
982
983 for (l = styli ; l ; l = l->next) {
984 settings = csd_wacom_stylus_get_settings (l->data);
985 g_signal_connect (G_OBJECT (settings), "changed",
986 G_CALLBACK (stylus_settings_changed), l->data);
987 }
988
989 g_list_free (styli);
990
991 g_signal_connect (G_OBJECT (device), "notify::last-stylus",
992 G_CALLBACK (last_stylus_changed), manager);
993 }
994
995 set_wacom_settings (manager, device);
996 }
997
998 static void
device_removed_cb(GdkDeviceManager * device_manager,GdkDevice * gdk_device,CsdWacomManager * manager)999 device_removed_cb (GdkDeviceManager *device_manager,
1000 GdkDevice *gdk_device,
1001 CsdWacomManager *manager)
1002 {
1003 g_debug ("Removing device '%s' from known devices list",
1004 gdk_device_get_name (gdk_device));
1005 g_hash_table_remove (manager->priv->devices, gdk_device);
1006
1007 /* Enable this chunk of code if you want to valgrind
1008 * test-wacom. It will exit when there are no Wacom devices left */
1009 #if 0
1010 if (g_hash_table_size (manager->priv->devices) == 0)
1011 gtk_main_quit ();
1012 #endif
1013 }
1014
1015 static CsdWacomDevice *
device_id_to_device(CsdWacomManager * manager,int deviceid)1016 device_id_to_device (CsdWacomManager *manager,
1017 int deviceid)
1018 {
1019 GList *devices, *l;
1020 CsdWacomDevice *ret;
1021
1022 ret = NULL;
1023 devices = g_hash_table_get_keys (manager->priv->devices);
1024
1025 for (l = devices; l != NULL; l = l->next) {
1026 GdkDevice *device = l->data;
1027 int id;
1028
1029 g_object_get (device, "device-id", &id, NULL);
1030 if (id == deviceid) {
1031 ret = g_hash_table_lookup (manager->priv->devices, device);
1032 break;
1033 }
1034 }
1035
1036 g_list_free (devices);
1037 return ret;
1038 }
1039
1040 struct {
1041 guint mask;
1042 KeySym keysym;
1043 } mods_keysyms[] = {
1044 { GDK_MOD1_MASK, XK_Alt_L },
1045 { GDK_SHIFT_MASK, XK_Shift_L },
1046 { GDK_CONTROL_MASK, XK_Control_L },
1047 };
1048
1049 static void
send_modifiers(Display * display,guint mask,gboolean is_press)1050 send_modifiers (Display *display,
1051 guint mask,
1052 gboolean is_press)
1053 {
1054 guint i;
1055
1056 if (mask == 0)
1057 return;
1058
1059 for (i = 0; i < G_N_ELEMENTS(mods_keysyms); i++) {
1060 if (mask & mods_keysyms[i].mask) {
1061 guint keycode;
1062
1063 keycode = XKeysymToKeycode (display, mods_keysyms[i].keysym);
1064 XTestFakeKeyEvent (display, keycode,
1065 is_press ? True : False, 0);
1066 }
1067 }
1068 }
1069
1070 static char *
get_elevator_shortcut_string(GSettings * settings,GtkDirectionType dir)1071 get_elevator_shortcut_string (GSettings *settings,
1072 GtkDirectionType dir)
1073 {
1074 char **strv, *str;
1075
1076 strv = g_settings_get_strv (settings, KEY_CUSTOM_ELEVATOR_ACTION);
1077 if (strv == NULL)
1078 return NULL;
1079
1080 if (g_strv_length (strv) >= 1 && dir == GTK_DIR_UP)
1081 str = g_strdup (strv[0]);
1082 else if (g_strv_length (strv) >= 2 && dir == GTK_DIR_DOWN)
1083 str = g_strdup (strv[1]);
1084 else
1085 str = NULL;
1086
1087 g_strfreev (strv);
1088
1089 return str;
1090 }
1091
1092 static void
generate_key(CsdWacomTabletButton * wbutton,int group,Display * display,GtkDirectionType dir,gboolean is_press)1093 generate_key (CsdWacomTabletButton *wbutton,
1094 int group,
1095 Display *display,
1096 GtkDirectionType dir,
1097 gboolean is_press)
1098 {
1099 char *str;
1100 guint keyval;
1101 guint *keycodes;
1102 guint keycode;
1103 guint mods;
1104 GdkKeymapKey *keys;
1105 int n_keys;
1106 guint i;
1107
1108 if (wbutton->type == WACOM_TABLET_BUTTON_TYPE_STRIP ||
1109 wbutton->type == WACOM_TABLET_BUTTON_TYPE_RING)
1110 str = get_elevator_shortcut_string (wbutton->settings, dir);
1111 else
1112 str = g_settings_get_string (wbutton->settings, KEY_CUSTOM_ACTION);
1113
1114 if (str == NULL)
1115 return;
1116
1117 gtk_accelerator_parse_with_keycode (str, &keyval, &keycodes, &mods);
1118 if (keycodes == NULL) {
1119 g_warning ("Failed to find a keycode for shortcut '%s'", str);
1120 g_free (str);
1121 return;
1122 }
1123 g_free (keycodes);
1124
1125 /* Now look for our own keycode, in the group as us */
1126 if (!gdk_keymap_get_entries_for_keyval (gdk_keymap_get_default (), keyval, &keys, &n_keys)) {
1127 g_warning ("Failed to find a keycode for keyval '%s' (0x%x)", gdk_keyval_name (keyval), keyval);
1128 g_free (str);
1129 return;
1130 }
1131
1132 keycode = 0;
1133 for (i = 0; i < n_keys; i++) {
1134 if (keys[i].group != group)
1135 continue;
1136 if (keys[i].level > 0)
1137 continue;
1138 keycode = keys[i].keycode;
1139 }
1140 /* Couldn't find it in the current group? Look in group 0 */
1141 if (keycode == 0) {
1142 for (i = 0; i < n_keys; i++) {
1143 if (keys[i].group > 0)
1144 continue;
1145 keycode = keys[i].keycode;
1146 }
1147 }
1148 g_free (keys);
1149
1150 if (keycode == 0) {
1151 g_warning ("Not emitting '%s' (keyval: %d, keycode: %d mods: 0x%x), invalid keycode",
1152 str, keyval, keycode, mods);
1153 g_free (str);
1154 return;
1155 } else {
1156 g_debug ("Emitting '%s' (keyval: %d, keycode: %d mods: 0x%x)",
1157 str, keyval, keycode, mods);
1158 }
1159
1160 /* And send out the keys! */
1161 gdk_x11_display_error_trap_push (gdk_display_get_default ());
1162 if (is_press)
1163 send_modifiers (display, mods, TRUE);
1164 XTestFakeKeyEvent (display, keycode,
1165 is_press ? True : False, 0);
1166 if (is_press == FALSE)
1167 send_modifiers (display, mods, FALSE);
1168 if (gdk_x11_display_error_trap_pop (gdk_display_get_default ()))
1169 g_warning ("Failed to generate fake key event '%s'", str);
1170
1171 g_free (str);
1172 }
1173
1174 static void
switch_monitor(CsdWacomDevice * device)1175 switch_monitor (CsdWacomDevice *device)
1176 {
1177 gint current_monitor, n_monitors;
1178
1179 /* We don't; do that for screen tablets, sorry... */
1180 if (csd_wacom_device_is_screen_tablet (device))
1181 return;
1182
1183 n_monitors = gdk_screen_get_n_monitors (gdk_screen_get_default ());
1184
1185 /* There's no point in switching if there just one monitor */
1186 if (n_monitors < 2)
1187 return;
1188
1189 current_monitor = csd_wacom_device_get_display_monitor (device);
1190
1191 /* Select next monitor */
1192 current_monitor++;
1193
1194 if (current_monitor >= n_monitors)
1195 current_monitor = CSD_WACOM_SET_ALL_MONITORS;
1196
1197 csd_wacom_device_set_display (device, current_monitor);
1198 }
1199
1200 static const char*
get_direction_name(CsdWacomTabletButtonType type,GtkDirectionType dir)1201 get_direction_name (CsdWacomTabletButtonType type,
1202 GtkDirectionType dir)
1203 {
1204 if (type == WACOM_TABLET_BUTTON_TYPE_RING)
1205 return (dir == GTK_DIR_UP ? " 'CCW'" : " 'CW'");
1206
1207 if (type == WACOM_TABLET_BUTTON_TYPE_STRIP)
1208 return (dir == GTK_DIR_UP ? " 'up'" : " 'down'");
1209
1210 return "";
1211 }
1212
1213 static GdkFilterReturn
filter_button_events(XEvent * xevent,GdkEvent * event,CsdWacomManager * manager)1214 filter_button_events (XEvent *xevent,
1215 GdkEvent *event,
1216 CsdWacomManager *manager)
1217 {
1218 XIEvent *xiev;
1219 XIDeviceEvent *xev;
1220 XGenericEventCookie *cookie;
1221 guint deviceid;
1222 CsdWacomDevice *device;
1223 int button;
1224 CsdWacomTabletButton *wbutton;
1225 GtkDirectionType dir;
1226 gboolean emulate;
1227
1228 /* verify we have a key event */
1229 if (xevent->type != GenericEvent)
1230 return GDK_FILTER_CONTINUE;
1231 cookie = &xevent->xcookie;
1232 if (cookie->extension != manager->priv->opcode)
1233 return GDK_FILTER_CONTINUE;
1234
1235 xiev = (XIEvent *) xevent->xcookie.data;
1236
1237 if (xiev->evtype != XI_ButtonRelease &&
1238 xiev->evtype != XI_ButtonPress)
1239 return GDK_FILTER_CONTINUE;
1240
1241 xev = (XIDeviceEvent *) xiev;
1242
1243 deviceid = xev->sourceid;
1244 device = device_id_to_device (manager, deviceid);
1245 if (csd_wacom_device_get_device_type (device) != WACOM_TYPE_PAD)
1246 return GDK_FILTER_CONTINUE;
1247
1248 if ((manager->priv->osd_window != NULL) &&
1249 (device != csd_wacom_osd_window_get_device (CSD_WACOM_OSD_WINDOW(manager->priv->osd_window))))
1250 /* This is a button event from another device while showing OSD window */
1251 osd_window_destroy (manager);
1252
1253 button = xev->detail;
1254
1255 wbutton = csd_wacom_device_get_button (device, button, &dir);
1256 if (wbutton == NULL) {
1257 g_warning ("Could not find matching button for '%d' on '%s'",
1258 button, csd_wacom_device_get_name (device));
1259 return GDK_FILTER_CONTINUE;
1260 }
1261
1262 g_debug ("Received event button %s '%s'%s ('%d') on device '%s' ('%d')",
1263 xiev->evtype == XI_ButtonPress ? "press" : "release",
1264 wbutton->id,
1265 get_direction_name (wbutton->type, dir),
1266 button,
1267 csd_wacom_device_get_name (device),
1268 deviceid);
1269
1270 if (wbutton->type == WACOM_TABLET_BUTTON_TYPE_HARDCODED) {
1271 int new_mode;
1272
1273 /* We switch modes on key press */
1274 if (xiev->evtype == XI_ButtonRelease) {
1275 osd_window_update_viewable (manager, wbutton, dir, xiev);
1276 return GDK_FILTER_REMOVE;
1277 }
1278 new_mode = csd_wacom_device_set_next_mode (device, wbutton);
1279 if (manager->priv->osd_window != NULL) {
1280 csd_wacom_osd_window_set_mode (CSD_WACOM_OSD_WINDOW(manager->priv->osd_window), wbutton->group_id, new_mode);
1281 osd_window_update_viewable (manager, wbutton, dir, xiev);
1282 }
1283 set_led (device, wbutton, new_mode);
1284 return GDK_FILTER_REMOVE;
1285 }
1286
1287 /* Update OSD window if shown */
1288 emulate = osd_window_update_viewable (manager, wbutton, dir, xiev);
1289
1290 /* Nothing to do */
1291 if (g_settings_get_enum (wbutton->settings, KEY_ACTION_TYPE) == CSD_WACOM_ACTION_TYPE_NONE)
1292 return GDK_FILTER_REMOVE;
1293
1294 /* Show OSD window when requested */
1295 if (g_settings_get_enum (wbutton->settings, KEY_ACTION_TYPE) == CSD_WACOM_ACTION_TYPE_HELP) {
1296 if (xiev->evtype == XI_ButtonRelease)
1297 osd_window_toggle_visibility (manager, device);
1298 return GDK_FILTER_REMOVE;
1299 }
1300
1301 if (emulate)
1302 return GDK_FILTER_REMOVE;
1303
1304 /* Switch monitor */
1305 if (g_settings_get_enum (wbutton->settings, KEY_ACTION_TYPE) == CSD_WACOM_ACTION_TYPE_SWITCH_MONITOR) {
1306 if (xiev->evtype == XI_ButtonRelease)
1307 switch_monitor (device);
1308 return GDK_FILTER_REMOVE;
1309 }
1310
1311 /* Send a key combination out */
1312 generate_key (wbutton, xev->group.effective, xev->display, dir, xiev->evtype == XI_ButtonPress ? True : False);
1313
1314 return GDK_FILTER_REMOVE;
1315 }
1316
1317 static void
set_devicepresence_handler(CsdWacomManager * manager)1318 set_devicepresence_handler (CsdWacomManager *manager)
1319 {
1320 GdkDeviceManager *device_manager;
1321
1322 device_manager = gdk_display_get_device_manager (gdk_display_get_default ());
1323 if (device_manager == NULL)
1324 return;
1325
1326 manager->priv->device_added_id = g_signal_connect (G_OBJECT (device_manager), "device-added",
1327 G_CALLBACK (device_added_cb), manager);
1328 manager->priv->device_removed_id = g_signal_connect (G_OBJECT (device_manager), "device-removed",
1329 G_CALLBACK (device_removed_cb), manager);
1330 manager->priv->device_manager = device_manager;
1331 }
1332
1333 static void
csd_wacom_manager_init(CsdWacomManager * manager)1334 csd_wacom_manager_init (CsdWacomManager *manager)
1335 {
1336 manager->priv = CSD_WACOM_MANAGER_GET_PRIVATE (manager);
1337 }
1338
1339 static gboolean
csd_wacom_manager_idle_cb(CsdWacomManager * manager)1340 csd_wacom_manager_idle_cb (CsdWacomManager *manager)
1341 {
1342 GList *devices, *l;
1343 GSList *ls;
1344
1345 cinnamon_settings_profile_start (NULL);
1346
1347 manager->priv->devices = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
1348
1349 set_devicepresence_handler (manager);
1350
1351 devices = gdk_device_manager_list_devices (manager->priv->device_manager, GDK_DEVICE_TYPE_SLAVE);
1352 for (l = devices; l ; l = l->next)
1353 device_added_cb (manager->priv->device_manager, l->data, manager);
1354 g_list_free (devices);
1355
1356 /* Start filtering the button events */
1357 for (ls = manager->priv->screens; ls != NULL; ls = ls->next) {
1358 gdk_window_add_filter (gdk_screen_get_root_window (ls->data),
1359 (GdkFilterFunc) filter_button_events,
1360 manager);
1361 }
1362
1363 cinnamon_settings_profile_end (NULL);
1364
1365 manager->priv->start_idle_id = 0;
1366
1367 return FALSE;
1368 }
1369
1370 /*
1371 * The monitors-changed signal is emitted when the number, size or
1372 * position of the monitors attached to the screen change.
1373 */
1374 static void
on_screen_changed_cb(GnomeRRScreen * rr_screen,CsdWacomManager * manager)1375 on_screen_changed_cb (GnomeRRScreen *rr_screen,
1376 CsdWacomManager *manager)
1377 {
1378 GList *devices, *l;
1379
1380 /*
1381 * A ::changed signal may be received at startup before
1382 * the devices get added, in this case simply ignore the
1383 * notification
1384 */
1385 if (manager->priv->devices == NULL)
1386 return;
1387
1388 g_debug ("Screen configuration changed");
1389 devices = g_hash_table_get_values (manager->priv->devices);
1390 for (l = devices; l != NULL; l = l->next) {
1391 CsdWacomDevice *device = l->data;
1392 CsdWacomDeviceType type;
1393 GSettings *settings;
1394
1395 type = csd_wacom_device_get_device_type (device);
1396 if (type == WACOM_TYPE_CURSOR || type == WACOM_TYPE_PAD)
1397 continue;
1398
1399 settings = csd_wacom_device_get_settings (device);
1400 /* Ignore touch devices as they do not share the same range of values for area */
1401 if (type != WACOM_TYPE_TOUCH) {
1402 if (csd_wacom_device_is_screen_tablet (device) == FALSE)
1403 set_keep_aspect (device, g_settings_get_boolean (settings, KEY_KEEP_ASPECT));
1404 set_area (device, g_settings_get_value (settings, KEY_AREA));
1405 }
1406 set_display (device, g_settings_get_value (settings, KEY_DISPLAY));
1407 }
1408 g_list_free (devices);
1409 }
1410
1411 static void
init_screens(CsdWacomManager * manager)1412 init_screens (CsdWacomManager *manager)
1413 {
1414 GdkDisplay *display;
1415 int i;
1416
1417 display = gdk_display_get_default ();
1418 for (i = 0; i < gdk_display_get_n_screens (display); i++) {
1419 GError *error = NULL;
1420 GdkScreen *screen;
1421 GnomeRRScreen *rr_screen;
1422
1423 screen = gdk_display_get_screen (display, i);
1424 if (screen == NULL) {
1425 continue;
1426 }
1427 manager->priv->screens = g_slist_append (manager->priv->screens, screen);
1428
1429 /*
1430 * We also keep a list of GnomeRRScreen to monitor changes such as rotation
1431 * which are not reported by Gdk's "monitors-changed" callback
1432 */
1433 rr_screen = gnome_rr_screen_new (screen, &error);
1434 if (rr_screen == NULL) {
1435 g_warning ("Failed to create GnomeRRScreen: %s", error->message);
1436 g_error_free (error);
1437 continue;
1438 }
1439 manager->priv->rr_screens = g_list_prepend (manager->priv->rr_screens, rr_screen);
1440 g_signal_connect (rr_screen,
1441 "changed",
1442 G_CALLBACK (on_screen_changed_cb),
1443 manager);
1444 }
1445 }
1446
1447 gboolean
csd_wacom_manager_start(CsdWacomManager * manager,GError ** error)1448 csd_wacom_manager_start (CsdWacomManager *manager,
1449 GError **error)
1450 {
1451 cinnamon_settings_profile_start (NULL);
1452
1453 if (supports_xinput2_devices (&manager->priv->opcode) == FALSE) {
1454 g_debug ("No Xinput2 support, disabling plugin");
1455 return TRUE;
1456 }
1457
1458 if (supports_xtest () == FALSE) {
1459 g_debug ("No XTest extension support, disabling plugin");
1460 return TRUE;
1461 }
1462
1463 init_screens (manager);
1464
1465 manager->priv->start_idle_id = g_idle_add ((GSourceFunc) csd_wacom_manager_idle_cb, manager);
1466
1467 cinnamon_settings_profile_end (NULL);
1468
1469 return TRUE;
1470 }
1471
1472 void
csd_wacom_manager_stop(CsdWacomManager * manager)1473 csd_wacom_manager_stop (CsdWacomManager *manager)
1474 {
1475 CsdWacomManagerPrivate *p = manager->priv;
1476 GSList *ls;
1477 GList *l;
1478
1479 g_debug ("Stopping wacom manager");
1480
1481 if (p->device_manager != NULL) {
1482 GList *devices;
1483
1484 g_signal_handler_disconnect (p->device_manager, p->device_added_id);
1485 g_signal_handler_disconnect (p->device_manager, p->device_removed_id);
1486
1487 devices = gdk_device_manager_list_devices (p->device_manager, GDK_DEVICE_TYPE_SLAVE);
1488 for (l = devices; l != NULL; l = l->next) {
1489 CsdWacomDeviceType type;
1490
1491 type = csd_wacom_device_get_device_type (l->data);
1492 if (type == WACOM_TYPE_PAD) {
1493 int id;
1494
1495 id = get_device_id (l->data);
1496 grab_button (id, FALSE, manager->priv->screens);
1497 }
1498 }
1499 g_list_free (devices);
1500
1501 p->device_manager = NULL;
1502 }
1503
1504 for (ls = p->screens; ls != NULL; ls = ls->next) {
1505 gdk_window_remove_filter (gdk_screen_get_root_window (ls->data),
1506 (GdkFilterFunc) filter_button_events,
1507 manager);
1508 }
1509
1510 for (l = p->rr_screens; l != NULL; l = l->next)
1511 g_signal_handlers_disconnect_by_func (l->data, on_screen_changed_cb, manager);
1512
1513 g_clear_pointer (&p->osd_window, gtk_widget_destroy);
1514 }
1515
1516 static void
csd_wacom_manager_finalize(GObject * object)1517 csd_wacom_manager_finalize (GObject *object)
1518 {
1519 CsdWacomManager *wacom_manager;
1520
1521 g_return_if_fail (object != NULL);
1522 g_return_if_fail (CSD_IS_WACOM_MANAGER (object));
1523
1524 wacom_manager = CSD_WACOM_MANAGER (object);
1525
1526 g_return_if_fail (wacom_manager->priv != NULL);
1527
1528 if (wacom_manager->priv->devices) {
1529 g_hash_table_destroy (wacom_manager->priv->devices);
1530 wacom_manager->priv->devices = NULL;
1531 }
1532
1533 if (wacom_manager->priv->screens != NULL) {
1534 g_slist_free (wacom_manager->priv->screens);
1535 wacom_manager->priv->screens = NULL;
1536 }
1537
1538 if (wacom_manager->priv->rr_screens != NULL) {
1539 g_list_free_full (wacom_manager->priv->rr_screens, g_object_unref);
1540 wacom_manager->priv->rr_screens = NULL;
1541 }
1542
1543 if (wacom_manager->priv->start_idle_id != 0) {
1544 g_source_remove (wacom_manager->priv->start_idle_id);
1545 wacom_manager->priv->start_idle_id = 0;
1546 }
1547
1548 G_OBJECT_CLASS (csd_wacom_manager_parent_class)->finalize (object);
1549 }
1550
1551 CsdWacomManager *
csd_wacom_manager_new(void)1552 csd_wacom_manager_new (void)
1553 {
1554 if (manager_object != NULL) {
1555 g_object_ref (manager_object);
1556 } else {
1557 manager_object = g_object_new (CSD_TYPE_WACOM_MANAGER, NULL);
1558 g_object_add_weak_pointer (manager_object,
1559 (gpointer *) &manager_object);
1560 }
1561
1562 return CSD_WACOM_MANAGER (manager_object);
1563 }
1564