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
This commit is contained in:
Giovanni Campagna 2013-09-04 11:11:39 +02:00 committed by Jasper St. Pierre
parent 78fcfec5c1
commit 9db02a7379
6 changed files with 226 additions and 53 deletions

View File

@ -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: * meta_display_handle_event:
* @display: The MetaDisplay that events are coming from * @display: The MetaDisplay that events are coming from
@ -2964,6 +2998,10 @@ meta_display_handle_event (MetaDisplay *display,
else if (event->xproperty.atom == else if (event->xproperty.atom ==
display->atom__NET_DESKTOP_NAMES) display->atom__NET_DESKTOP_NAMES)
meta_screen_update_workspace_names (screen); 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 #if 0
else if (event->xproperty.atom == else if (event->xproperty.atom ==
display->atom__NET_RESTACK_WINDOW) display->atom__NET_RESTACK_WINDOW)

View File

@ -536,6 +536,81 @@ meta_prop_get_utf8_list (MetaDisplay *display,
return utf8_list_from_results (&results, str_p, n_str_p); 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 void
meta_prop_set_utf8_string_hint (MetaDisplay *display, meta_prop_set_utf8_string_hint (MetaDisplay *display,
Window xwindow, Window xwindow,

View File

@ -102,6 +102,11 @@ gboolean meta_prop_get_utf8_list (MetaDisplay *display,
Atom xatom, Atom xatom,
char ***str_p, char ***str_p,
int *n_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 void meta_prop_set_utf8_string_hint
(MetaDisplay *display, (MetaDisplay *display,
Window xwindow, Window xwindow,

View File

@ -81,6 +81,7 @@ item(TIMESTAMP)
item(VERSION) item(VERSION)
item(ATOM_PAIR) item(ATOM_PAIR)
item(BACKLIGHT) item(BACKLIGHT)
item(_XKB_RULES_NAMES)
/* Oddities: These are used, and we need atoms for them, /* Oddities: These are used, and we need atoms for them,
* but when we need all _NET_WM hints (i.e. when we're making * but when we need all _NET_WM hints (i.e. when we're making

View File

@ -106,11 +106,49 @@ create_anonymous_file (off_t size,
return -1; return -1;
} }
static gboolean static void
meta_wayland_xkb_info_new_keymap (MetaWaylandXkbInfo *xkb_info) 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; GError *error = NULL;
char *keymap_str; 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_info->shift_mod =
xkb_map_mod_get_index (xkb_info->keymap, XKB_MOD_NAME_SHIFT); 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); keymap_str = xkb_map_get_as_string (xkb_info->keymap);
if (keymap_str == NULL) if (keymap_str == NULL)
{ {
g_warning ("failed to get string version of keymap\n"); g_warning ("failed to get string version of keymap");
return FALSE; return;
} }
previous_size = xkb_info->keymap_size;
xkb_info->keymap_size = strlen (keymap_str) + 1; 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); xkb_info->keymap_fd = create_anonymous_file (xkb_info->keymap_size, &error);
if (xkb_info->keymap_fd < 0) 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, (unsigned long) xkb_info->keymap_size,
error->message); error->message);
g_clear_error (&error); g_clear_error (&error);
goto err_keymap_str; 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, xkb_info->keymap_area = mmap (NULL, xkb_info->keymap_size,
PROT_READ | PROT_WRITE, PROT_READ | PROT_WRITE,
MAP_SHARED, xkb_info->keymap_fd, 0); 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); strcpy (xkb_info->keymap_area, keymap_str);
free (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: err_dev_zero:
close (xkb_info->keymap_fd); close (xkb_info->keymap_fd);
xkb_info->keymap_fd = -1; xkb_info->keymap_fd = -1;
err_keymap_str: err_keymap_str:
free (keymap_str); free (keymap_str);
return FALSE; return;
}
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;
} }
static void static void
@ -306,9 +334,8 @@ meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
struct wl_display *display, struct wl_display *display,
gboolean is_evdev) gboolean is_evdev)
{ {
ClutterDeviceManager *manager;
memset (keyboard, 0, sizeof *keyboard); memset (keyboard, 0, sizeof *keyboard);
keyboard->xkb_info.keymap_fd = -1;
wl_list_init (&keyboard->resource_list); wl_list_init (&keyboard->resource_list);
wl_array_init (&keyboard->keys); wl_array_init (&keyboard->keys);
@ -320,18 +347,15 @@ meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
keyboard->display = display; keyboard->display = display;
keyboard->xkb_context = xkb_context_new (0 /* flags */); 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; 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; return TRUE;
} }
@ -559,12 +583,6 @@ meta_wayland_keyboard_end_grab (MetaWaylandKeyboard *keyboard)
void void
meta_wayland_keyboard_release (MetaWaylandKeyboard *keyboard) 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); meta_wayland_xkb_info_destroy (&keyboard->xkb_info);
xkb_context_unref (keyboard->xkb_context); xkb_context_unref (keyboard->xkb_context);
@ -652,3 +670,28 @@ meta_wayland_keyboard_end_modal (MetaWaylandKeyboard *keyboard,
meta_verbose ("Released modal keyboard grab, timestamp %d\n", timestamp); 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);
}

View File

@ -112,7 +112,6 @@ struct _MetaWaylandKeyboard
struct xkb_context *xkb_context; struct xkb_context *xkb_context;
gboolean is_evdev; gboolean is_evdev;
MetaWaylandXkbInfo xkb_info; MetaWaylandXkbInfo xkb_info;
struct xkb_rule_names xkb_names;
MetaWaylandKeyboardGrab input_method_grab; MetaWaylandKeyboardGrab input_method_grab;
struct wl_resource *input_method_resource; struct wl_resource *input_method_resource;
@ -123,6 +122,18 @@ meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
struct wl_display *display, struct wl_display *display,
gboolean is_evdev); 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 gboolean
meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard, meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
const ClutterKeyEvent *event); const ClutterKeyEvent *event);