/* * Copyright (C) 2011 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Author: Bastien Nocera * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "gsd-input-helper.h" #include "gsd-enums.h" #include "gsd-wacom-device.h" #define GSD_WACOM_STYLUS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_WACOM_STYLUS, GsdWacomStylusPrivate)) #define WACOM_TABLET_SCHEMA "org.gnome.settings-daemon.peripherals.wacom" #define WACOM_DEVICE_CONFIG_BASE "/org/gnome/settings-daemon/peripherals/wacom/%s-%s/" #define WACOM_STYLUS_SCHEMA "org.gnome.settings-daemon.peripherals.wacom.stylus" #define WACOM_ERASER_SCHEMA "org.gnome.settings-daemon.peripherals.wacom.eraser" #define WACOM_BUTTON_SCHEMA "org.gnome.settings-daemon.peripherals.wacom.tablet-button" static struct { GsdRRRotation rotation; GsdWacomRotation rotation_wacom; const gchar *rotation_string; } rotation_table[] = { { GSD_RR_ROTATION_0, GSD_WACOM_ROTATION_NONE, "none" }, { GSD_RR_ROTATION_90, GSD_WACOM_ROTATION_CCW, "ccw" }, { GSD_RR_ROTATION_180, GSD_WACOM_ROTATION_HALF, "half" }, { GSD_RR_ROTATION_270, GSD_WACOM_ROTATION_CW, "cw" } }; static WacomDeviceDatabase *db = NULL; struct GsdWacomStylusPrivate { GsdWacomDevice *device; int id; WacomStylusType type; char *name; const char *icon_name; GSettings *settings; gboolean has_eraser; int num_buttons; }; static void gsd_wacom_stylus_class_init (GsdWacomStylusClass *klass); static void gsd_wacom_stylus_init (GsdWacomStylus *wacom_stylus); static void gsd_wacom_stylus_finalize (GObject *object); G_DEFINE_TYPE (GsdWacomStylus, gsd_wacom_stylus, G_TYPE_OBJECT) static void gsd_wacom_stylus_class_init (GsdWacomStylusClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gsd_wacom_stylus_finalize; g_type_class_add_private (klass, sizeof (GsdWacomStylusPrivate)); } static void gsd_wacom_stylus_init (GsdWacomStylus *stylus) { stylus->priv = GSD_WACOM_STYLUS_GET_PRIVATE (stylus); } static void gsd_wacom_stylus_finalize (GObject *object) { GsdWacomStylus *stylus; GsdWacomStylusPrivate *p; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_WACOM_STYLUS (object)); stylus = GSD_WACOM_STYLUS (object); g_return_if_fail (stylus->priv != NULL); p = stylus->priv; if (p->settings != NULL) { g_object_unref (p->settings); p->settings = NULL; } g_free (p->name); p->name = NULL; G_OBJECT_CLASS (gsd_wacom_stylus_parent_class)->finalize (object); } static const char * get_icon_name_from_type (WacomStylusType type) { switch (type) { case WSTYLUS_INKING: case WSTYLUS_STROKE: /* The stroke pen is the same as the inking pen with * a different nib */ return "wacom-stylus-inking"; case WSTYLUS_AIRBRUSH: return "wacom-stylus-airbrush"; case WSTYLUS_MARKER: return "wacom-stylus-art-pen"; case WSTYLUS_CLASSIC: return "wacom-stylus-classic"; default: return "wacom-stylus"; } } static GsdWacomStylus * gsd_wacom_stylus_new (GsdWacomDevice *device, const WacomStylus *wstylus, GSettings *settings) { GsdWacomStylus *stylus; g_return_val_if_fail (G_IS_SETTINGS (settings), NULL); g_return_val_if_fail (wstylus != NULL, NULL); stylus = GSD_WACOM_STYLUS (g_object_new (GSD_TYPE_WACOM_STYLUS, NULL)); stylus->priv->device = device; stylus->priv->id = libwacom_stylus_get_id (wstylus); stylus->priv->name = g_strdup (libwacom_stylus_get_name (wstylus)); stylus->priv->settings = settings; stylus->priv->type = libwacom_stylus_get_type (wstylus); stylus->priv->icon_name = get_icon_name_from_type (stylus->priv->type); stylus->priv->has_eraser = libwacom_stylus_has_eraser (wstylus); stylus->priv->num_buttons = libwacom_stylus_get_num_buttons (wstylus); return stylus; } GSettings * gsd_wacom_stylus_get_settings (GsdWacomStylus *stylus) { g_return_val_if_fail (GSD_IS_WACOM_STYLUS (stylus), NULL); return stylus->priv->settings; } const char * gsd_wacom_stylus_get_name (GsdWacomStylus *stylus) { g_return_val_if_fail (GSD_IS_WACOM_STYLUS (stylus), NULL); return stylus->priv->name; } const char * gsd_wacom_stylus_get_icon_name (GsdWacomStylus *stylus) { g_return_val_if_fail (GSD_IS_WACOM_STYLUS (stylus), NULL); return stylus->priv->icon_name; } GsdWacomDevice * gsd_wacom_stylus_get_device (GsdWacomStylus *stylus) { g_return_val_if_fail (GSD_IS_WACOM_STYLUS (stylus), NULL); return stylus->priv->device; } gboolean gsd_wacom_stylus_get_has_eraser (GsdWacomStylus *stylus) { g_return_val_if_fail (GSD_IS_WACOM_STYLUS (stylus), FALSE); return stylus->priv->has_eraser; } guint gsd_wacom_stylus_get_num_buttons (GsdWacomStylus *stylus) { g_return_val_if_fail (GSD_IS_WACOM_STYLUS (stylus), -1); return stylus->priv->num_buttons; } GsdWacomStylusType gsd_wacom_stylus_get_stylus_type (GsdWacomStylus *stylus) { g_return_val_if_fail (GSD_IS_WACOM_STYLUS (stylus), WACOM_STYLUS_TYPE_UNKNOWN); switch (stylus->priv->type) { case WSTYLUS_UNKNOWN: return WACOM_STYLUS_TYPE_UNKNOWN; case WSTYLUS_GENERAL: return WACOM_STYLUS_TYPE_GENERAL; case WSTYLUS_INKING: return WACOM_STYLUS_TYPE_INKING; case WSTYLUS_AIRBRUSH: return WACOM_STYLUS_TYPE_AIRBRUSH; case WSTYLUS_CLASSIC: return WACOM_STYLUS_TYPE_CLASSIC; case WSTYLUS_MARKER: return WACOM_STYLUS_TYPE_MARKER; case WSTYLUS_STROKE: return WACOM_STYLUS_TYPE_STROKE; case WSTYLUS_PUCK: return WACOM_STYLUS_TYPE_PUCK; default: g_assert_not_reached (); } return WACOM_STYLUS_TYPE_UNKNOWN; } int gsd_wacom_stylus_get_id (GsdWacomStylus *stylus) { g_return_val_if_fail (GSD_IS_WACOM_STYLUS (stylus), -1); return stylus->priv->id; } /* Tablet buttons */ static GsdWacomTabletButton * gsd_wacom_tablet_button_new (const char *name, const char *id, const char *settings_path, GsdWacomTabletButtonType type, GsdWacomTabletButtonPos pos, int group_id, int idx, int status_led) { GsdWacomTabletButton *ret; ret = g_new0 (GsdWacomTabletButton, 1); ret->name = g_strdup (name); ret->id = g_strdup (id); if (type != WACOM_TABLET_BUTTON_TYPE_HARDCODED) { char *button_settings_path; button_settings_path = g_strdup_printf ("%s%s/", settings_path, id); ret->settings = g_settings_new_with_path (WACOM_BUTTON_SCHEMA, button_settings_path); g_free (button_settings_path); } ret->group_id = group_id; ret->idx = idx; ret->type = type; ret->pos = pos; ret->status_led = status_led; return ret; } void gsd_wacom_tablet_button_free (GsdWacomTabletButton *button) { g_return_if_fail (button != NULL); if (button->settings != NULL) g_object_unref (button->settings); g_free (button->name); g_free (button->id); g_free (button); } GsdWacomTabletButton * gsd_wacom_tablet_button_copy (GsdWacomTabletButton *button) { GsdWacomTabletButton *ret; g_return_val_if_fail (button != NULL, NULL); ret = g_new0 (GsdWacomTabletButton, 1); ret->name = g_strdup (button->name); if (button->settings != NULL) ret->settings = g_object_ref (button->settings); ret->id = button->id; ret->type = button->type; ret->group_id = button->group_id; return ret; } #define GSD_WACOM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_WACOM_DEVICE, GsdWacomDevicePrivate)) /* we support two types of settings: * Tablet-wide settings: applied to each tool on the tablet. e.g. rotation * Tool-specific settings: applied to one tool only. */ #define SETTINGS_WACOM_DIR "org.gnome.settings-daemon.peripherals.wacom" #define SETTINGS_STYLUS_DIR "stylus" #define SETTINGS_ERASER_DIR "eraser" struct GsdWacomDevicePrivate { GdkDevice *gdk_device; int device_id; int opcode; GsdWacomDeviceType type; char *name; char *path; char *machine_id; const char *icon_name; char *layout_path; char *tool_name; gboolean reversible; gboolean is_screen_tablet; gboolean is_isd; /* integrated system device */ gboolean is_fallback; GList *styli; GsdWacomStylus *last_stylus; GList *buttons; gint num_rings; gint num_strips; GHashTable *modes; /* key = int (group), value = int (index) */ GHashTable *num_modes; /* key = int (group), value = int (index) */ GSettings *wacom_settings; }; enum { PROP_0, PROP_GDK_DEVICE, PROP_LAST_STYLUS }; static void gsd_wacom_device_class_init (GsdWacomDeviceClass *klass); static void gsd_wacom_device_init (GsdWacomDevice *wacom_device); static void gsd_wacom_device_finalize (GObject *object); G_DEFINE_TYPE (GsdWacomDevice, gsd_wacom_device, G_TYPE_OBJECT) static GdkFilterReturn filter_events (XEvent *xevent, GdkEvent *event, GsdWacomDevice *device) { XIEvent *xiev; XIPropertyEvent *pev; XGenericEventCookie *cookie; char *name; int tool_id; /* verify we have a property event */ if (xevent->type != GenericEvent) return GDK_FILTER_CONTINUE; cookie = &xevent->xcookie; if (cookie->extension != device->priv->opcode) return GDK_FILTER_CONTINUE; xiev = (XIEvent *) xevent->xcookie.data; if (xiev->evtype != XI_PropertyEvent) return GDK_FILTER_CONTINUE; pev = (XIPropertyEvent *) xiev; /* Is the event for us? */ if (pev->deviceid != device->priv->device_id) return GDK_FILTER_CONTINUE; name = XGetAtomName (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), pev->property); if (name == NULL || g_strcmp0 (name, WACOM_SERIAL_IDS_PROP) != 0) { if (name) XFree (name); return GDK_FILTER_CONTINUE; } XFree (name); tool_id = xdevice_get_last_tool_id (device->priv->device_id); if (tool_id == -1) { g_warning ("Failed to get value for changed stylus ID on device '%d'", device->priv->device_id); return GDK_FILTER_CONTINUE; } gsd_wacom_device_set_current_stylus (device, tool_id); return GDK_FILTER_CONTINUE; } static gboolean setup_property_notify (GsdWacomDevice *device) { Display *dpy; XIEventMask evmask; int tool_id; evmask.deviceid = device->priv->device_id; evmask.mask_len = XIMaskLen (XI_PropertyEvent); evmask.mask = g_new0 (guchar, evmask.mask_len); XISetMask (evmask.mask, XI_PropertyEvent); dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); XISelectEvents (dpy, DefaultRootWindow (dpy), &evmask, 1); g_free (evmask.mask); gdk_window_add_filter (NULL, (GdkFilterFunc) filter_events, device); tool_id = xdevice_get_last_tool_id (device->priv->device_id); if (tool_id == -1) { g_warning ("Failed to get value for changed stylus ID on device '%d", device->priv->device_id); return TRUE; } gsd_wacom_device_set_current_stylus (device, tool_id); return TRUE; } static GsdWacomDeviceType get_device_type (XDeviceInfo *dev) { GsdWacomDeviceType ret; static Atom stylus, cursor, eraser, pad, touch, prop; XDevice *device; Atom realtype; int realformat; unsigned long nitems, bytes_after; unsigned char *data = NULL; int rc; ret = WACOM_TYPE_INVALID; if ((dev->use == IsXPointer) || (dev->use == IsXKeyboard)) return ret; if (!stylus) stylus = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "STYLUS", False); if (!eraser) eraser = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "ERASER", False); if (!cursor) cursor = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "CURSOR", False); if (!pad) pad = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "PAD", False); if (!touch) touch = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "TOUCH", False); if (!prop) prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Wacom Tool Type", False); if (dev->type == stylus) ret = WACOM_TYPE_STYLUS; else if (dev->type == eraser) ret = WACOM_TYPE_ERASER; else if (dev->type == cursor) ret = WACOM_TYPE_CURSOR; else if (dev->type == pad) ret = WACOM_TYPE_PAD; else if (dev->type == touch) ret = WACOM_TYPE_TOUCH; if (ret == WACOM_TYPE_INVALID) return ret; /* There is currently no good way of detecting the driver for a device * other than checking for a driver-specific property. * Wacom Tool Type exists on all tools */ gdk_error_trap_push (); device = XOpenDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), dev->id); if (gdk_error_trap_pop () || (device == NULL)) return ret; gdk_error_trap_push (); rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device, prop, 0, 1, False, XA_ATOM, &realtype, &realformat, &nitems, &bytes_after, &data); XCloseDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device); if (gdk_error_trap_pop () || rc != Success || realtype == None) ret = WACOM_TYPE_INVALID; XFree (data); return ret; } /* Finds an output which matches the given EDID information. Any NULL * parameter will be interpreted to match any value. */ static GsdRROutput * find_output_by_edid (GsdRRScreen *rr_screen, const gchar *vendor, const gchar *product, const gchar *serial) { GsdRROutput **rr_outputs; GsdRROutput *retval = NULL; guint i; rr_outputs = gsd_rr_screen_list_outputs (rr_screen); for (i = 0; rr_outputs[i] != NULL; i++) { gchar *o_vendor_s; gchar *o_product_s; int o_product; gchar *o_serial_s; int o_serial; gboolean match; if (!gsd_rr_output_is_connected (rr_outputs[i])) continue; if (!gsd_rr_output_get_ids_from_edid (rr_outputs[i], &o_vendor_s, &o_product, &o_serial)) continue; o_product_s = g_strdup_printf ("%d", o_product); o_serial_s = g_strdup_printf ("%d", o_serial); g_debug ("Checking for match between '%s','%s','%s' and '%s','%s','%s'", \ vendor, product, serial, o_vendor_s, o_product_s, o_serial_s); match = (vendor == NULL || g_strcmp0 (vendor, o_vendor_s) == 0) && \ (product == NULL || g_strcmp0 (product, o_product_s) == 0) && \ (serial == NULL || g_strcmp0 (serial, o_serial_s) == 0); g_free (o_vendor_s); g_free (o_product_s); g_free (o_serial_s); if (match) { retval = rr_outputs[i]; break; } } if (retval == NULL) g_debug ("Did not find a matching output for EDID '%s,%s,%s'", vendor, product, serial); return retval; } static GsdRROutput* find_builtin_output (GsdRRScreen *rr_screen) { GsdRROutput **rr_outputs; GsdRROutput *retval = NULL; guint i; rr_outputs = gsd_rr_screen_list_outputs (rr_screen); for (i = 0; rr_outputs[i] != NULL; i++) { if (!gsd_rr_output_is_connected (rr_outputs[i])) continue; if (gsd_rr_output_is_laptop(rr_outputs[i])) { retval = rr_outputs[i]; break; } } if (retval == NULL) g_debug ("Did not find a built-in monitor"); return retval; } static GsdRROutput * find_output_by_heuristic (GsdRRScreen *rr_screen, GsdWacomDevice *device) { GsdRROutput *rr_output; /* TODO: This heuristic will fail for non-Wacom display * tablets and may give the wrong result if multiple Wacom * display tablets are connected. */ rr_output = find_output_by_edid (rr_screen, "WAC", NULL, NULL); if (!rr_output) rr_output = find_builtin_output (rr_screen); return rr_output; } static GsdRROutput * find_output_by_display (GsdRRScreen *rr_screen, GsdWacomDevice *device) { gsize n; GSettings *tablet; GVariant *display; const gchar **edid; GsdRROutput *ret; if (device == NULL) return NULL; ret = NULL; tablet = device->priv->wacom_settings; display = g_settings_get_value (tablet, "display"); edid = g_variant_get_strv (display, &n); if (n != 3) { g_critical ("Expected 'display' key to store %d values; got %"G_GSIZE_FORMAT".", 3, n); goto out; } if (strlen (edid[0]) == 0 || strlen (edid[1]) == 0 || strlen (edid[2]) == 0) goto out; ret = find_output_by_edid (rr_screen, edid[0], edid[1], edid[2]); out: g_free (edid); g_variant_unref (display); return ret; } static gboolean is_on (GsdRROutput *output) { GsdRRCrtc *crtc; crtc = gsd_rr_output_get_crtc (output); if (!crtc) return FALSE; return gsd_rr_crtc_get_current_mode (crtc) != NULL; } static GsdRROutput * find_output_by_monitor (GsdRRScreen *rr_screen, GdkScreen *screen, int monitor) { GsdRROutput **rr_outputs; GsdRROutput *ret; guint i; ret = NULL; rr_outputs = gsd_rr_screen_list_outputs (rr_screen); for (i = 0; rr_outputs[i] != NULL; i++) { GsdRROutput *rr_output; GsdRRCrtc *crtc; int x, y; rr_output = rr_outputs[i]; if (!is_on (rr_output)) continue; crtc = gsd_rr_output_get_crtc (rr_output); if (!crtc) continue; gsd_rr_crtc_get_position (crtc, &x, &y); if (monitor == gdk_screen_get_monitor_at_point (screen, x, y)) { ret = rr_output; break; } } if (ret == NULL) g_warning ("No output found for monitor %d.", monitor); return ret; } static void set_display_by_output (GsdWacomDevice *device, GsdRROutput *rr_output) { GSettings *tablet; GVariant *c_array; GVariant *n_array; gsize nvalues; gchar *o_vendor_s, *o_product_s, *o_serial_s; int o_product, o_serial; const gchar *values[3]; tablet = gsd_wacom_device_get_settings (device); c_array = g_settings_get_value (tablet, "display"); g_variant_get_strv (c_array, &nvalues); if (nvalues != 3) { g_warning ("Unable set set display property. Got %"G_GSIZE_FORMAT" items; expected %d items.\n", nvalues, 4); return; } if (rr_output == NULL || !gsd_rr_output_get_ids_from_edid (rr_output, &o_vendor_s, &o_product, &o_serial)) { o_vendor_s = g_strdup (""); o_product_s = g_strdup (""); o_serial_s = g_strdup (""); } else { o_product_s = g_strdup_printf ("%d", o_product); o_serial_s = g_strdup_printf ("%d", o_serial); } values[0] = o_vendor_s; values[1] = o_product_s; values[2] = o_serial_s; n_array = g_variant_new_strv ((const gchar * const *) &values, 3); g_settings_set_value (tablet, "display", n_array); g_free (o_vendor_s); g_free (o_product_s); g_free (o_serial_s); } static GsdWacomRotation get_rotation_wacom (GsdRRRotation rotation) { guint i; for (i = 0; i < G_N_ELEMENTS (rotation_table); i++) { if (rotation_table[i].rotation & rotation) return (rotation_table[i].rotation_wacom); } g_assert_not_reached (); } void gsd_wacom_device_set_display (GsdWacomDevice *device, int monitor) { GError *error = NULL; GsdRRScreen *rr_screen; GsdRROutput *output = NULL; g_return_if_fail (GSD_IS_WACOM_DEVICE (device)); rr_screen = gsd_rr_screen_new (gdk_screen_get_default (), &error); if (rr_screen == NULL) { g_warning ("Failed to create GsdRRScreen: %s", error->message); g_error_free (error); return; } if (monitor > GSD_WACOM_SET_ALL_MONITORS) output = find_output_by_monitor (rr_screen, gdk_screen_get_default (), monitor); set_display_by_output (device, output); g_object_unref (rr_screen); } static GsdRROutput * find_output (GsdRRScreen *rr_screen, GsdWacomDevice *device) { GsdRROutput *rr_output; rr_output = find_output_by_display (rr_screen, device); if (rr_output == NULL) { if (gsd_wacom_device_is_screen_tablet (device)) { rr_output = find_output_by_heuristic (rr_screen, device); if (rr_output == NULL) g_warning ("No fuzzy match based on heuristics was found."); else g_warning ("Automatically mapping tablet to heuristically-found display."); } } return rr_output; } static void calculate_transformation_matrix (const GdkRectangle mapped, const GdkRectangle desktop, float matrix[NUM_ELEMS_MATRIX]) { float x_scale = (float)mapped.x / desktop.width; float y_scale = (float)mapped.y / desktop.height; float width_scale = (float)mapped.width / desktop.width; float height_scale = (float)mapped.height / desktop.height; matrix[0] = width_scale; matrix[1] = 0.0f; matrix[2] = x_scale; matrix[3] = 0.0f; matrix[4] = height_scale; matrix[5] = y_scale; matrix[6] = 0.0f; matrix[7] = 0.0f; matrix[8] = 1.0f; g_debug ("Matrix is %f,%f,%f,%f,%f,%f,%f,%f,%f.", matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6], matrix[7], matrix[8]); return; } int gsd_wacom_device_get_display_monitor (GsdWacomDevice *device) { GError *error = NULL; GsdRRScreen *rr_screen; GsdRROutput *rr_output; GsdRRMode *mode; GsdRRCrtc *crtc; gint area[4]; g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), GSD_WACOM_SET_ALL_MONITORS); rr_screen = gsd_rr_screen_new (gdk_screen_get_default (), &error); if (rr_screen == NULL) { g_warning ("Failed to create GsdRRScreen: %s", error->message); g_error_free (error); return GSD_WACOM_SET_ALL_MONITORS; } rr_output = find_output (rr_screen, device); if (rr_output == NULL) { g_object_unref (rr_screen); return GSD_WACOM_SET_ALL_MONITORS; } if (!is_on (rr_output)) { g_warning ("Output is not active."); g_object_unref (rr_screen); return GSD_WACOM_SET_ALL_MONITORS; } crtc = gsd_rr_output_get_crtc (rr_output); gsd_rr_crtc_get_position (crtc, &area[0], &area[1]); mode = gsd_rr_crtc_get_current_mode (crtc); area[2] = gsd_rr_mode_get_width (mode); area[3] = gsd_rr_mode_get_height (mode); g_object_unref (rr_screen); if (area[2] <= 0 || area[3] <= 0) { g_warning ("Output has non-positive area."); return GSD_WACOM_SET_ALL_MONITORS; } g_debug ("Area: %d,%d %dx%d", area[0], area[1], area[2], area[3]); return gdk_screen_get_monitor_at_point (gdk_screen_get_default (), area[0], area[1]); } gboolean gsd_wacom_device_get_display_matrix (GsdWacomDevice *device, float matrix[NUM_ELEMS_MATRIX]) { int monitor; GdkRectangle display; GdkRectangle desktop; GdkScreen *screen = gdk_screen_get_default (); matrix[0] = 1.0f; matrix[1] = 0.0f; matrix[2] = 0.0f; matrix[3] = 0.0f; matrix[4] = 1.0f; matrix[5] = 0.0f; matrix[6] = 0.0f; matrix[7] = 0.0f; matrix[8] = 1.0f; monitor = gsd_wacom_device_get_display_monitor (device); if (monitor < 0) return FALSE; desktop.x = 0; desktop.y = 0; desktop.width = gdk_screen_get_width (screen); desktop.height = gdk_screen_get_height (screen); gdk_screen_get_monitor_geometry (screen, monitor, &display); calculate_transformation_matrix (display, desktop, matrix); return TRUE; } GsdWacomRotation gsd_wacom_device_get_display_rotation (GsdWacomDevice *device) { GError *error = NULL; GsdRRScreen *rr_screen; GsdRROutput *rr_output; GsdRRRotation rotation = GSD_RR_ROTATION_0; rr_screen = gsd_rr_screen_new (gdk_screen_get_default (), &error); if (rr_screen == NULL) { g_warning ("Failed to create GsdRRScreen: %s", error->message); g_error_free (error); return GSD_WACOM_ROTATION_NONE; } rr_output = find_output (rr_screen, device); if (rr_output) { GsdRRCrtc *crtc = gsd_rr_output_get_crtc (rr_output); if (crtc) rotation = gsd_rr_crtc_get_current_rotation (crtc); } g_object_unref (rr_screen); return get_rotation_wacom (rotation); } static void add_stylus_to_device (GsdWacomDevice *device, const char *settings_path, int id) { const WacomStylus *wstylus; wstylus = libwacom_stylus_get_for_id (db, id); if (wstylus) { GsdWacomStylus *stylus; char *stylus_settings_path; GSettings *settings; if (device->priv->type == WACOM_TYPE_STYLUS && libwacom_stylus_is_eraser (wstylus)) return; if (device->priv->type == WACOM_TYPE_ERASER && libwacom_stylus_is_eraser (wstylus) == FALSE) return; stylus_settings_path = g_strdup_printf ("%s0x%x/", settings_path, id); if (device->priv->type == WACOM_TYPE_STYLUS) { settings = g_settings_new_with_path (WACOM_STYLUS_SCHEMA, stylus_settings_path); stylus = gsd_wacom_stylus_new (device, wstylus, settings); } else { settings = g_settings_new_with_path (WACOM_ERASER_SCHEMA, stylus_settings_path); stylus = gsd_wacom_stylus_new (device, wstylus, settings); } g_free (stylus_settings_path); device->priv->styli = g_list_prepend (device->priv->styli, stylus); } } int gsd_wacom_device_get_num_modes (GsdWacomDevice *device, int group_id) { int num_modes; g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), -1); num_modes = GPOINTER_TO_INT (g_hash_table_lookup (device->priv->num_modes, GINT_TO_POINTER(group_id))); return num_modes; } int gsd_wacom_device_get_current_mode (GsdWacomDevice *device, int group_id) { int current_idx; g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), -1); current_idx = GPOINTER_TO_INT (g_hash_table_lookup (device->priv->modes, GINT_TO_POINTER(group_id))); /* That means that the mode doesn't exist, see gsd_wacom_device_add_modes() */ g_return_val_if_fail (current_idx != 0, -1); return current_idx; } int gsd_wacom_device_set_next_mode (GsdWacomDevice *device, GsdWacomTabletButton *button) { GList *l; int current_idx; int num_modes; int num_switches; int group_id; g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), -1); group_id = button->group_id; current_idx = 0; num_switches = 0; num_modes = GPOINTER_TO_INT (g_hash_table_lookup (device->priv->num_modes, GINT_TO_POINTER(group_id))); /* * Check if we have multiple mode-switch buttons for that * group, and if so, compute the current index based on * the position in the list... */ for (l = device->priv->buttons; l != NULL; l = l->next) { GsdWacomTabletButton *b = l->data; if (b->type != WACOM_TABLET_BUTTON_TYPE_HARDCODED) continue; if (button->group_id == b->group_id) num_switches++; if (g_strcmp0 (button->id, b->id) == 0) current_idx = num_switches; } /* We should at least have found the current mode-switch button... * If not, then it means that the given button is not a valid * mode-switch. */ g_return_val_if_fail (num_switches != 0, -1); /* Only one mode-switch? cycle through the modes */ if (num_switches == 1) { current_idx = gsd_wacom_device_get_current_mode (device, group_id); /* gsd_wacom_device_get_current_mode() returns -1 when the mode doesn't exist */ g_return_val_if_fail (current_idx > 0, -1); current_idx++; } if (current_idx > num_modes) current_idx = 1; g_hash_table_insert (device->priv->modes, GINT_TO_POINTER (group_id), GINT_TO_POINTER (current_idx)); return current_idx; } static int flags_to_group (WacomButtonFlags flags) { if (flags & WACOM_BUTTON_RING_MODESWITCH) return 1; if (flags & WACOM_BUTTON_RING2_MODESWITCH) return 2; if (flags & WACOM_BUTTON_TOUCHSTRIP_MODESWITCH) return 3; if (flags & WACOM_BUTTON_TOUCHSTRIP2_MODESWITCH) return 4; return 0; } static GList * gsd_wacom_device_add_ring_modes (WacomDevice *wacom_device, const char *settings_path, WacomButtonFlags direction) { GList *l; guint num_modes; guint group; guint i; char *name, *id; l = NULL; if ((direction & WACOM_BUTTON_POSITION_LEFT) && libwacom_has_ring (wacom_device)) { num_modes = libwacom_get_ring_num_modes (wacom_device); group = flags_to_group (WACOM_BUTTON_RING_MODESWITCH); if (num_modes == 0) { /* If no mode is available, we use "left-ring-mode-1" for backward compat */ l = g_list_append (l, gsd_wacom_tablet_button_new (_("Left Ring"), "left-ring-mode-1", settings_path, WACOM_TABLET_BUTTON_TYPE_RING, WACOM_TABLET_BUTTON_POS_LEFT, group, 0, GSD_WACOM_NO_LED)); } else { for (i = 1; i <= num_modes; i++) { name = g_strdup_printf (_("Left Ring Mode #%d"), i); id = g_strdup_printf ("left-ring-mode-%d", i); l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_RING, WACOM_TABLET_BUTTON_POS_LEFT, group, i - 1, GSD_WACOM_NO_LED)); g_free (name); g_free (id); } } } else if ((direction & WACOM_BUTTON_POSITION_RIGHT) && libwacom_has_ring2 (wacom_device)) { num_modes = libwacom_get_ring2_num_modes (wacom_device); group = flags_to_group (WACOM_BUTTON_RING2_MODESWITCH); if (num_modes == 0) { /* If no mode is available, we use "right-ring-mode-1" for backward compat */ l = g_list_append (l, gsd_wacom_tablet_button_new (_("Right Ring"), "right-ring-mode-1", settings_path, WACOM_TABLET_BUTTON_TYPE_RING, WACOM_TABLET_BUTTON_POS_RIGHT, group, 0, GSD_WACOM_NO_LED)); } else { for (i = 1; i <= num_modes; i++) { name = g_strdup_printf (_("Right Ring Mode #%d"), i); id = g_strdup_printf ("right-ring-mode-%d", i); l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_RING, WACOM_TABLET_BUTTON_POS_RIGHT, group, i - 1, GSD_WACOM_NO_LED)); g_free (name); g_free (id); } } } return l; } static GList * gsd_wacom_device_add_strip_modes (WacomDevice *wacom_device, const char *settings_path, WacomButtonFlags direction) { GList *l; guint num_modes; guint num_strips; guint group; guint i; char *name, *id; l = NULL; num_strips = libwacom_get_num_strips (wacom_device); if (num_strips > 2) g_warning ("Unhandled number of touchstrips: %d", num_strips); if ((direction & WACOM_BUTTON_POSITION_LEFT) && num_strips >= 1) { num_modes = libwacom_get_strips_num_modes (wacom_device); group = flags_to_group (WACOM_BUTTON_TOUCHSTRIP_MODESWITCH); if (num_modes == 0) { /* If no mode is available, we use "left-strip-mode-1" for backward compat */ l = g_list_append (l, gsd_wacom_tablet_button_new (_("Left Touchstrip"), "left-strip-mode-1", settings_path, WACOM_TABLET_BUTTON_TYPE_STRIP, WACOM_TABLET_BUTTON_POS_LEFT, group, 0, GSD_WACOM_NO_LED)); } else { for (i = 1; i <= num_modes; i++) { name = g_strdup_printf (_("Left Touchstrip Mode #%d"), i); id = g_strdup_printf ("left-strip-mode-%d", i); l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_STRIP, WACOM_TABLET_BUTTON_POS_LEFT, group, i - 1, GSD_WACOM_NO_LED)); g_free (name); g_free (id); } } } else if ((direction & WACOM_BUTTON_POSITION_RIGHT) && num_strips >= 2) { num_modes = libwacom_get_strips_num_modes (wacom_device); group = flags_to_group (WACOM_BUTTON_TOUCHSTRIP2_MODESWITCH); if (num_modes == 0) { /* If no mode is available, we use "right-strip-mode-1" for backward compat */ l = g_list_append (l, gsd_wacom_tablet_button_new (_("Right Touchstrip"), "right-strip-mode-1", settings_path, WACOM_TABLET_BUTTON_TYPE_STRIP, WACOM_TABLET_BUTTON_POS_RIGHT, group, 0, GSD_WACOM_NO_LED)); } else { for (i = 1; i <= num_modes; i++) { name = g_strdup_printf (_("Right Touchstrip Mode #%d"), i); id = g_strdup_printf ("right-strip-mode-%d", i); l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_STRIP, WACOM_TABLET_BUTTON_POS_RIGHT, group, i - 1, GSD_WACOM_NO_LED)); g_free (name); g_free (id); } } } return l; } static char * gsd_wacom_device_modeswitch_name (WacomButtonFlags flags, guint button_num) { if (flags & WACOM_BUTTON_RINGS_MODESWITCH) { if (flags & WACOM_BUTTON_POSITION_LEFT) return g_strdup_printf (_("Left Touchring Mode Switch")); else return g_strdup_printf (_("Right Touchring Mode Switch")); } else if (flags & WACOM_BUTTON_TOUCHSTRIPS_MODESWITCH) { if (flags & WACOM_BUTTON_POSITION_LEFT) return g_strdup_printf (_("Left Touchstrip Mode Switch")); else return g_strdup_printf (_("Right Touchstrip Mode Switch")); } g_warning ("Unhandled modeswitch and direction combination"); return g_strdup_printf (_("Mode Switch #%d"), button_num); } static GsdWacomTabletButtonType gsd_wacom_device_button_pos (WacomButtonFlags flags) { if (flags & WACOM_BUTTON_POSITION_LEFT) return WACOM_TABLET_BUTTON_POS_LEFT; else if (flags & WACOM_BUTTON_POSITION_RIGHT) return WACOM_TABLET_BUTTON_POS_RIGHT; else if (flags & WACOM_BUTTON_POSITION_TOP) return WACOM_TABLET_BUTTON_POS_TOP; else if (flags & WACOM_BUTTON_POSITION_BOTTOM) return WACOM_TABLET_BUTTON_POS_BOTTOM; g_warning ("Unhandled button position"); return WACOM_TABLET_BUTTON_POS_UNDEF; } static GList * gsd_wacom_device_add_buttons_dir (WacomDevice *wacom_device, const char *settings_path, WacomButtonFlags direction, const char *button_str, const char *button_str_id) { GList *l; guint num_buttons, i, button_num; char *name, *id; l = NULL; button_num = 1; num_buttons = libwacom_get_num_buttons (wacom_device); for (i = 'A'; i < 'A' + num_buttons; i++) { WacomButtonFlags flags; flags = libwacom_get_button_flag (wacom_device, i); if (!(flags & direction)) continue; /* Ignore mode switches */ if (flags & WACOM_BUTTON_MODESWITCH) continue; name = g_strdup_printf (button_str, button_num++); id = g_strdup_printf ("%s%c", button_str_id, i); l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_NORMAL, gsd_wacom_device_button_pos (flags), flags_to_group (flags), -1, GSD_WACOM_NO_LED)); g_free (name); g_free (id); } /* Handle modeswitches */ for (i = 'A'; i < 'A' + num_buttons; i++) { WacomButtonFlags flags; char *name, *id; int status_led; flags = libwacom_get_button_flag (wacom_device, i); if (!(flags & direction)) continue; /* Ignore non-mode switches */ if (!(flags & WACOM_BUTTON_MODESWITCH)) continue; name = gsd_wacom_device_modeswitch_name (flags, button_num++); id = g_strdup_printf ("%s%c", button_str_id, i); status_led = libwacom_get_button_led_group (wacom_device, i); l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_HARDCODED, gsd_wacom_device_button_pos (flags), flags_to_group (flags), -1, status_led)); g_free (name); g_free (id); } /* Handle touch{strips,rings} */ if (libwacom_has_ring2 (wacom_device) || libwacom_has_ring (wacom_device)) l = g_list_concat (l, gsd_wacom_device_add_ring_modes (wacom_device, settings_path, direction)); if (libwacom_get_num_strips (wacom_device) > 0) l = g_list_concat (l, gsd_wacom_device_add_strip_modes (wacom_device, settings_path, direction)); return l; } static void gsd_wacom_device_add_buttons (GsdWacomDevice *device, WacomDevice *wacom_device, const char *settings_path) { GList *l, *ret; ret = NULL; l = gsd_wacom_device_add_buttons_dir (wacom_device, settings_path, WACOM_BUTTON_POSITION_LEFT, _("Left Button #%d"), "button"); if (l) ret = l; l = gsd_wacom_device_add_buttons_dir (wacom_device, settings_path, WACOM_BUTTON_POSITION_RIGHT, _("Right Button #%d"), "button"); if (l) ret = g_list_concat (ret, l); l = gsd_wacom_device_add_buttons_dir (wacom_device, settings_path, WACOM_BUTTON_POSITION_TOP, _("Top Button #%d"), "button"); if (l) ret = g_list_concat (ret, l); l = gsd_wacom_device_add_buttons_dir (wacom_device, settings_path, WACOM_BUTTON_POSITION_BOTTOM, _("Bottom Button #%d"), "button"); if (l) ret = g_list_concat (ret, l); device->priv->buttons = ret; } static void gsd_wacom_device_get_modeswitches (WacomDevice *wacom_device, gint *num_rings, gint *num_strips) { *num_strips = libwacom_get_num_strips (wacom_device); if (libwacom_has_ring2 (wacom_device)) *num_rings = 2; else if (libwacom_has_ring (wacom_device)) *num_rings = 1; else *num_rings = 0; } static void gsd_wacom_device_add_modes (GsdWacomDevice *device, WacomDevice *wacom_device) { GList *l; device->priv->modes = g_hash_table_new (g_direct_hash, g_direct_equal); device->priv->num_modes = g_hash_table_new (g_direct_hash, g_direct_equal); for (l = device->priv->buttons; l != NULL; l = l->next) { GsdWacomTabletButton *button = l->data; if (button->group_id > 0) g_hash_table_insert (device->priv->modes, GINT_TO_POINTER (button->group_id), GINT_TO_POINTER (1)); /* See flags_to_group() for group ID/button type matches */ if (button->group_id == 1) { g_hash_table_insert (device->priv->num_modes, GINT_TO_POINTER (button->group_id), GINT_TO_POINTER (libwacom_get_ring_num_modes (wacom_device))); } else if (button->group_id == 2) { g_hash_table_insert (device->priv->num_modes, GINT_TO_POINTER (button->group_id), GINT_TO_POINTER (libwacom_get_ring2_num_modes (wacom_device))); } else if (button->group_id == 3 || button->group_id == 4) { g_hash_table_insert (device->priv->num_modes, GINT_TO_POINTER (button->group_id), GINT_TO_POINTER (libwacom_get_strips_num_modes (wacom_device))); } } } static void gsd_wacom_device_update_from_db (GsdWacomDevice *device, WacomDevice *wacom_device, const char *identifier) { char *settings_path; WacomIntegrationFlags integration_flags; settings_path = g_strdup_printf (WACOM_DEVICE_CONFIG_BASE, device->priv->machine_id, libwacom_get_match (wacom_device)); device->priv->wacom_settings = g_settings_new_with_path (WACOM_TABLET_SCHEMA, settings_path); device->priv->name = g_strdup (libwacom_get_name (wacom_device)); device->priv->layout_path = g_strdup (libwacom_get_layout_filename (wacom_device)); device->priv->reversible = libwacom_is_reversible (wacom_device); integration_flags = libwacom_get_integration_flags (wacom_device); device->priv->is_screen_tablet = (integration_flags & WACOM_DEVICE_INTEGRATED_DISPLAY); device->priv->is_isd = (integration_flags & WACOM_DEVICE_INTEGRATED_SYSTEM); if (device->priv->is_screen_tablet) { if (!device->priv->is_isd) device->priv->icon_name = "wacom-tablet-cintiq"; else device->priv->icon_name = "wacom-tablet-pc"; } else { device->priv->icon_name = "wacom-tablet"; } if (device->priv->type == WACOM_TYPE_PAD) { gsd_wacom_device_get_modeswitches (wacom_device, &device->priv->num_rings, &device->priv->num_strips); gsd_wacom_device_add_buttons (device, wacom_device, settings_path); gsd_wacom_device_add_modes (device, wacom_device); } if (device->priv->type == WACOM_TYPE_STYLUS || device->priv->type == WACOM_TYPE_ERASER) { const int *ids; int num_styli; guint i; ids = libwacom_get_supported_styli (wacom_device, &num_styli); g_assert (num_styli >= 1); for (i = 0; i < num_styli; i++) add_stylus_to_device (device, settings_path, ids[i]); device->priv->styli = g_list_reverse (device->priv->styli); } g_free (settings_path); } static GObject * gsd_wacom_device_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GsdWacomDevice *device; GdkDeviceManager *device_manager; XDeviceInfo *device_info; WacomDevice *wacom_device; int n_devices; guint i; device = GSD_WACOM_DEVICE (G_OBJECT_CLASS (gsd_wacom_device_parent_class)->constructor (type, n_construct_properties, construct_properties)); if (device->priv->gdk_device == NULL) return G_OBJECT (device); device_manager = gdk_display_get_device_manager (gdk_display_get_default ()); g_object_get (device_manager, "opcode", &device->priv->opcode, NULL); g_object_get (device->priv->gdk_device, "device-id", &device->priv->device_id, NULL); device_info = XListInputDevices (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &n_devices); if (device_info == NULL) { g_warning ("Could not list any input devices through XListInputDevices()"); goto end; } for (i = 0; i < n_devices; i++) { if (device_info[i].id == device->priv->device_id) { device->priv->type = get_device_type (&device_info[i]); device->priv->tool_name = g_strdup (device_info[i].name); break; } } XFreeDeviceList (device_info); if (device->priv->type == WACOM_TYPE_INVALID) goto end; device->priv->path = xdevice_get_device_node (device->priv->device_id); if (device->priv->path == NULL) { g_warning ("Could not get the device node path for ID '%d'", device->priv->device_id); device->priv->type = WACOM_TYPE_INVALID; goto end; } if (db == NULL) db = libwacom_database_new (); wacom_device = libwacom_new_from_path (db, device->priv->path, FALSE, NULL); if (!wacom_device) { WacomError *wacom_error; g_debug ("Creating fallback driver for wacom tablet '%s' ('%s')", gdk_device_get_name (device->priv->gdk_device), device->priv->path); device->priv->is_fallback = TRUE; wacom_error = libwacom_error_new (); wacom_device = libwacom_new_from_path (db, device->priv->path, TRUE, wacom_error); if (wacom_device == NULL) { g_warning ("Failed to create fallback wacom device for '%s': %s (%d)", device->priv->path, libwacom_error_get_message (wacom_error), libwacom_error_get_code (wacom_error)); libwacom_error_free (&wacom_error); device->priv->type = WACOM_TYPE_INVALID; goto end; } } gsd_wacom_device_update_from_db (device, wacom_device, device->priv->path); libwacom_destroy (wacom_device); if (device->priv->type == WACOM_TYPE_STYLUS || device->priv->type == WACOM_TYPE_ERASER) { setup_property_notify (device); } end: return G_OBJECT (device); } static void gsd_wacom_device_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GsdWacomDevice *device; device = GSD_WACOM_DEVICE (object); switch (prop_id) { case PROP_GDK_DEVICE: device->priv->gdk_device = g_value_get_pointer (value); break; case PROP_LAST_STYLUS: device->priv->last_stylus = g_value_get_pointer (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gsd_wacom_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GsdWacomDevice *device; device = GSD_WACOM_DEVICE (object); switch (prop_id) { case PROP_GDK_DEVICE: g_value_set_pointer (value, device->priv->gdk_device); break; case PROP_LAST_STYLUS: g_value_set_pointer (value, device->priv->last_stylus); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gsd_wacom_device_class_init (GsdWacomDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructor = gsd_wacom_device_constructor; object_class->finalize = gsd_wacom_device_finalize; object_class->set_property = gsd_wacom_device_set_property; object_class->get_property = gsd_wacom_device_get_property; g_type_class_add_private (klass, sizeof (GsdWacomDevicePrivate)); g_object_class_install_property (object_class, PROP_GDK_DEVICE, g_param_spec_pointer ("gdk-device", "gdk-device", "gdk-device", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_LAST_STYLUS, g_param_spec_pointer ("last-stylus", "last-stylus", "last-stylus", G_PARAM_READWRITE)); } static void gsd_wacom_device_init (GsdWacomDevice *device) { device->priv = GSD_WACOM_DEVICE_GET_PRIVATE (device); device->priv->type = WACOM_TYPE_INVALID; if (g_file_get_contents ("/etc/machine-id", &device->priv->machine_id, NULL, NULL) == FALSE) if (g_file_get_contents ("/var/lib/dbus/machine-id", &device->priv->machine_id, NULL, NULL) == FALSE) device->priv->machine_id = g_strdup ("00000000000000000000000000000000"); device->priv->machine_id = g_strstrip (device->priv->machine_id); } static void gsd_wacom_device_finalize (GObject *object) { GsdWacomDevice *device; GsdWacomDevicePrivate *p; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_WACOM_DEVICE (object)); device = GSD_WACOM_DEVICE (object); g_return_if_fail (device->priv != NULL); p = device->priv; if (p->wacom_settings != NULL) { g_object_unref (p->wacom_settings); p->wacom_settings = NULL; } g_list_foreach (p->styli, (GFunc) g_object_unref, NULL); g_list_free (p->styli); g_list_foreach (p->buttons, (GFunc) gsd_wacom_tablet_button_free, NULL); g_list_free (p->buttons); g_free (p->name); p->name = NULL; g_free (p->tool_name); p->tool_name = NULL; g_free (p->path); p->path = NULL; g_free (p->machine_id); p->machine_id = NULL; if (p->modes) { g_hash_table_destroy (p->modes); p->modes = NULL; } if (p->num_modes) { g_hash_table_destroy (p->num_modes); p->num_modes = NULL; } g_clear_pointer (&p->layout_path, g_free); gdk_window_remove_filter (NULL, (GdkFilterFunc) filter_events, device); G_OBJECT_CLASS (gsd_wacom_device_parent_class)->finalize (object); } GsdWacomDevice * gsd_wacom_device_new (GdkDevice *device) { return GSD_WACOM_DEVICE (g_object_new (GSD_TYPE_WACOM_DEVICE, "gdk-device", device, NULL)); } GList * gsd_wacom_device_list_styli (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); return g_list_copy (device->priv->styli); } GsdWacomStylus * gsd_wacom_device_get_stylus_for_type (GsdWacomDevice *device, GsdWacomStylusType type) { GList *l; g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); for (l = device->priv->styli; l != NULL; l = l->next) { GsdWacomStylus *stylus = l->data; if (gsd_wacom_stylus_get_stylus_type (stylus) == type) return stylus; } return NULL; } const char * gsd_wacom_device_get_name (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); return device->priv->name; } const char * gsd_wacom_device_get_layout_path (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); return device->priv->layout_path; } const char * gsd_wacom_device_get_path (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); return device->priv->path; } const char * gsd_wacom_device_get_icon_name (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); return device->priv->icon_name; } const char * gsd_wacom_device_get_tool_name (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); return device->priv->tool_name; } gboolean gsd_wacom_device_reversible (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), FALSE); return device->priv->reversible; } gboolean gsd_wacom_device_is_screen_tablet (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), FALSE); return device->priv->is_screen_tablet; } gboolean gsd_wacom_device_is_isd (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), FALSE); return device->priv->is_isd; } gboolean gsd_wacom_device_is_fallback (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), FALSE); return device->priv->is_fallback; } gint gsd_wacom_device_get_num_strips (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), 0); return device->priv->num_strips; } gint gsd_wacom_device_get_num_rings (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), 0); return device->priv->num_rings; } GSettings * gsd_wacom_device_get_settings (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); return device->priv->wacom_settings; } void gsd_wacom_device_set_current_stylus (GsdWacomDevice *device, int stylus_id) { GList *l; GsdWacomStylus *stylus; g_return_if_fail (GSD_IS_WACOM_DEVICE (device)); /* Don't change anything if the stylus is already set */ if (device->priv->last_stylus != NULL) { GsdWacomStylus *stylus = device->priv->last_stylus; if (stylus->priv->id == stylus_id) return; } for (l = device->priv->styli; l; l = l->next) { stylus = l->data; /* Set a nice default if 0x0 */ if (stylus_id == 0x0 && stylus->priv->type == WSTYLUS_GENERAL) { g_object_set (device, "last-stylus", stylus, NULL); return; } if (stylus->priv->id == stylus_id) { g_object_set (device, "last-stylus", stylus, NULL); return; } } /* Setting the default stylus to be the generic one */ for (l = device->priv->styli; l; l = l->next) { stylus = l->data; /* Set a nice default if 0x0 */ if (stylus->priv->type == WSTYLUS_GENERAL) { g_debug ("Could not find stylus ID 0x%x for tablet '%s', setting general pen ID 0x%x instead", stylus_id, device->priv->name, stylus->priv->id); g_object_set (device, "last-stylus", stylus, NULL); return; } } g_warning ("Could not set the current stylus ID 0x%x for tablet '%s', no general pen found", stylus_id, device->priv->name); /* Setting the default stylus to be the first one */ g_assert (device->priv->styli); stylus = device->priv->styli->data; g_object_set (device, "last-stylus", stylus, NULL); } GsdWacomDeviceType gsd_wacom_device_get_device_type (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), WACOM_TYPE_INVALID); return device->priv->type; } gint * gsd_wacom_device_get_area (GsdWacomDevice *device) { int i, id; XDevice *xdevice; Atom area, realtype; int rc, realformat; unsigned long nitems, bytes_after; unsigned char *data = NULL; gint *device_area; g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); g_object_get (device->priv->gdk_device, "device-id", &id, NULL); area = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Wacom Tablet Area", False); gdk_error_trap_push (); xdevice = XOpenDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), id); if (gdk_error_trap_pop () || (device == NULL)) return NULL; gdk_error_trap_push (); rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, area, 0, 4, False, XA_INTEGER, &realtype, &realformat, &nitems, &bytes_after, &data); if (gdk_error_trap_pop () || rc != Success || realtype == None || bytes_after != 0 || nitems != 4) { XCloseDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice); return NULL; } device_area = g_new0 (int, nitems); for (i = 0; i < nitems; i++) device_area[i] = ((long *)data)[i]; XFree (data); XCloseDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice); return device_area; } const char * gsd_wacom_device_type_to_string (GsdWacomDeviceType type) { switch (type) { case WACOM_TYPE_INVALID: return "Invalid"; case WACOM_TYPE_STYLUS: return "Stylus"; case WACOM_TYPE_ERASER: return "Eraser"; case WACOM_TYPE_CURSOR: return "Cursor"; case WACOM_TYPE_PAD: return "Pad"; case WACOM_TYPE_TOUCH: return "Touch"; default: return "Unknown type"; } } GList * gsd_wacom_device_get_buttons (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); return g_list_copy (device->priv->buttons); } static GsdWacomTabletButton * find_button_with_id (GsdWacomDevice *device, const char *id) { GList *l; for (l = device->priv->buttons; l != NULL; l = l->next) { GsdWacomTabletButton *button = l->data; if (g_strcmp0 (button->id, id) == 0) return button; } return NULL; } static GsdWacomTabletButton * find_button_with_index (GsdWacomDevice *device, const char *id, int index) { GsdWacomTabletButton *button; char *str; str = g_strdup_printf ("%s-mode-%d", id, index); button = find_button_with_id (device, str); g_free (str); return button; } GsdWacomTabletButton * gsd_wacom_device_get_button (GsdWacomDevice *device, int button, GtkDirectionType *dir) { int index; if (button <= 26) { char *id; GsdWacomTabletButton *ret; int physical_button; /* mouse_button = physical_button < 4 ? physical_button : physical_button + 4 */ if (button > 4) physical_button = button - 4; else physical_button = button; id = g_strdup_printf ("button%c", 'A' + physical_button - 1); ret = find_button_with_id (device, id); g_free (id); return ret; } switch (button) { case 90: case 92: case 94: case 96: *dir = GTK_DIR_UP; break; case 91: case 93: case 95: case 97: *dir = GTK_DIR_DOWN; break; default: ;; } /* The group ID is implied by the button number */ switch (button) { case 90: case 91: index = GPOINTER_TO_INT (g_hash_table_lookup (device->priv->modes, GINT_TO_POINTER (1))); return find_button_with_index (device, "left-ring", index); case 92: case 93: index = GPOINTER_TO_INT (g_hash_table_lookup (device->priv->modes, GINT_TO_POINTER (2))); return find_button_with_index (device, "right-ring", index); case 94: case 95: index = GPOINTER_TO_INT (g_hash_table_lookup (device->priv->modes, GINT_TO_POINTER (3))); return find_button_with_index (device, "left-strip", index); case 96: case 97: index = GPOINTER_TO_INT (g_hash_table_lookup (device->priv->modes, GINT_TO_POINTER (4))); return find_button_with_index (device, "right-strip", index); default: return NULL; } } GsdWacomRotation gsd_wacom_device_rotation_name_to_type (const char *rotation) { guint i; g_return_val_if_fail (rotation != NULL, GSD_WACOM_ROTATION_NONE); for (i = 0; i < G_N_ELEMENTS (rotation_table); i++) { if (strcmp (rotation_table[i].rotation_string, rotation) == 0) return (rotation_table[i].rotation_wacom); } return GSD_WACOM_ROTATION_NONE; } const char * gsd_wacom_device_rotation_type_to_name (GsdWacomRotation type) { guint i; for (i = 0; i < G_N_ELEMENTS (rotation_table); i++) { if (rotation_table[i].rotation_wacom == type) return (rotation_table[i].rotation_string); } return "none"; } GsdWacomDevice * gsd_wacom_device_create_fake (GsdWacomDeviceType type, const char *name, const char *tool_name) { GsdWacomDevice *device; GsdWacomDevicePrivate *priv; WacomDevice *wacom_device; device = GSD_WACOM_DEVICE (g_object_new (GSD_TYPE_WACOM_DEVICE, NULL)); if (db == NULL) db = libwacom_database_new (); wacom_device = libwacom_new_from_name (db, name, NULL); if (wacom_device == NULL) return NULL; priv = device->priv; priv->type = type; priv->tool_name = g_strdup (tool_name); gsd_wacom_device_update_from_db (device, wacom_device, name); libwacom_destroy (wacom_device); return device; } GList * gsd_wacom_device_create_fake_cintiq (void) { GsdWacomDevice *device; GList *devices; device = gsd_wacom_device_create_fake (WACOM_TYPE_STYLUS, "Wacom Cintiq 21UX2", "Wacom Cintiq 21UX2 stylus"); devices = g_list_prepend (NULL, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_ERASER, "Wacom Cintiq 21UX2", "Wacom Cintiq 21UX2 eraser"); devices = g_list_prepend (devices, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_PAD, "Wacom Cintiq 21UX2", "Wacom Cintiq 21UX2 pad"); devices = g_list_prepend (devices, device); return devices; } GList * gsd_wacom_device_create_fake_bt (void) { GsdWacomDevice *device; GList *devices; device = gsd_wacom_device_create_fake (WACOM_TYPE_STYLUS, "Wacom Graphire Wireless", "Graphire Wireless stylus"); devices = g_list_prepend (NULL, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_ERASER, "Wacom Graphire Wireless", "Graphire Wireless eraser"); devices = g_list_prepend (devices, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_PAD, "Wacom Graphire Wireless", "Graphire Wireless pad"); devices = g_list_prepend (devices, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_CURSOR, "Wacom Graphire Wireless", "Graphire Wireless cursor"); devices = g_list_prepend (devices, device); return devices; } GList * gsd_wacom_device_create_fake_x201 (void) { GsdWacomDevice *device; GList *devices; device = gsd_wacom_device_create_fake (WACOM_TYPE_STYLUS, "Wacom Serial Tablet WACf004", "Wacom Serial Tablet WACf004 stylus"); devices = g_list_prepend (NULL, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_ERASER, "Wacom Serial Tablet WACf004", "Wacom Serial Tablet WACf004 eraser"); devices = g_list_prepend (devices, device); return devices; } GList * gsd_wacom_device_create_fake_intuos4 (void) { GsdWacomDevice *device; GList *devices; device = gsd_wacom_device_create_fake (WACOM_TYPE_STYLUS, "Wacom Intuos4 6x9", "Wacom Intuos4 6x9 stylus"); devices = g_list_prepend (NULL, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_ERASER, "Wacom Intuos4 6x9", "Wacom Intuos4 6x9 eraser"); devices = g_list_prepend (devices, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_PAD, "Wacom Intuos4 6x9", "Wacom Intuos4 6x9 pad"); devices = g_list_prepend (devices, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_CURSOR, "Wacom Intuos4 6x9", "Wacom Intuos4 6x9 cursor"); devices = g_list_prepend (devices, device); return devices; }