/* GDK - The GIMP Drawing Kit * Copyright (C) 2015 Red Hat * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #include "gdkseatdefaultprivate.h" #include "gdkdevicetoolprivate.h" typedef struct _GdkSeatDefaultPrivate GdkSeatDefaultPrivate; struct _GdkSeatDefaultPrivate { GdkDevice *master_pointer; GdkDevice *master_keyboard; GList *slave_pointers; GList *slave_keyboards; GdkSeatCapabilities capabilities; GPtrArray *tools; }; #define KEYBOARD_EVENTS (GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | \ GDK_FOCUS_CHANGE_MASK) #define TOUCH_EVENTS (GDK_TOUCH_MASK) #define POINTER_EVENTS (GDK_POINTER_MOTION_MASK | \ GDK_BUTTON_PRESS_MASK | \ GDK_BUTTON_RELEASE_MASK | \ GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK | \ GDK_ENTER_NOTIFY_MASK | \ GDK_LEAVE_NOTIFY_MASK | \ GDK_PROXIMITY_IN_MASK | \ GDK_PROXIMITY_OUT_MASK) G_DEFINE_TYPE_WITH_PRIVATE (GdkSeatDefault, gdk_seat_default, GDK_TYPE_SEAT) static void gdk_seat_dispose (GObject *object) { GdkSeatDefault *seat = GDK_SEAT_DEFAULT (object); GdkSeatDefaultPrivate *priv = gdk_seat_default_get_instance_private (seat); GList *l; if (priv->master_pointer) { gdk_seat_device_removed (GDK_SEAT (seat), priv->master_pointer); g_clear_object (&priv->master_pointer); } if (priv->master_keyboard) { gdk_seat_device_removed (GDK_SEAT (seat), priv->master_keyboard); g_clear_object (&priv->master_pointer); } for (l = priv->slave_pointers; l; l = l->next) { gdk_seat_device_removed (GDK_SEAT (seat), l->data); g_object_unref (l->data); } for (l = priv->slave_keyboards; l; l = l->next) { gdk_seat_device_removed (GDK_SEAT (seat), l->data); g_object_unref (l->data); } if (priv->tools) { g_ptr_array_unref (priv->tools); priv->tools = NULL; } g_list_free (priv->slave_pointers); g_list_free (priv->slave_keyboards); priv->slave_pointers = NULL; priv->slave_keyboards = NULL; G_OBJECT_CLASS (gdk_seat_default_parent_class)->dispose (object); } static GdkSeatCapabilities gdk_seat_default_get_capabilities (GdkSeat *seat) { GdkSeatDefaultPrivate *priv; priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat)); return priv->capabilities; } static GdkGrabStatus gdk_seat_default_grab (GdkSeat *seat, GdkWindow *window, GdkSeatCapabilities capabilities, gboolean owner_events, GdkCursor *cursor, const GdkEvent *event, GdkSeatGrabPrepareFunc prepare_func, gpointer prepare_func_data) { GdkSeatDefaultPrivate *priv; guint32 evtime = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME; GdkGrabStatus status = GDK_GRAB_SUCCESS; priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat)); if (prepare_func) (prepare_func) (seat, window, prepare_func_data); if (!gdk_window_is_visible (window)) { g_critical ("Window %p has not been made visible in GdkSeatGrabPrepareFunc", window); return GDK_GRAB_NOT_VIEWABLE; } G_GNUC_BEGIN_IGNORE_DEPRECATIONS; if (capabilities & GDK_SEAT_CAPABILITY_ALL_POINTING) { /* ALL_POINTING spans 3 capabilities; get the mask for the ones we have */ GdkEventMask pointer_evmask = 0; /* We let tablet styli take over the pointer cursor */ if (capabilities & (GDK_SEAT_CAPABILITY_POINTER | GDK_SEAT_CAPABILITY_TABLET_STYLUS)) { pointer_evmask |= POINTER_EVENTS; } if (capabilities & GDK_SEAT_CAPABILITY_TOUCH) pointer_evmask |= TOUCH_EVENTS; status = gdk_device_grab (priv->master_pointer, window, GDK_OWNERSHIP_NONE, owner_events, pointer_evmask, cursor, evtime); } if (status == GDK_GRAB_SUCCESS && capabilities & GDK_SEAT_CAPABILITY_KEYBOARD) { status = gdk_device_grab (priv->master_keyboard, window, GDK_OWNERSHIP_NONE, owner_events, KEYBOARD_EVENTS, cursor, evtime); if (status != GDK_GRAB_SUCCESS) { if (capabilities & ~GDK_SEAT_CAPABILITY_KEYBOARD) gdk_device_ungrab (priv->master_pointer, evtime); gdk_window_hide (window); } } G_GNUC_END_IGNORE_DEPRECATIONS; return status; } static void gdk_seat_default_ungrab (GdkSeat *seat) { GdkSeatDefaultPrivate *priv; priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat)); G_GNUC_BEGIN_IGNORE_DEPRECATIONS; gdk_device_ungrab (priv->master_pointer, GDK_CURRENT_TIME); gdk_device_ungrab (priv->master_keyboard, GDK_CURRENT_TIME); G_GNUC_END_IGNORE_DEPRECATIONS; } static GdkDevice * gdk_seat_default_get_master (GdkSeat *seat, GdkSeatCapabilities capability) { GdkSeatDefaultPrivate *priv; priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat)); /* There must be only one flag set */ switch (capability) { case GDK_SEAT_CAPABILITY_POINTER: case GDK_SEAT_CAPABILITY_TOUCH: return priv->master_pointer; case GDK_SEAT_CAPABILITY_KEYBOARD: return priv->master_keyboard; default: g_warning ("Unhandled capability %x", capability); break; } return NULL; } static GdkSeatCapabilities device_get_capability (GdkDevice *device) { GdkInputSource source; source = gdk_device_get_source (device); switch (source) { case GDK_SOURCE_KEYBOARD: return GDK_SEAT_CAPABILITY_KEYBOARD; case GDK_SOURCE_TOUCHSCREEN: return GDK_SEAT_CAPABILITY_TOUCH; case GDK_SOURCE_MOUSE: case GDK_SOURCE_TOUCHPAD: default: return GDK_SEAT_CAPABILITY_POINTER; } return GDK_SEAT_CAPABILITY_NONE; } static GList * append_filtered (GList *list, GList *devices, GdkSeatCapabilities capabilities) { GList *l; for (l = devices; l; l = l->next) { GdkSeatCapabilities device_cap; device_cap = device_get_capability (l->data); if ((device_cap & capabilities) != 0) list = g_list_prepend (list, l->data); } return list; } static GList * gdk_seat_default_get_slaves (GdkSeat *seat, GdkSeatCapabilities capabilities) { GdkSeatDefaultPrivate *priv; GList *devices = NULL; priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat)); if (capabilities & (GDK_SEAT_CAPABILITY_POINTER | GDK_SEAT_CAPABILITY_TOUCH)) devices = append_filtered (devices, priv->slave_pointers, capabilities); if (capabilities & GDK_SEAT_CAPABILITY_KEYBOARD) devices = append_filtered (devices, priv->slave_keyboards, capabilities); return devices; } static GdkDeviceTool * gdk_seat_default_get_tool (GdkSeat *seat, guint64 serial) { GdkSeatDefaultPrivate *priv; GdkDeviceTool *tool; guint i; priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat)); if (!priv->tools) return NULL; for (i = 0; i < priv->tools->len; i++) { tool = g_ptr_array_index (priv->tools, i); if (tool->serial == serial) return tool; } return NULL; } static void gdk_seat_default_class_init (GdkSeatDefaultClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GdkSeatClass *seat_class = GDK_SEAT_CLASS (klass); object_class->dispose = gdk_seat_dispose; seat_class->get_capabilities = gdk_seat_default_get_capabilities; seat_class->grab = gdk_seat_default_grab; seat_class->ungrab = gdk_seat_default_ungrab; seat_class->get_master = gdk_seat_default_get_master; seat_class->get_slaves = gdk_seat_default_get_slaves; seat_class->get_tool = gdk_seat_default_get_tool; } static void gdk_seat_default_init (GdkSeatDefault *seat) { } GdkSeat * gdk_seat_default_new_for_master_pair (GdkDevice *pointer, GdkDevice *keyboard) { GdkSeatDefaultPrivate *priv; GdkDisplay *display; GdkSeat *seat; display = gdk_device_get_display (pointer); seat = g_object_new (GDK_TYPE_SEAT_DEFAULT, "display", display, NULL); priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat)); priv->master_pointer = g_object_ref (pointer); priv->master_keyboard = g_object_ref (keyboard); gdk_seat_device_added (seat, priv->master_pointer); gdk_seat_device_added (seat, priv->master_keyboard); return seat; } void gdk_seat_default_add_slave (GdkSeatDefault *seat, GdkDevice *device) { GdkSeatDefaultPrivate *priv; GdkSeatCapabilities capability; g_return_if_fail (GDK_IS_SEAT_DEFAULT (seat)); g_return_if_fail (GDK_IS_DEVICE (device)); priv = gdk_seat_default_get_instance_private (seat); capability = device_get_capability (device); if (capability & (GDK_SEAT_CAPABILITY_POINTER | GDK_SEAT_CAPABILITY_TOUCH)) priv->slave_pointers = g_list_prepend (priv->slave_pointers, g_object_ref (device)); else if (capability & GDK_SEAT_CAPABILITY_KEYBOARD) priv->slave_keyboards = g_list_prepend (priv->slave_keyboards, g_object_ref (device)); else { g_critical ("Unhandled capability %x for device '%s'", capability, gdk_device_get_name (device)); return; } priv->capabilities |= capability; gdk_seat_device_added (GDK_SEAT (seat), device); } void gdk_seat_default_remove_slave (GdkSeatDefault *seat, GdkDevice *device) { GdkSeatDefaultPrivate *priv; GList *l; g_return_if_fail (GDK_IS_SEAT_DEFAULT (seat)); g_return_if_fail (GDK_IS_DEVICE (device)); priv = gdk_seat_default_get_instance_private (seat); if (g_list_find (priv->slave_pointers, device)) { priv->slave_pointers = g_list_remove (priv->slave_pointers, device); priv->capabilities &= ~(GDK_SEAT_CAPABILITY_POINTER | GDK_SEAT_CAPABILITY_TOUCH); for (l = priv->slave_pointers; l; l = l->next) priv->capabilities |= device_get_capability (GDK_DEVICE (l->data)); gdk_seat_device_removed (GDK_SEAT (seat), device); } else if (g_list_find (priv->slave_keyboards, device)) { priv->slave_keyboards = g_list_remove (priv->slave_keyboards, device); if (priv->slave_keyboards == NULL) priv->capabilities &= ~GDK_SEAT_CAPABILITY_KEYBOARD; gdk_seat_device_removed (GDK_SEAT (seat), device); } } void gdk_seat_default_add_tool (GdkSeatDefault *seat, GdkDeviceTool *tool) { GdkSeatDefaultPrivate *priv; g_return_if_fail (GDK_IS_SEAT_DEFAULT (seat)); g_return_if_fail (tool != NULL); priv = gdk_seat_default_get_instance_private (seat); if (!priv->tools) priv->tools = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); g_ptr_array_add (priv->tools, g_object_ref (tool)); g_signal_emit_by_name (seat, "tool-added", tool); } void gdk_seat_default_remove_tool (GdkSeatDefault *seat, GdkDeviceTool *tool) { GdkSeatDefaultPrivate *priv; g_return_if_fail (GDK_IS_SEAT_DEFAULT (seat)); g_return_if_fail (tool != NULL); priv = gdk_seat_default_get_instance_private (seat); if (tool != gdk_seat_get_tool (GDK_SEAT (seat), gdk_device_tool_get_serial (tool))) return; g_signal_emit_by_name (seat, "tool-removed", tool); g_ptr_array_remove (priv->tools, tool); }