1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
3
* Copyright (C) 2010 Bastien Nocera <hadess@hadess.net>
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26
#include <sys/types.h>
27
#include <X11/Xatom.h>
28
#include <X11/extensions/XInput2.h>
30
#include "gsd-input-helper.h"
32
#define INPUT_DEVICES_SCHEMA "org.gnome.settings-daemon.peripherals.input-devices"
33
#define KEY_HOTPLUG_COMMAND "hotplug-command"
35
typedef gboolean (* InfoIdentifyFunc) (XDeviceInfo *device_info);
36
typedef gboolean (* DeviceIdentifyFunc) (XDevice *xdevice);
39
device_set_property (XDevice *xdevice,
40
const char *device_name,
41
PropertyHelper *property)
47
unsigned long nitems, bytes_after;
50
prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
51
property->name, False);
55
gdk_error_trap_push ();
57
rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
58
xdevice, prop, 0, property->nitems, False,
59
AnyPropertyType, &realtype, &realformat, &nitems,
63
realtype != property->type ||
64
realformat != property->format ||
65
nitems < property->nitems) {
66
gdk_error_trap_pop_ignored ();
67
g_warning ("Error reading property \"%s\" for \"%s\"", property->name, device_name);
71
for (i = 0; i < nitems; i++) {
72
switch (property->format) {
74
data[i] = property->data.c[i];
77
((long*)data)[i] = property->data.i[i];
82
XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
83
xdevice, prop, realtype, realformat,
84
PropModeReplace, data, nitems);
88
if (gdk_error_trap_pop ()) {
89
g_warning ("Error in setting \"%s\" for \"%s\"", property->name, device_name);
97
supports_xinput_devices_with_opcode (int *opcode)
99
gint op_code, event, error;
102
retval = XQueryExtension (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
114
supports_xinput_devices (void)
116
return supports_xinput_devices_with_opcode (NULL);
120
supports_xinput2_devices (int *opcode)
124
if (supports_xinput_devices_with_opcode (opcode) == FALSE)
127
gdk_error_trap_push ();
132
if (XIQueryVersion (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &major, &minor) != Success) {
133
gdk_error_trap_pop_ignored ();
134
/* try for 2.2, maybe gtk has already announced 2.2 support */
135
gdk_error_trap_push ();
138
if (XIQueryVersion (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &major, &minor) != Success) {
139
gdk_error_trap_pop_ignored ();
143
gdk_error_trap_pop_ignored ();
145
if ((major * 1000 + minor) < (2000))
152
device_is_touchpad (XDevice *xdevice)
156
unsigned long nitems, bytes_after;
159
/* we don't check on the type being XI_TOUCHPAD here,
160
* but having a "Synaptics Off" property should be enough */
162
prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Off", False);
166
gdk_error_trap_push ();
167
if ((XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop, 0, 1, False,
168
XA_INTEGER, &realtype, &realformat, &nitems,
169
&bytes_after, &data) == Success) && (realtype != None)) {
170
gdk_error_trap_pop_ignored ();
174
gdk_error_trap_pop_ignored ();
180
device_info_is_touchpad (XDeviceInfo *device_info)
182
return (device_info->type == XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XI_TOUCHPAD, False));
186
device_info_is_touchscreen (XDeviceInfo *device_info)
188
return (device_info->type == XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XI_TOUCHSCREEN, False));
192
device_info_is_mouse (XDeviceInfo *device_info)
194
return (device_info->type == XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XI_MOUSE, False));
198
device_type_is_present (InfoIdentifyFunc info_func,
199
DeviceIdentifyFunc device_func)
201
XDeviceInfo *device_info;
206
if (supports_xinput_devices () == FALSE)
211
device_info = XListInputDevices (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &n_devices);
212
if (device_info == NULL)
215
for (i = 0; i < n_devices; i++) {
218
/* Check with the device info first */
219
retval = (info_func) (&device_info[i]);
223
/* If we only have an info func, we're done checking */
224
if (device_func == NULL)
227
gdk_error_trap_push ();
228
device = XOpenDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device_info[i].id);
229
if (gdk_error_trap_pop () || (device == NULL))
232
retval = (device_func) (device);
234
XCloseDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device);
238
XCloseDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device);
240
XFreeDeviceList (device_info);
246
touchscreen_is_present (void)
248
return device_type_is_present (device_info_is_touchscreen,
253
touchpad_is_present (void)
255
return device_type_is_present (device_info_is_touchpad,
260
mouse_is_present (void)
262
return device_type_is_present (device_info_is_mouse,
267
xdevice_get_device_node (int deviceid)
272
unsigned long nitems, bytes_after;
276
gdk_display_sync (gdk_display_get_default ());
278
prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Device Node", False);
282
gdk_error_trap_push ();
284
if (!XIGetProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
285
deviceid, prop, 0, 1000, False,
286
AnyPropertyType, &act_type, &act_format,
287
&nitems, &bytes_after, &data) == Success) {
288
gdk_error_trap_pop_ignored ();
291
if (gdk_error_trap_pop ())
297
if (act_type != XA_STRING)
300
/* Unknown string format */
304
ret = g_strdup ((char *) data);
314
#define TOOL_ID_FORMAT_SIZE 32
316
get_id_for_index (guchar *data,
323
ptr += TOOL_ID_FORMAT_SIZE / 8 * idx;
325
id = *((int32_t*)ptr);
332
#define STYLUS_DEVICE_ID 0x02
333
#define ERASER_DEVICE_ID 0x0A
336
xdevice_get_last_tool_id (int deviceid)
341
unsigned long nitems, bytes_after;
347
gdk_display_sync (gdk_display_get_default ());
349
prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), WACOM_SERIAL_IDS_PROP, False);
355
gdk_error_trap_push ();
357
if (XIGetProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
358
deviceid, prop, 0, 1000, False,
359
AnyPropertyType, &act_type, &act_format,
360
&nitems, &bytes_after, &data) != Success) {
361
gdk_error_trap_pop_ignored ();
365
if (gdk_error_trap_pop ())
368
if (nitems != 4 && nitems != 5)
371
if (act_type != XA_INTEGER)
374
if (act_format != TOOL_ID_FORMAT_SIZE)
377
/* item 0 = tablet ID
378
* item 1 = old device serial number (== last tool in proximity)
379
* item 2 = old hardware serial number (including tool ID)
380
* item 3 = current serial number (0 if no tool in proximity)
381
* item 4 = current tool ID (since Feb 2012)
383
* Get the current tool ID first, if available, then the old one */
386
id = get_id_for_index (data, 4);
388
id = get_id_for_index (data, 2);
390
/* That means that no tool was set down yet */
391
if (id == STYLUS_DEVICE_ID ||
392
id == ERASER_DEVICE_ID)
402
set_device_enabled (int device_id,
408
prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Device Enabled", False);
412
gdk_error_trap_push ();
414
value = enabled ? 1 : 0;
415
XIChangeProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
416
device_id, prop, XA_INTEGER, 8, PropModeReplace, &value, 1);
418
if (gdk_error_trap_pop ())
425
custom_command_to_string (CustomCommand command)
428
case COMMAND_DEVICE_ADDED:
430
case COMMAND_DEVICE_REMOVED:
432
case COMMAND_DEVICE_PRESENT:
435
g_assert_not_reached ();
439
/* Run a custom command on device presence events. Parameters passed into
440
* the custom command are:
441
* command -t [added|removed|present] -i <device ID> <device name>
442
* Type 'added' and 'removed' signal 'device added' and 'device removed',
443
* respectively. Type 'present' signals 'device present at
444
* gnome-settings-daemon init'.
446
* The script is expected to run synchronously, and an exit value
447
* of "1" means that no other settings will be applied to this
450
* More options may be added in the future.
452
* This function returns TRUE if we should not apply any more settings
456
run_custom_command (GdkDevice *device,
457
CustomCommand command)
466
settings = g_settings_new (INPUT_DEVICES_SCHEMA);
467
cmd = g_settings_get_string (settings, KEY_HOTPLUG_COMMAND);
468
g_object_unref (settings);
470
if (!cmd || cmd[0] == '\0') {
476
g_object_get (device, "device-id", &id, NULL);
480
argv[2] = (char *) custom_command_to_string (command);
482
argv[4] = g_strdup_printf ("%d", id);
483
argv[5] = g_strdup_printf ("%s", gdk_device_get_name (device));
486
rc = g_spawn_sync (g_get_home_dir (), argv, NULL, G_SPAWN_SEARCH_PATH,
487
NULL, NULL, NULL, NULL, &exit_status, NULL);
490
g_warning ("Couldn't execute command '%s', verify that this is a valid command.", cmd);
496
return (exit_status == 0);
500
get_disabled_devices (GdkDeviceManager *manager)
502
XDeviceInfo *device_info;
509
device_info = XListInputDevices (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &n_devices);
510
if (device_info == NULL)
513
for (i = 0; i < n_devices; i++) {
516
/* Ignore core devices */
517
if (device_info[i].use == IsXKeyboard ||
518
device_info[i].use == IsXPointer)
521
/* Check whether the device is actually available */
522
device = gdk_x11_device_manager_lookup (manager, device_info[i].id);
526
ret = g_list_prepend (ret, GINT_TO_POINTER (device_info[i].id));
529
XFreeDeviceList (device_info);