[mutter/wip/wayland-work: 14/20] wayland: sync the keymap from X to wayland



commit 8c4bcc8a5bc7f8f02f046d13e3447c8d6ce2ac64
Author: Giovanni Campagna <gcampagn redhat com>
Date:   Wed Sep 4 11:11:39 2013 +0200

    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 f8a95b4..587977f 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -2182,6 +2182,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
@@ -2954,6 +2988,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 ac31659..43701d9 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 5a799f8..a5c4fb9 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 d7a6e7e..43a18a9 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 494d9f8..34ce390 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);
@@ -321,18 +348,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;
 }
@@ -643,12 +667,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);
 
@@ -736,3 +754,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 110363d..e0bd67e 100644
--- a/src/wayland/meta-wayland-keyboard.h
+++ b/src/wayland/meta-wayland-keyboard.h
@@ -113,7 +113,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;
@@ -124,6 +123,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);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]