diff --git a/gdk/gdkdevice.c b/gdk/gdkdevice.c index 601cbd0ddb9164fa7f911895d26029673cea592d..22dcad570b13c5c5a5104087c3a99648f6539fa8 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 07720b21f1d5cefbc9d523e6be7ae95e6eb18925..fb3149b29db26c2837065b013cb87be06f9064a8 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__ */ diff --git a/gdk/gdkkeys.c b/gdk/gdkkeys.c index fabd8c06c032fc2443bc923ec44c38acd6d173e7..1f24f274d0b4557ec43058c416f5c15e77dd1c16 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 b75d2cd414deb6e569d4f634ea90a23889814f8d..ae48f245157f7fe61f1374ea8c2e4b36932c49cf 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 96f67b1519f132d0bacfe49dea24f088a2316897..b9b14d96e98103061a85bfa5b7f3b461c3ea4d32 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 */ diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c index 6c63ad34f8a7d783e2f4a17897c48492f939ae0a..c2489925ba4a9d61272766c6f7209d887ecb0b29 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 0c23df59235e0c8dba93dd99c25fd7d6cb2487e1..3e93d29fd04a2f4ebd9d2657306a47647927719e 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 diff --git a/gdk/x11/gdkkeys-x11.c b/gdk/x11/gdkkeys-x11.c index 577e4a18c012869f8eb25cdd340a1a7369a64ad3..0fbeec41591acf56efa2638ce0b045c44c0f54fe 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; } diff --git a/gtk/inspector/general.c b/gtk/inspector/general.c index a3a341956bc5be4e1d938a7856e7ca426a4bad5d..76940e3322e8a4987215c55a4ce011a07b5351c2 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 *