diff --git a/panels/mouse/gsd-input-helper.c b/panels/mouse/gsd-input-helper.c index 53627018d..95b1b3848 100644 --- a/panels/mouse/gsd-input-helper.c +++ b/panels/mouse/gsd-input-helper.c @@ -25,22 +25,118 @@ #include #include +#include #include "gsd-input-helper.h" #define INPUT_DEVICES_SCHEMA "org.gnome.settings-daemon.peripherals.input-devices" #define KEY_HOTPLUG_COMMAND "hotplug-command" +typedef gboolean (* InfoIdentifyFunc) (XDeviceInfo *device_info); +typedef gboolean (* DeviceIdentifyFunc) (XDevice *xdevice); + +gboolean +device_set_property (XDevice *xdevice, + const char *device_name, + PropertyHelper *property) +{ + int rc, i; + Atom prop; + Atom realtype; + int realformat; + unsigned long nitems, bytes_after; + unsigned char *data; + + prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), + property->name, False); + if (!prop) + return FALSE; + + gdk_error_trap_push (); + + rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), + xdevice, prop, 0, property->nitems, False, + AnyPropertyType, &realtype, &realformat, &nitems, + &bytes_after, &data); + + if (rc != Success || + realtype != property->type || + realformat != property->format || + nitems < property->nitems) { + gdk_error_trap_pop_ignored (); + g_warning ("Error reading property \"%s\" for \"%s\"", property->name, device_name); + return FALSE; + } + + for (i = 0; i < nitems; i++) { + switch (property->format) { + case 8: + data[i] = property->data.c[i]; + break; + case 32: + ((long*)data)[i] = property->data.i[i]; + break; + } + } + + XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), + xdevice, prop, realtype, realformat, + PropModeReplace, data, nitems); + + if (gdk_error_trap_pop ()) { + g_warning ("Error in setting \"%s\" for \"%s\"", property->name, device_name); + return FALSE; + } + + return TRUE; +} + +static gboolean +supports_xinput_devices_with_opcode (int *opcode) +{ + gint op_code, event, error; + gboolean retval; + + retval = XQueryExtension (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), + "XInputExtension", + &op_code, + &event, + &error); + if (opcode) + *opcode = op_code; + + return retval; +} + gboolean supports_xinput_devices (void) { - gint op_code, event, error; + return supports_xinput_devices_with_opcode (NULL); +} - return XQueryExtension (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), - "XInputExtension", - &op_code, - &event, - &error); +gboolean +supports_xinput2_devices (int *opcode) +{ + int major, minor; + + if (supports_xinput_devices_with_opcode (opcode) == FALSE) + return FALSE; + + gdk_error_trap_push (); + + major = 2; + minor = 0; + + if (XIQueryVersion (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &major, &minor) != Success) { + gdk_error_trap_pop_ignored (); + return FALSE; + } + gdk_error_trap_pop_ignored (); + + if ((major * 1000 + minor) < (2000)) + return FALSE; + + return TRUE; } gboolean @@ -51,9 +147,8 @@ device_is_touchpad (XDevice *xdevice) unsigned long nitems, bytes_after; unsigned char *data; - /* FIXME - * we don't check on the type being XI_TOUCHPAD, but having a "Synaptics Off" - * property should be enough */ + /* we don't check on the type being XI_TOUCHPAD here, + * but having a "Synaptics Off" property should be enough */ prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Off", False); if (!prop) @@ -73,7 +168,20 @@ device_is_touchpad (XDevice *xdevice) } gboolean -touchpad_is_present (void) +device_info_is_touchpad (XDeviceInfo *device_info) +{ + return (device_info->type == XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XI_TOUCHPAD, False)); +} + +gboolean +device_info_is_touchscreen (XDeviceInfo *device_info) +{ + return (device_info->type == XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XI_TOUCHSCREEN, False)); +} + +static gboolean +device_type_is_present (InfoIdentifyFunc info_func, + DeviceIdentifyFunc device_func) { XDeviceInfo *device_info; gint n_devices; @@ -92,12 +200,21 @@ touchpad_is_present (void) for (i = 0; i < n_devices; i++) { XDevice *device; + /* Check with the device info first */ + retval = (info_func) (&device_info[i]); + if (retval == FALSE) + continue; + + /* If we only have an info func, we're done checking */ + if (device_func == NULL) + break; + gdk_error_trap_push (); device = XOpenDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device_info[i].id); if (gdk_error_trap_pop () || (device == NULL)) continue; - retval = device_is_touchpad (device); + retval = (device_func) (device); if (retval) { XCloseDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device); break; @@ -110,6 +227,175 @@ touchpad_is_present (void) return retval; } +gboolean +touchscreen_is_present (void) +{ + return device_type_is_present (device_info_is_touchscreen, + NULL); +} + +gboolean +touchpad_is_present (void) +{ + return device_type_is_present (device_info_is_touchpad, + device_is_touchpad); +} + +char * +xdevice_get_device_node (int deviceid) +{ + Atom prop; + Atom act_type; + int act_format; + unsigned long nitems, bytes_after; + unsigned char *data; + char *ret; + + gdk_display_sync (gdk_display_get_default ()); + + prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Device Node", False); + if (!prop) + return NULL; + + gdk_error_trap_push (); + + if (!XIGetProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), + deviceid, prop, 0, 1000, False, + AnyPropertyType, &act_type, &act_format, + &nitems, &bytes_after, &data) == Success) { + gdk_error_trap_pop_ignored (); + return NULL; + } + if (gdk_error_trap_pop ()) + goto out; + + if (nitems == 0) + goto out; + + if (act_type != XA_STRING) + goto out; + + /* Unknown string format */ + if (act_format != 8) + goto out; + + ret = g_strdup ((char *) data); + + XFree (data); + return ret; + +out: + XFree (data); + return NULL; +} + +#define TOOL_ID_FORMAT_SIZE 32 +static int +get_id_for_index (guchar *data, + guint idx) +{ + guchar *ptr; + int id; + + ptr = data; + ptr += TOOL_ID_FORMAT_SIZE / 8 * idx; + + id = *((int32_t*)ptr); + id = id & 0xfffff; + + return id; +} + + +#define STYLUS_DEVICE_ID 0x02 +#define ERASER_DEVICE_ID 0x0A + +int +xdevice_get_last_tool_id (int deviceid) +{ + Atom prop; + Atom act_type; + int act_format; + unsigned long nitems, bytes_after; + unsigned char *data; + int id; + + id = -1; + + gdk_display_sync (gdk_display_get_default ()); + + prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), WACOM_SERIAL_IDS_PROP, False); + if (!prop) + return -1; + + gdk_error_trap_push (); + + if (!XIGetProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), + deviceid, prop, 0, 1000, False, + AnyPropertyType, &act_type, &act_format, + &nitems, &bytes_after, &data) == Success) { + gdk_error_trap_pop_ignored (); + return -1; + } + + if (gdk_error_trap_pop ()) + goto out; + + if (nitems != 4 && nitems != 5) + goto out; + + if (act_type != XA_INTEGER) + goto out; + + if (act_format != TOOL_ID_FORMAT_SIZE) + goto out; + + /* item 0 = tablet ID + * item 1 = old device serial number (== last tool in proximity) + * item 2 = old hardware serial number (including tool ID) + * item 3 = current serial number (0 if no tool in proximity) + * item 4 = current tool ID (since Feb 2012) + * + * Get the current tool ID first, if available, then the old one */ + id = 0x0; + if (nitems == 5) + id = get_id_for_index (data, 4); + if (id == 0x0) + id = get_id_for_index (data, 2); + + /* That means that no tool was set down yet */ + if (id == STYLUS_DEVICE_ID || + id == ERASER_DEVICE_ID) + return 0x0; + +out: + XFree (data); + return id; +} + +gboolean +set_device_enabled (int device_id, + gboolean enabled) +{ + Atom prop; + guchar value; + + prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Device Enabled", False); + if (!prop) + return FALSE; + + gdk_error_trap_push (); + + value = enabled ? 1 : 0; + XIChangeProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), + device_id, prop, XA_INTEGER, 8, PropModeReplace, &value, 1); + + if (gdk_error_trap_pop ()) + return FALSE; + + return TRUE; +} + static const char * custom_command_to_string (CustomCommand command) { @@ -182,3 +468,38 @@ run_custom_command (GdkDevice *device, return (exit_status == 0); } + +GList * +get_disabled_devices (GdkDeviceManager *manager) +{ + XDeviceInfo *device_info; + gint n_devices; + guint i; + GList *ret; + + ret = NULL; + + device_info = XListInputDevices (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &n_devices); + if (device_info == NULL) + return ret; + + for (i = 0; i < n_devices; i++) { + GdkDevice *device; + + /* Ignore core devices */ + if (device_info[i].use == IsXKeyboard || + device_info[i].use == IsXPointer) + continue; + + /* Check whether the device is actually available */ + device = gdk_x11_device_manager_lookup (manager, device_info[i].id); + if (device != NULL) + continue; + + ret = g_list_prepend (ret, GINT_TO_POINTER (device_info[i].id)); + } + + XFreeDeviceList (device_info); + + return ret; +} diff --git a/panels/mouse/gsd-input-helper.h b/panels/mouse/gsd-input-helper.h index a705f90a5..dfde51f2e 100644 --- a/panels/mouse/gsd-input-helper.h +++ b/panels/mouse/gsd-input-helper.h @@ -27,18 +27,54 @@ G_BEGIN_DECLS #include #include +#define WACOM_SERIAL_IDS_PROP "Wacom Serial IDs" + typedef enum { - COMMAND_DEVICE_ADDED, - COMMAND_DEVICE_REMOVED, - COMMAND_DEVICE_PRESENT + COMMAND_DEVICE_ADDED, + COMMAND_DEVICE_REMOVED, + COMMAND_DEVICE_PRESENT } CustomCommand; -gboolean supports_xinput_devices (void); -gboolean device_is_touchpad (XDevice *xdevice); +/* Generic property setting code. Fill up the struct property with the property + * data and pass it into device_set_property together with the device to be + * changed. Note: doesn't cater for non-zero offsets yet, but we don't have + * any settings that require that. + */ +typedef struct { + const char *name; /* property name */ + gint nitems; /* number of items in data */ + gint format; /* CARD8 or CARD32 sized-items */ + gint type; /* Atom representing data type */ + union { + const gchar *c; /* 8 bit data */ + const gint *i; /* 32 bit data */ + } data; +} PropertyHelper; + +gboolean supports_xinput_devices (void); +gboolean supports_xinput2_devices (int *opcode); + +gboolean set_device_enabled (int device_id, + gboolean enabled); + +gboolean device_is_touchpad (XDevice *xdevice); + +gboolean device_info_is_touchpad (XDeviceInfo *device_info); +gboolean device_info_is_touchscreen (XDeviceInfo *device_info); + gboolean touchpad_is_present (void); +gboolean touchscreen_is_present (void); + +gboolean device_set_property (XDevice *xdevice, + const char *device_name, + PropertyHelper *property); gboolean run_custom_command (GdkDevice *device, - CustomCommand command); + CustomCommand command); + +GList * get_disabled_devices (GdkDeviceManager *manager); +char * xdevice_get_device_node (int deviceid); +int xdevice_get_last_tool_id (int deviceid); G_END_DECLS