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