From 93ae868987edc6f73dc07bc7e2d6721a653bd1e3 Mon Sep 17 00:00:00 2001 From: Giovanni Campagna Date: Wed, 4 Sep 2013 11:11:39 +0200 Subject: [PATCH] wayland: sync the keymap from X to wayland When X clients change the keyboard map, the also update a property on the root window. We can notice that and rebuild our data structures with the new values, as well as inform the wayland clients. This is a terrible hack, and it's not how we want to implement things in 3.12, but it's enough to have the same keyboard layout in the shell, in X clients and in wayland clients in 3.10, until we decide on the fate of the keyboard g-s-d plugin. https://bugzilla.gnome.org/show_bug.cgi?id=707446 --- src/core/display.c | 38 +++++++ src/core/xprops.c | 75 ++++++++++++++ src/core/xprops.h | 5 + src/meta/atomnames.h | 1 + src/wayland/meta-wayland-keyboard.c | 147 ++++++++++++++++++---------- src/wayland/meta-wayland-keyboard.h | 13 ++- 6 files changed, 226 insertions(+), 53 deletions(-) diff --git a/src/core/display.c b/src/core/display.c index 14ee96ef9..74193f492 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -2191,6 +2191,40 @@ handle_window_focus_event (MetaDisplay *display, } } +static void +reload_xkb_rules (MetaScreen *screen) +{ + MetaWaylandCompositor *compositor; + char **names; + int n_names; + gboolean ok; + const char *rules, *model, *layout, *variant, *options; + + compositor = meta_wayland_compositor_get_default (); + + ok = meta_prop_get_latin1_list (screen->display, screen->xroot, + screen->display->atom__XKB_RULES_NAMES, + &names, &n_names); + if (!ok) + return; + + if (n_names != 5) + goto out; + + rules = names[0]; + model = names[1]; + layout = names[2]; + variant = names[3]; + options = names[4]; + + meta_wayland_keyboard_set_keymap_names (&compositor->seat->keyboard, + rules, model, layout, variant, options, + META_WAYLAND_KEYBOARD_SKIP_XCLIENTS); + + out: + g_strfreev (names); +} + /** * meta_display_handle_event: * @display: The MetaDisplay that events are coming from @@ -2964,6 +2998,10 @@ meta_display_handle_event (MetaDisplay *display, else if (event->xproperty.atom == display->atom__NET_DESKTOP_NAMES) meta_screen_update_workspace_names (screen); + else if (meta_is_wayland_compositor () && + event->xproperty.atom == + display->atom__XKB_RULES_NAMES) + reload_xkb_rules (screen); #if 0 else if (event->xproperty.atom == display->atom__NET_RESTACK_WINDOW) diff --git a/src/core/xprops.c b/src/core/xprops.c index 844824f81..040581c53 100644 --- a/src/core/xprops.c +++ b/src/core/xprops.c @@ -536,6 +536,81 @@ meta_prop_get_utf8_list (MetaDisplay *display, return utf8_list_from_results (&results, str_p, n_str_p); } +/* this one freakishly returns g_malloc memory */ +static gboolean +latin1_list_from_results (GetPropertyResults *results, + char ***str_p, + int *n_str_p) +{ + int i; + int n_strings; + char **retval; + const char *p; + + *str_p = NULL; + *n_str_p = 0; + + if (!validate_or_free_results (results, 8, XA_STRING, FALSE)) + return FALSE; + + /* I'm not sure this is right, but I'm guessing the + * property is nul-separated + */ + i = 0; + n_strings = 0; + while (i < (int) results->n_items) + { + if (results->prop[i] == '\0') + ++n_strings; + ++i; + } + + if (results->prop[results->n_items - 1] != '\0') + ++n_strings; + + /* we're guaranteed that results->prop has a nul on the end + * by XGetWindowProperty + */ + + retval = g_new0 (char*, n_strings + 1); + + p = (char *)results->prop; + i = 0; + while (i < n_strings) + { + retval[i] = g_strdup (p); + + p = p + strlen (p) + 1; + ++i; + } + + *str_p = retval; + *n_str_p = i; + + meta_XFree (results->prop); + results->prop = NULL; + + return TRUE; +} + +gboolean +meta_prop_get_latin1_list (MetaDisplay *display, + Window xwindow, + Atom xatom, + char ***str_p, + int *n_str_p) +{ + GetPropertyResults results; + + *str_p = NULL; + + if (!get_property (display, xwindow, xatom, + XA_STRING, &results)) + return FALSE; + + return latin1_list_from_results (&results, str_p, n_str_p); +} + void meta_prop_set_utf8_string_hint (MetaDisplay *display, Window xwindow, diff --git a/src/core/xprops.h b/src/core/xprops.h index 5a799f80c..a5c4fb9a3 100644 --- a/src/core/xprops.h +++ b/src/core/xprops.h @@ -102,6 +102,11 @@ gboolean meta_prop_get_utf8_list (MetaDisplay *display, Atom xatom, char ***str_p, int *n_str_p); +gboolean meta_prop_get_latin1_list (MetaDisplay *display, + Window xwindow, + Atom xatom, + char ***str_p, + int *n_str_p); void meta_prop_set_utf8_string_hint (MetaDisplay *display, Window xwindow, diff --git a/src/meta/atomnames.h b/src/meta/atomnames.h index d7a6e7e35..43a18a9c9 100644 --- a/src/meta/atomnames.h +++ b/src/meta/atomnames.h @@ -81,6 +81,7 @@ item(TIMESTAMP) item(VERSION) item(ATOM_PAIR) item(BACKLIGHT) +item(_XKB_RULES_NAMES) /* Oddities: These are used, and we need atoms for them, * but when we need all _NET_WM hints (i.e. when we're making diff --git a/src/wayland/meta-wayland-keyboard.c b/src/wayland/meta-wayland-keyboard.c index 6e24e3656..05db151ef 100644 --- a/src/wayland/meta-wayland-keyboard.c +++ b/src/wayland/meta-wayland-keyboard.c @@ -106,11 +106,49 @@ create_anonymous_file (off_t size, return -1; } -static gboolean -meta_wayland_xkb_info_new_keymap (MetaWaylandXkbInfo *xkb_info) +static void +inform_clients_of_new_keymap (MetaWaylandKeyboard *keyboard, + int flags) { + MetaWaylandCompositor *compositor; + struct wl_client *xclient; + struct wl_resource *keyboard_resource; + + compositor = meta_wayland_compositor_get_default (); + xclient = compositor->xwayland_client; + + wl_resource_for_each (keyboard_resource, &keyboard->resource_list) + { + if ((flags & META_WAYLAND_KEYBOARD_SKIP_XCLIENTS) && + wl_resource_get_client (keyboard_resource) == xclient) + continue; + + wl_keyboard_send_keymap (keyboard_resource, + WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, + keyboard->xkb_info.keymap_fd, + keyboard->xkb_info.keymap_size); + } +} + +static void +meta_wayland_keyboard_take_keymap (MetaWaylandKeyboard *keyboard, + struct xkb_keymap *keymap, + int flags) +{ + MetaWaylandXkbInfo *xkb_info = &keyboard->xkb_info; GError *error = NULL; char *keymap_str; + size_t previous_size; + + if (keymap == NULL) + { + g_warning ("Attempting to set null keymap (compilation probably failed)"); + return; + } + + if (xkb_info->keymap) + xkb_keymap_unref (xkb_info->keymap); + xkb_info->keymap = keymap; xkb_info->shift_mod = xkb_map_mod_get_index (xkb_info->keymap, XKB_MOD_NAME_SHIFT); @@ -129,21 +167,28 @@ meta_wayland_xkb_info_new_keymap (MetaWaylandXkbInfo *xkb_info) keymap_str = xkb_map_get_as_string (xkb_info->keymap); if (keymap_str == NULL) { - g_warning ("failed to get string version of keymap\n"); - return FALSE; + g_warning ("failed to get string version of keymap"); + return; } + previous_size = xkb_info->keymap_size; xkb_info->keymap_size = strlen (keymap_str) + 1; + if (xkb_info->keymap_fd >= 0) + close (xkb_info->keymap_fd); + xkb_info->keymap_fd = create_anonymous_file (xkb_info->keymap_size, &error); if (xkb_info->keymap_fd < 0) { - g_warning ("creating a keymap file for %lu bytes failed: %s\n", + g_warning ("creating a keymap file for %lu bytes failed: %s", (unsigned long) xkb_info->keymap_size, error->message); g_clear_error (&error); goto err_keymap_str; } + if (xkb_info->keymap_area) + munmap (xkb_info->keymap_area, previous_size); + xkb_info->keymap_area = mmap (NULL, xkb_info->keymap_size, PROT_READ | PROT_WRITE, MAP_SHARED, xkb_info->keymap_fd, 0); @@ -156,41 +201,24 @@ meta_wayland_xkb_info_new_keymap (MetaWaylandXkbInfo *xkb_info) strcpy (xkb_info->keymap_area, keymap_str); free (keymap_str); - return TRUE; + if (keyboard->is_evdev) + { + ClutterDeviceManager *manager; + + manager = clutter_device_manager_get_default (); + clutter_evdev_set_keyboard_map (manager, xkb_info->keymap); + } + + inform_clients_of_new_keymap (keyboard, flags); + + return; err_dev_zero: close (xkb_info->keymap_fd); xkb_info->keymap_fd = -1; err_keymap_str: free (keymap_str); - return FALSE; -} - -static gboolean -meta_wayland_keyboard_build_global_keymap (struct xkb_context *xkb_context, - struct xkb_rule_names *xkb_names, - MetaWaylandXkbInfo *xkb_info) -{ - xkb_info->keymap = xkb_map_new_from_names (xkb_context, - xkb_names, - 0 /* flags */); - if (xkb_info->keymap == NULL) - { - g_warning ("failed to compile global XKB keymap\n" - " tried rules %s, model %s, layout %s, variant %s, " - "options %s\n", - xkb_names->rules, - xkb_names->model, - xkb_names->layout, - xkb_names->variant, - xkb_names->options); - return FALSE; - } - - if (!meta_wayland_xkb_info_new_keymap (xkb_info)) - return FALSE; - - return TRUE; + return; } static void @@ -306,9 +334,8 @@ meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard, struct wl_display *display, gboolean is_evdev) { - ClutterDeviceManager *manager; - memset (keyboard, 0, sizeof *keyboard); + keyboard->xkb_info.keymap_fd = -1; wl_list_init (&keyboard->resource_list); wl_array_init (&keyboard->keys); @@ -320,18 +347,15 @@ meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard, keyboard->display = display; keyboard->xkb_context = xkb_context_new (0 /* flags */); - - meta_wayland_keyboard_build_global_keymap (keyboard->xkb_context, - &keyboard->xkb_names, - &keyboard->xkb_info); - keyboard->is_evdev = is_evdev; - if (is_evdev) - { - manager = clutter_device_manager_get_default (); - clutter_evdev_set_keyboard_map (manager, keyboard->xkb_info.keymap); - } + /* Compute a default until gnome-settings-daemon starts and sets + the appropriate values + */ + meta_wayland_keyboard_set_keymap_names (keyboard, + "evdev", + "pc105", + "us", "", "", 0); return TRUE; } @@ -563,12 +587,6 @@ meta_wayland_keyboard_end_grab (MetaWaylandKeyboard *keyboard) void meta_wayland_keyboard_release (MetaWaylandKeyboard *keyboard) { - g_free ((char *) keyboard->xkb_names.rules); - g_free ((char *) keyboard->xkb_names.model); - g_free ((char *) keyboard->xkb_names.layout); - g_free ((char *) keyboard->xkb_names.variant); - g_free ((char *) keyboard->xkb_names.options); - meta_wayland_xkb_info_destroy (&keyboard->xkb_info); xkb_context_unref (keyboard->xkb_context); @@ -656,3 +674,28 @@ meta_wayland_keyboard_end_modal (MetaWaylandKeyboard *keyboard, meta_verbose ("Released modal keyboard grab, timestamp %d\n", timestamp); } + +void +meta_wayland_keyboard_set_keymap_names (MetaWaylandKeyboard *keyboard, + const char *rules, + const char *model, + const char *layout, + const char *variant, + const char *options, + int flags) +{ + struct xkb_rule_names xkb_names; + + xkb_names.rules = rules; + xkb_names.model = model; + xkb_names.layout = layout; + xkb_names.variant = variant; + xkb_names.options = options; + + meta_wayland_keyboard_take_keymap (keyboard, + xkb_keymap_new_from_names (keyboard->xkb_context, + &xkb_names, + 0 /* flags */), + flags); +} + diff --git a/src/wayland/meta-wayland-keyboard.h b/src/wayland/meta-wayland-keyboard.h index adb3d4428..4354faf21 100644 --- a/src/wayland/meta-wayland-keyboard.h +++ b/src/wayland/meta-wayland-keyboard.h @@ -112,7 +112,6 @@ struct _MetaWaylandKeyboard struct xkb_context *xkb_context; gboolean is_evdev; MetaWaylandXkbInfo xkb_info; - struct xkb_rule_names xkb_names; MetaWaylandKeyboardGrab input_method_grab; struct wl_resource *input_method_resource; @@ -123,6 +122,18 @@ meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard, struct wl_display *display, gboolean is_evdev); +typedef enum { + META_WAYLAND_KEYBOARD_SKIP_XCLIENTS = 1, +} MetaWaylandKeyboardSetKeymapFlags; + +void +meta_wayland_keyboard_set_keymap_names (MetaWaylandKeyboard *keyboard, + const char *rules, + const char *model, + const char *layout, + const char *variant, + const char *options, + int flags); gboolean meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard, const ClutterKeyEvent *event);