From 58992d3f21575e1a87db96b45c0f2e279d3078cf Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Wed, 15 Jan 2025 14:26:58 +0100 Subject: [PATCH 1/5] gdk: Add a GdkKeymap layout names and active layout index API Add new API to retrieve the names of the layouts and the index of the active layout from the GdkKeymap. The actual implementation is backend dependent. (cherry picked from commit 7897b2447f4c6f8ee33bceaf3769790bfff70cb3) --- gdk/gdkkeys.c | 42 ++++++++++++++++++++++++++++++++++++++++++ gdk/gdkkeys.h | 4 ++++ gdk/gdkkeysprivate.h | 2 ++ 3 files changed, 48 insertions(+) diff --git a/gdk/gdkkeys.c b/gdk/gdkkeys.c index fabd8c06c03..1f24f274d0b 100644 --- a/gdk/gdkkeys.c +++ b/gdk/gdkkeys.c @@ -398,6 +398,48 @@ gdk_keymap_get_modifier_state (GdkKeymap *keymap) return 0; } +/** + * gdk_keymap_get_active_layout_index: + * @keymap: a `GdkKeymap` + * + * Returns the index of the active layout. + * + * If there is no valid active layout, this function will return -1; + * + * Returns: The layout index of the active layout or -1. + */ +gint +gdk_keymap_get_active_layout_index (GdkKeymap *keymap) +{ + g_return_val_if_fail (GDK_IS_KEYMAP (keymap), -1); + + if (GDK_KEYMAP_GET_CLASS (keymap)->get_active_layout_index) + return GDK_KEYMAP_GET_CLASS (keymap)->get_active_layout_index (keymap); + + return -1; +} + + +/** + * gdk_keymap_get_layout_names: + * @keymap: a `GdkKeymap` + * + * Returns the layouts as a %NULL-terminated array of strings. + * + * Returns: (transfer full) (nullable) (array zero-terminated=1): + * %NULL-terminated array of strings of layouts, + */ +gchar ** +gdk_keymap_get_layout_names (GdkKeymap *keymap) +{ + g_return_val_if_fail (GDK_IS_KEYMAP (keymap), NULL); + + if (GDK_KEYMAP_GET_CLASS (keymap)->get_layout_names) + return GDK_KEYMAP_GET_CLASS (keymap)->get_layout_names (keymap); + + return NULL; +} + /** * gdk_keymap_get_entries_for_keyval: * @keymap: a #GdkKeymap diff --git a/gdk/gdkkeys.h b/gdk/gdkkeys.h index b75d2cd414d..ae48f245157 100644 --- a/gdk/gdkkeys.h +++ b/gdk/gdkkeys.h @@ -131,6 +131,10 @@ GDK_AVAILABLE_IN_3_4 GdkModifierType gdk_keymap_get_modifier_mask (GdkKeymap *keymap, GdkModifierIntent intent); +GDK_AVAILABLE_IN_3_24 +gchar ** gdk_keymap_get_layout_names (GdkKeymap *keymap); +GDK_AVAILABLE_IN_3_24 +gint gdk_keymap_get_active_layout_index (GdkKeymap *keymap); /* Key values */ diff --git a/gdk/gdkkeysprivate.h b/gdk/gdkkeysprivate.h index 96f67b1519f..b9b14d96e98 100644 --- a/gdk/gdkkeysprivate.h +++ b/gdk/gdkkeysprivate.h @@ -63,6 +63,8 @@ struct _GdkKeymapClass GdkModifierType (*get_modifier_mask) (GdkKeymap *keymap, GdkModifierIntent intent); guint (* get_modifier_state) (GdkKeymap *keymap); + gint (* get_active_layout_index) (GdkKeymap *keymap); + gchar ** (* get_layout_names) (GdkKeymap *keymap); /* Signals */ -- GitLab From 8fc7b201cac8c335d04fa715e45d17927c8f58e2 Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Wed, 15 Jan 2025 14:30:32 +0100 Subject: [PATCH 2/5] gdk: Add active layout index and names API to GdkDevice This adds the entry point API to get the active layout index and names from the GdkDevice. This is backend dependent and only applicable to keyboards. (cherry picked from commit 4e58a4cddf5387311a54e76b509326bf28440104) --- gdk/gdkdevice.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++ gdk/gdkdevice.h | 6 +++- 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/gdk/gdkdevice.c b/gdk/gdkdevice.c index 601cbd0ddb9..22dcad570b1 100644 --- a/gdk/gdkdevice.c +++ b/gdk/gdkdevice.c @@ -96,6 +96,9 @@ enum { PROP_NUM_TOUCHES, PROP_AXES, PROP_TOOL, + PROP_LAYOUT_NAMES, + PROP_ACTIVE_LAYOUT_INDEX, + LAST_PROP }; @@ -330,6 +333,36 @@ gdk_device_class_init (GdkDeviceClass *klass) GDK_TYPE_DEVICE_TOOL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + /** + * GdkDevice:active-layout-index: + * + * The index of the keyboard active layout of a `GdkDevice`. + * + * Will be -1 if there is no valid active layout. + * + * This is only relevant for keyboard devices. + * + * Since: 3.24 + */ + device_props[PROP_ACTIVE_LAYOUT_INDEX] = + g_param_spec_int ("active-layout-index", NULL, NULL, + -1, G_MAXINT, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * GdkDevice:layout-names: + * + * The names of the keyboard layouts of a `GdkDevice`. + * + * This is only relevant for keyboard devices. + * + * Since: 3.24 + */ + device_props[PROP_LAYOUT_NAMES] = + g_param_spec_boxed ("layout-names", NULL, NULL, + G_TYPE_STRV, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, LAST_PROP, device_props); /** @@ -527,6 +560,13 @@ gdk_device_get_property (GObject *object, case PROP_TOOL: g_value_set_object (value, device->last_tool); break; + case PROP_ACTIVE_LAYOUT_INDEX: + g_value_set_int (value, gdk_device_get_active_layout_index (device)); + break; + case PROP_LAYOUT_NAMES: + g_value_set_boxed (value, gdk_device_get_layout_names (device)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2060,3 +2100,53 @@ gdk_device_get_input_mode (GdkDevice *device) { return device->mode; } + +/** + * gdk_device_get_active_layout_index: + * @device: a `GdkDevice` + * + * Retrieves the index of the active layout of the keyboard. + * + * If there is no valid active layout for the `GdkDevice`, this function will + * return -1; + * + * This is only relevant for keyboard devices. + * + * Returns: The layout index of the active layout or -1. + * + * Since: 4.18 + */ +gint +gdk_device_get_active_layout_index (GdkDevice *device) +{ + GdkKeymap *keymap = gdk_keymap_get_for_display (device->display); + + if (device->source == GDK_SOURCE_KEYBOARD) + return gdk_keymap_get_active_layout_index (keymap); + + return -1; +} + +/** + * gdk_device_get_layout_names: + * @device: a `GdkDevice` + * + * Retrieves the names of the layouts of the keyboard. + * + * This is only relevant for keyboard devices. + * + * Returns: (transfer full) (nullable) (array zero-terminated=1): + * %NULL-terminated array of strings of layouts, + * + * Since: 4.18 + */ +char ** +gdk_device_get_layout_names (GdkDevice *device) +{ + GdkKeymap *keymap = gdk_keymap_get_for_display (device->display); + + if (device->source == GDK_SOURCE_KEYBOARD) + return gdk_keymap_get_layout_names (keymap); + + return NULL; +} diff --git a/gdk/gdkdevice.h b/gdk/gdkdevice.h index 07720b21f1d..fb3149b29db 100644 --- a/gdk/gdkdevice.h +++ b/gdk/gdkdevice.h @@ -161,7 +161,6 @@ void gdk_device_set_axis_use (GdkDevice *device, guint index_, GdkAxisUse use); - GDK_AVAILABLE_IN_ALL void gdk_device_get_state (GdkDevice *device, GdkWindow *window, @@ -265,6 +264,11 @@ GdkSeat *gdk_device_get_seat (GdkDevice *device); GDK_AVAILABLE_IN_3_22 GdkAxisFlags gdk_device_get_axes (GdkDevice *device); +GDK_AVAILABLE_IN_3_24 +gint gdk_device_get_active_layout_index (GdkDevice *device); +GDK_AVAILABLE_IN_3_24 +gchar ** gdk_device_get_layout_names (GdkDevice *device); + G_END_DECLS #endif /* __GDK_DEVICE_H__ */ -- GitLab From b8dcaf0724edc500b687817aa954f2351d1ef52a Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Tue, 14 Jan 2025 17:03:28 +0100 Subject: [PATCH 3/5] gdk/wayland: Add keymap get_active_layout_index/get_layout_names Add the Wayland implementation for the new get_active_layout_index() and get_layout_names() API. (cherry picked from commit ff44236baf55380a62ddd0f4edd56da4ab53b8e1) --- gdk/wayland/gdkdevice-wayland.c | 27 ++++++++++++++++++++++ gdk/wayland/gdkkeys-wayland.c | 41 +++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c index 6c63ad34f8a..c2489925ba4 100644 --- a/gdk/wayland/gdkdevice-wayland.c +++ b/gdk/wayland/gdkdevice-wayland.c @@ -1944,16 +1944,30 @@ keyboard_handle_keymap (void *data, { GdkWaylandSeat *seat = data; PangoDirection direction; + int layout_index; + gchar **old_layout_names, **new_layout_names; direction = gdk_keymap_get_direction (seat->keymap); + layout_index = gdk_keymap_get_active_layout_index (seat->keymap); + old_layout_names = gdk_keymap_get_layout_names (seat->keymap); _gdk_wayland_keymap_update_from_fd (seat->keymap, format, fd, size); + new_layout_names = gdk_keymap_get_layout_names (seat->keymap); + g_signal_emit_by_name (seat->keymap, "keys-changed"); g_signal_emit_by_name (seat->keymap, "state-changed"); if (direction != gdk_keymap_get_direction (seat->keymap)) g_signal_emit_by_name (seat->keymap, "direction-changed"); + if (layout_index != gdk_keymap_get_active_layout_index (seat->keymap)) + g_object_notify (G_OBJECT (seat->master_keyboard), "active-layout-index"); + if (!g_strv_equal ((const gchar * const *) old_layout_names, + (const gchar * const *) new_layout_names)) + g_object_notify (G_OBJECT (seat->master_keyboard), "layout-names"); + + g_strfreev (old_layout_names); + g_strfreev (new_layout_names); } static void @@ -2358,18 +2372,31 @@ keyboard_handle_modifiers (void *data, GdkKeymap *keymap; struct xkb_state *xkb_state; PangoDirection direction; + int layout_index; + gchar **old_layout_names, **new_layout_names; keymap = seat->keymap; direction = gdk_keymap_get_direction (keymap); xkb_state = _gdk_wayland_keymap_get_xkb_state (keymap); + layout_index = gdk_keymap_get_active_layout_index (keymap); + old_layout_names = gdk_keymap_get_layout_names (keymap); xkb_state_update_mask (xkb_state, mods_depressed, mods_latched, mods_locked, group, 0, 0); seat->key_modifiers = gdk_keymap_get_modifier_state (keymap); + new_layout_names = gdk_keymap_get_layout_names (keymap); g_signal_emit_by_name (keymap, "state-changed"); if (direction != gdk_keymap_get_direction (keymap)) g_signal_emit_by_name (keymap, "direction-changed"); + if (layout_index != gdk_keymap_get_active_layout_index (keymap)) + g_object_notify (G_OBJECT (seat->master_keyboard), "active-layout-index"); + if (!g_strv_equal ((const gchar * const *) old_layout_names, + (const gchar * const *) new_layout_names)) + g_object_notify (G_OBJECT (seat->master_keyboard), "layout-names"); + + g_strfreev (old_layout_names); + g_strfreev (new_layout_names); } static void diff --git a/gdk/wayland/gdkkeys-wayland.c b/gdk/wayland/gdkkeys-wayland.c index 0c23df59235..3e93d29fd04 100644 --- a/gdk/wayland/gdkkeys-wayland.c +++ b/gdk/wayland/gdkkeys-wayland.c @@ -375,6 +375,45 @@ gdk_wayland_keymap_get_modifier_state (GdkKeymap *keymap) return get_gdk_modifiers (xkb_keymap, mods); } +static int +gdk_wayland_keymap_get_active_layout_index (GdkKeymap *keymap) +{ + struct xkb_keymap *xkb_keymap; + struct xkb_state *xkb_state; + + xkb_keymap = _gdk_wayland_keymap_get_xkb_keymap (keymap); + xkb_state = _gdk_wayland_keymap_get_xkb_state (keymap); + + for (int i = 0; i < xkb_keymap_num_layouts (xkb_keymap); i++) + { + if (xkb_state_layout_index_is_active (xkb_state, i, XKB_STATE_LAYOUT_EFFECTIVE)) + return i; + } + + return -1; +} + +static char ** +gdk_wayland_keymap_get_layout_names (GdkKeymap *keymap) +{ + struct xkb_keymap *xkb_keymap; + GArray *names_builder; + int num_layouts; + + xkb_keymap = _gdk_wayland_keymap_get_xkb_keymap (keymap); + num_layouts = xkb_keymap_num_layouts (xkb_keymap); + names_builder = g_array_sized_new (TRUE, FALSE, sizeof (gchar *), num_layouts); + + for (int i = 0; i < num_layouts; i++) + { + const char *layout_name = xkb_keymap_layout_get_name (xkb_keymap, i); + char *val = g_strdup (layout_name ? layout_name : ""); + g_array_append_val (names_builder, val); + } + + return (gchar **)(void *) g_array_free (names_builder, FALSE); +} + static void gdk_wayland_keymap_add_virtual_modifiers (GdkKeymap *keymap, GdkModifierType *state) @@ -457,6 +496,8 @@ _gdk_wayland_keymap_class_init (GdkWaylandKeymapClass *klass) keymap_class->get_modifier_state = gdk_wayland_keymap_get_modifier_state; keymap_class->add_virtual_modifiers = gdk_wayland_keymap_add_virtual_modifiers; keymap_class->map_virtual_modifiers = gdk_wayland_keymap_map_virtual_modifiers; + keymap_class->get_active_layout_index = gdk_wayland_keymap_get_active_layout_index; + keymap_class->get_layout_names = gdk_wayland_keymap_get_layout_names; } static void -- GitLab From 9b1536d67ec247fd20b94abad63a13ddeb2ba779 Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Fri, 17 Jan 2025 13:32:10 +0100 Subject: [PATCH 4/5] gdk/x11: Add keymap get_active_layout_index/get_layout_names Add the X11 implementation for the new get_active_layout_index() and get_layout_names() API. (cherry picked from commit 87d80ab7ba8ce5c79ea75194d16f7ea3afe6eeb4) --- gdk/x11/gdkkeys-x11.c | 84 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/gdk/x11/gdkkeys-x11.c b/gdk/x11/gdkkeys-x11.c index 577e4a18c01..0fbeec41591 100644 --- a/gdk/x11/gdkkeys-x11.c +++ b/gdk/x11/gdkkeys-x11.c @@ -91,6 +91,8 @@ struct _GdkX11Keymap * cache. */ DirectionCacheEntry group_direction_cache[4]; + + int layout_index; #endif }; @@ -126,6 +128,7 @@ gdk_x11_keymap_init (GdkX11Keymap *keymap) keymap->xkb_desc = NULL; keymap->current_group_atom = 0; keymap->current_cache_serial = 0; + keymap->layout_index = -1; #endif } @@ -199,6 +202,16 @@ update_modmap (Display *display, } } +static int +get_xkb_layout_index (GdkX11Keymap *keymap_x11) +{ + XkbStateRec state_rec; + + XkbGetState (KEYMAP_XDISPLAY (GDK_KEYMAP (keymap_x11)), XkbUseCoreKbd, &state_rec); + + return (state_rec.group); +} + static XkbDescPtr get_xkb (GdkX11Keymap *keymap_x11) { @@ -219,6 +232,9 @@ get_xkb (GdkX11Keymap *keymap_x11) XkbGetNames (xdisplay, XkbGroupNamesMask | XkbVirtualModNamesMask, keymap_x11->xkb_desc); update_modmap (xdisplay, keymap_x11); + + if (keymap_x11->layout_index < 0) + keymap_x11->layout_index = get_xkb_layout_index (keymap_x11); } else if (keymap_x11->current_serial != display_x11->keymap_serial) { @@ -646,6 +662,8 @@ _gdk_x11_keymap_state_changed (GdkDisplay *display, if (display_x11->keymap) { GdkX11Keymap *keymap_x11 = GDK_X11_KEYMAP (display_x11->keymap); + GdkDevice *keyboard; + int layout_index; if (update_direction (keymap_x11, XkbStateGroup (&xkb_event->state))) g_signal_emit_by_name (keymap_x11, "direction-changed"); @@ -654,6 +672,14 @@ _gdk_x11_keymap_state_changed (GdkDisplay *display, xkb_event->state.locked_mods, xkb_event->state.mods)) g_signal_emit_by_name (keymap_x11, "state-changed"); + + keyboard = gdk_seat_get_keyboard (gdk_display_get_default_seat (display)); + layout_index = xkb_event->state.group; + if (keymap_x11->layout_index != layout_index) + { + keymap_x11->layout_index = layout_index; + g_object_notify (G_OBJECT (keyboard), "active-layout-index"); + } } } @@ -687,7 +713,17 @@ _gdk_x11_keymap_keys_changed (GdkDisplay *display) ++display_x11->keymap_serial; if (display_x11->keymap) - g_signal_emit_by_name (display_x11->keymap, "keys_changed", 0); + { + g_signal_emit_by_name (display_x11->keymap, "keys_changed", 0); + + if (KEYMAP_USE_XKB (display_x11->keymap)) + { + GdkDevice *keyboard; + + keyboard = gdk_seat_get_keyboard (gdk_display_get_default_seat (display)); + g_object_notify (G_OBJECT (keyboard), "layout-names"); + } + } } static PangoDirection @@ -1378,6 +1414,50 @@ gdk_x11_keymap_translate_keyboard_state (GdkKeymap *keymap, return tmp_keyval != NoSymbol; } +static int +gdk_x11_keymap_get_active_layout_index (GdkKeymap *keymap) +{ + if (KEYMAP_USE_XKB (keymap)) + { + GdkX11Keymap *keymap_x11 = GDK_X11_KEYMAP (keymap); + + if (keymap_x11->layout_index < 0) + keymap_x11->layout_index = get_xkb_layout_index (keymap_x11); + + return keymap_x11->layout_index; + } + + return -1; +} + +static char ** +gdk_x11_keymap_get_layout_names (GdkKeymap *keymap) +{ + if (KEYMAP_USE_XKB (keymap)) + { + GdkDisplay *display = keymap->display; + GdkX11Keymap *keymap_x11 = GDK_X11_KEYMAP (keymap); + XkbDescPtr xkb = get_xkb (keymap_x11); + GArray *names_builder; + int num_layouts; + + num_layouts = get_num_groups (keymap, xkb); + names_builder = g_array_sized_new (TRUE, FALSE, sizeof (gchar *), num_layouts); + + for (int i = 0; i < num_layouts; i++) + { + const char *layout_name = + gdk_x11_get_xatom_name_for_display (display, xkb->names->groups[i]); + char *val = g_strdup (layout_name ? layout_name : ""); + g_array_append_val (names_builder, val); + } + + return (gchar **)(void *) g_array_free (names_builder, FALSE); + } + + return NULL; +} + /** * gdk_x11_keymap_get_group_for_state: * @keymap: (type GdkX11Keymap): a #GdkX11Keymap @@ -1592,4 +1672,6 @@ gdk_x11_keymap_class_init (GdkX11KeymapClass *klass) keymap_class->add_virtual_modifiers = gdk_x11_keymap_add_virtual_modifiers; keymap_class->map_virtual_modifiers = gdk_x11_keymap_map_virtual_modifiers; keymap_class->get_modifier_mask = gdk_x11_keymap_get_modifier_mask; + keymap_class->get_active_layout_index = gdk_x11_keymap_get_active_layout_index; + keymap_class->get_layout_names = gdk_x11_keymap_get_layout_names; } -- GitLab From 97639d84808c73ce0403303f41e2c66324f07d47 Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Fri, 17 Jan 2025 17:30:16 +0100 Subject: [PATCH 5/5] inspector: Use the new GdkDevice get_layout_names() API The nice side effect is that it works with both X11 and Wayland now. (cherry picked from commit ae92d8e4105dd2d7e4e18a9a19166c659c9c51f4) --- gtk/inspector/general.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/gtk/inspector/general.c b/gtk/inspector/general.c index a3a341956bc..76940e3322e 100644 --- a/gtk/inspector/general.c +++ b/gtk/inspector/general.c @@ -623,6 +623,29 @@ add_device (GtkInspectorGeneral *gen, add_label_row (gen, GTK_LIST_BOX (gen->priv->device_box), "Touches", text, 20); g_free (text); } + + if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD) + { + char **layout_names; + int n_layouts, active_layout; + GString *s; + + layout_names = gdk_device_get_layout_names (device); + active_layout = gdk_device_get_active_layout_index (device); + n_layouts = g_strv_length (layout_names); + s = g_string_new (""); + for (int i = 0; i < n_layouts; i++) + { + if (s->len > 0) + g_string_append (s, ", "); + g_string_append (s, layout_names[i]); + if (i == active_layout) + g_string_append (s, "*"); + } + + add_label_row (gen, GTK_LIST_BOX (gen->priv->device_box), "Layouts", s->str, 20); + g_string_free (s, TRUE); + } } static char * -- GitLab