/* * AT-SPI - Assistive Technology Service Provider Interface * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) * * Copyright 2020 SUSE LLC. * * 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.1 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, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "atspi-device-legacy.h" #include "atspi-private.h" #ifdef HAVE_X11 #include #include #include #include #endif typedef struct { guint keycode; guint modifier; } AtspiLegacyKeyModifier; typedef struct _AtspiDeviceLegacyPrivate AtspiDeviceLegacyPrivate; struct _AtspiDeviceLegacyPrivate { AtspiDeviceListener *listener; #ifdef HAVE_X11 Display *display; Window window; #endif GSList *modifiers; guint virtual_mods_enabled; gboolean keyboard_grabbed; unsigned int numlock_physical_mask; }; GObjectClass *device_legacy_parent_class; G_DEFINE_TYPE_WITH_CODE (AtspiDeviceLegacy, atspi_device_legacy, ATSPI_TYPE_DEVICE, G_ADD_PRIVATE (AtspiDeviceLegacy)) static guint find_virtual_mapping (AtspiDeviceLegacy *legacy_device, gint keycode) { AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); GSList *l; for (l = priv->modifiers; l; l = l->next) { AtspiLegacyKeyModifier *entry = l->data; if (entry->keycode == keycode) return entry->modifier; } return 0; } static void set_virtual_modifier (AtspiDeviceLegacy *legacy_device, gint keycode, gboolean enabled) { AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); guint modifier = find_virtual_mapping (legacy_device, keycode); if (enabled) priv->virtual_mods_enabled |= modifier; else priv->virtual_mods_enabled &= ~modifier; } static gboolean key_cb (AtspiDeviceEvent *event, void *user_data) { AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (user_data); AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); gboolean ret = priv->keyboard_grabbed; guint modifiers; g_object_ref (legacy_device); set_virtual_modifier (legacy_device, event->hw_code, event->type == (AtspiEventType) ATSPI_KEY_PRESS); modifiers = event->modifiers | priv->virtual_mods_enabled; if (modifiers & (1 << ATSPI_MODIFIER_NUMLOCK)) modifiers &= ~priv->numlock_physical_mask; ret |= atspi_device_notify_key (ATSPI_DEVICE (legacy_device), event->type == (AtspiEventType) ATSPI_KEY_PRESS, event->hw_code, event->id, modifiers, event->event_string); g_boxed_free (ATSPI_TYPE_DEVICE_EVENT, event); g_object_unref (legacy_device); return ret; } static guint atspi_device_legacy_get_locked_modifiers (AtspiDevice *device) { #ifdef HAVE_X11 AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device); AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); XkbStateRec state_rec; memset (&state_rec, 0, sizeof (state_rec)); XkbGetState (priv->display, XkbUseCoreKbd, &state_rec); return state_rec.locked_mods; #else return 0; #endif } static gboolean check_virtual_modifier (AtspiDeviceLegacy *legacy_device, guint modifier) { AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); GSList *l; if (modifier == (1 << ATSPI_MODIFIER_NUMLOCK)) return TRUE; for (l = priv->modifiers; l; l = l->next) { AtspiLegacyKeyModifier *entry = l->data; if (entry->modifier == modifier) return TRUE; } return FALSE; } static guint get_unused_virtual_modifier (AtspiDeviceLegacy *legacy_device) { guint ret = 0x1000; while (ret < 0x10000) { if (!check_virtual_modifier (legacy_device, ret)) return ret; ret <<= 1; } return 0; } static guint atspi_device_legacy_map_modifier (AtspiDevice *device, gint keycode) { AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device); AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); guint ret; AtspiLegacyKeyModifier *entry; #ifdef HAVE_X11 XkbDescPtr desc; desc = XkbGetMap (priv->display, XkbModifierMapMask, XkbUseCoreKbd); if (keycode < desc->min_key_code || keycode >= desc->max_key_code) { XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE); g_warning ("Passed invalid keycode %d", keycode); return 0; } ret = desc->map->modmap[keycode]; XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE); if (ret & (ShiftMask | ControlMask)) return ret; #endif ret = find_virtual_mapping (legacy_device, keycode); if (ret) return ret; ret = get_unused_virtual_modifier (legacy_device); entry = g_new (AtspiLegacyKeyModifier, 1); entry->keycode = keycode; entry->modifier = ret; priv->modifiers = g_slist_append (priv->modifiers, entry); return ret; } static void atspi_device_legacy_unmap_modifier (AtspiDevice *device, gint keycode) { AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device); AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); GSList *l; for (l = priv->modifiers; l; l = l->next) { AtspiLegacyKeyModifier *entry = l->data; if (entry->keycode == keycode) { priv->modifiers = g_slist_remove (priv->modifiers, entry); g_free (entry); return; } } } static guint atspi_device_legacy_get_modifier (AtspiDevice *device, gint keycode) { AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device); #ifdef HAVE_X11 AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); XkbDescPtr desc; guint ret; desc = XkbGetMap (priv->display, XkbModifierMapMask, XkbUseCoreKbd); if (keycode < desc->min_key_code || keycode >= desc->max_key_code) { XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE); g_warning ("Passed invalid keycode %d", keycode); return 0; } ret = desc->map->modmap[keycode]; XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE); if (ret) return ret; #endif return find_virtual_mapping (legacy_device, keycode); } static gboolean atspi_device_legacy_grab_keyboard (AtspiDevice *device) { AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device); AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); priv->keyboard_grabbed = TRUE; return TRUE; } static void atspi_device_legacy_ungrab_keyboard (AtspiDevice *device) { AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device); AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); priv->keyboard_grabbed = FALSE; } static void atspi_device_legacy_init (AtspiDeviceLegacy *device) { AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (device); gint i; priv->listener = atspi_device_listener_new (key_cb, device, NULL); for (i = 0; i < 256; i++) atspi_register_keystroke_listener (priv->listener, NULL, i, 3, ATSPI_KEYLISTENER_SYNCHRONOUS | ATSPI_KEYLISTENER_CANCONSUME, NULL); #ifdef HAVE_X11 priv->display = XOpenDisplay (""); if (priv->display) priv->window = DefaultRootWindow (priv->display); priv->numlock_physical_mask = XkbKeysymToModifiers (priv->display, XK_Num_Lock); #endif } static void atspi_device_legacy_finalize (GObject *object) { AtspiDeviceLegacy *device = ATSPI_DEVICE_LEGACY (object); AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (device); g_clear_object (&priv->listener); device_legacy_parent_class->finalize (object); } static void atspi_device_legacy_class_init (AtspiDeviceLegacyClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; AtspiDeviceClass *device_class = ATSPI_DEVICE_CLASS (klass); device_legacy_parent_class = g_type_class_peek_parent (klass); object_class->finalize = atspi_device_legacy_finalize; device_class->map_modifier = atspi_device_legacy_map_modifier; device_class->unmap_modifier = atspi_device_legacy_unmap_modifier; device_class->get_modifier = atspi_device_legacy_get_modifier; device_class->get_locked_modifiers = atspi_device_legacy_get_locked_modifiers; device_class->grab_keyboard = atspi_device_legacy_grab_keyboard; device_class->ungrab_keyboard = atspi_device_legacy_ungrab_keyboard; } /** * atspi_device_legacy_new: * * Creates a new #AtspiDeviceLegacy. * * Returns: (transfer full): a pointer to a newly-created #AtspiDeviceLegacy. * **/ AtspiDeviceLegacy * atspi_device_legacy_new () { AtspiDeviceLegacy *device = g_object_new (atspi_device_legacy_get_type (), NULL); return device; }