[mutter] backends: Add methods to handle keymaps



commit 101b215d6b7f0fba0c6b6ab8dee8d4fa121a9311
Author: Rui Matos <tiagomatos gmail com>
Date:   Mon Aug 4 16:47:35 2014 +0200

    backends: Add methods to handle keymaps
    
    These methods allow us to set and get xkbcommon keymaps as well as
    locking a specific layout in a layout group.
    
    With this, we introduce dependencies on xkeyboard-config, xkbfile,
    xkbcommon-x11 and a libX11 new enough to have xcb support.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=734301

 configure.ac                              |    6 +
 src/backends/meta-backend-private.h       |   13 ++
 src/backends/meta-backend.c               |   23 ++++
 src/backends/meta-backend.h               |   12 ++
 src/backends/native/meta-backend-native.c |   46 +++++++
 src/backends/x11/meta-backend-x11.c       |  182 +++++++++++++++++++++++++++++
 6 files changed, 282 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index b5bbc2f..2463140 100644
--- a/configure.ac
+++ b/configure.ac
@@ -87,6 +87,9 @@ MUTTER_PC_MODULES="
    wayland-server >= 1.5.90
    upower-glib >= 0.99.0
    gnome-desktop-3.0
+   xkbfile
+   xkeyboard-config
+   xkbcommon-x11
 "
 
 GLIB_GSETTINGS
@@ -235,6 +238,9 @@ if test x$have_xinerama = xno; then
    AC_MSG_ERROR([Xinerama extension was not found])
 fi
 
+AC_DEFINE_UNQUOTED([XKB_BASE], ["`$PKG_CONFIG --variable xkb_base xkeyboard-config`"],
+                               [XKB base dir])
+
 RANDR_LIBS=
 found_randr=no
 AC_CHECK_LIB(Xrandr, XRRUpdateConfiguration,
diff --git a/src/backends/meta-backend-private.h b/src/backends/meta-backend-private.h
index 02b123d..5b15781 100644
--- a/src/backends/meta-backend-private.h
+++ b/src/backends/meta-backend-private.h
@@ -30,6 +30,9 @@
 
 #include "meta-backend.h"
 
+#define DEFAULT_XKB_RULES_FILE "evdev"
+#define DEFAULT_XKB_MODEL "pc105+inet"
+
 #define META_TYPE_BACKEND             (meta_backend_get_type ())
 #define META_BACKEND(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BACKEND, MetaBackend))
 #define META_BACKEND_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass),  META_TYPE_BACKEND, 
MetaBackendClass))
@@ -66,6 +69,16 @@ struct _MetaBackendClass
   void (* warp_pointer) (MetaBackend *backend,
                          int          x,
                          int          y);
+
+  void (* set_keymap) (MetaBackend *backend,
+                       const char  *layouts,
+                       const char  *variants,
+                       const char  *options);
+
+  struct xkb_keymap * (* get_keymap) (MetaBackend *backend);
+
+  void (* lock_layout_group) (MetaBackend *backend,
+                              guint        idx);
 };
 
 #endif /* META_BACKEND_PRIVATE_H */
diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c
index 3cfb951..976d19e 100644
--- a/src/backends/meta-backend.c
+++ b/src/backends/meta-backend.c
@@ -200,6 +200,29 @@ meta_backend_warp_pointer (MetaBackend *backend,
   META_BACKEND_GET_CLASS (backend)->warp_pointer (backend, x, y);
 }
 
+void
+meta_backend_set_keymap (MetaBackend *backend,
+                         const char  *layouts,
+                         const char  *variants,
+                         const char  *options)
+{
+  META_BACKEND_GET_CLASS (backend)->set_keymap (backend, layouts, variants, options);
+}
+
+struct xkb_keymap *
+meta_backend_get_keymap (MetaBackend *backend)
+
+{
+  return META_BACKEND_GET_CLASS (backend)->get_keymap (backend);
+}
+
+void
+meta_backend_lock_layout_group (MetaBackend *backend,
+                                guint idx)
+{
+  META_BACKEND_GET_CLASS (backend)->lock_layout_group (backend, idx);
+}
+
 static GType
 get_backend_type (void)
 {
diff --git a/src/backends/meta-backend.h b/src/backends/meta-backend.h
index f93b6f6..f4eab55 100644
--- a/src/backends/meta-backend.h
+++ b/src/backends/meta-backend.h
@@ -27,6 +27,8 @@
 
 #include <glib-object.h>
 
+#include <xkbcommon/xkbcommon.h>
+
 #include <meta/meta-idle-monitor.h>
 #include "meta-monitor-manager.h"
 #include "meta-cursor-renderer.h"
@@ -54,6 +56,16 @@ void meta_backend_warp_pointer (MetaBackend *backend,
                                 int          x,
                                 int          y);
 
+void meta_backend_set_keymap (MetaBackend *backend,
+                              const char  *layouts,
+                              const char  *variants,
+                              const char  *options);
+
+struct xkb_keymap * meta_backend_get_keymap (MetaBackend *backend);
+
+void meta_backend_lock_layout_group (MetaBackend *backend,
+                                     guint        idx);
+
 void meta_clutter_init (void);
 
 #endif /* META_BACKEND_H */
diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c
index e217880..60f039e 100644
--- a/src/backends/native/meta-backend-native.c
+++ b/src/backends/native/meta-backend-native.c
@@ -189,6 +189,49 @@ meta_backend_native_warp_pointer (MetaBackend *backend,
 }
 
 static void
+meta_backend_native_set_keymap (MetaBackend *backend,
+                                const char  *layouts,
+                                const char  *variants,
+                                const char  *options)
+{
+  ClutterDeviceManager *manager = clutter_device_manager_get_default ();
+  struct xkb_rule_names names;
+  struct xkb_keymap *keymap;
+  struct xkb_context *context;
+
+  names.rules = DEFAULT_XKB_RULES_FILE;
+  names.model = DEFAULT_XKB_MODEL;
+  names.layout = layouts;
+  names.variant = variants;
+  names.options = options;
+
+  context = xkb_context_new (XKB_CONTEXT_NO_FLAGS);
+  keymap = xkb_keymap_new_from_names (context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
+  xkb_context_unref (context);
+
+  clutter_evdev_set_keyboard_map (manager, keymap);
+
+  /* FIXME: emit keymap changed signal */
+
+  xkb_keymap_unref (keymap);
+}
+
+static struct xkb_keymap *
+meta_backend_native_get_keymap (MetaBackend *backend)
+{
+  ClutterDeviceManager *manager = clutter_device_manager_get_default ();
+  return clutter_evdev_get_keyboard_map (manager);
+}
+
+static void
+meta_backend_native_lock_layout_group (MetaBackend *backend,
+                                       guint        idx)
+{
+  ClutterDeviceManager *manager = clutter_device_manager_get_default ();
+  clutter_evdev_set_keyboard_layout_index (manager, idx);
+}
+
+static void
 meta_backend_native_class_init (MetaBackendNativeClass *klass)
 {
   MetaBackendClass *backend_class = META_BACKEND_CLASS (klass);
@@ -199,6 +242,9 @@ meta_backend_native_class_init (MetaBackendNativeClass *klass)
   backend_class->create_cursor_renderer = meta_backend_native_create_cursor_renderer;
 
   backend_class->warp_pointer = meta_backend_native_warp_pointer;
+  backend_class->set_keymap = meta_backend_native_set_keymap;
+  backend_class->get_keymap = meta_backend_native_get_keymap;
+  backend_class->lock_layout_group = meta_backend_native_lock_layout_group;
 }
 
 static void
diff --git a/src/backends/x11/meta-backend-x11.c b/src/backends/x11/meta-backend-x11.c
index 913e00a..5e8c25a 100644
--- a/src/backends/x11/meta-backend-x11.c
+++ b/src/backends/x11/meta-backend-x11.c
@@ -24,11 +24,18 @@
 
 #include "config.h"
 
+#include <string.h>
+#include <stdlib.h>
+
 #include "meta-backend-x11.h"
 
 #include <clutter/x11/clutter-x11.h>
 
 #include <X11/extensions/sync.h>
+#include <X11/XKBlib.h>
+#include <X11/extensions/XKBrules.h>
+#include <X11/Xlib-xcb.h>
+#include <xkbcommon/xkbcommon-x11.h>
 
 #include "meta-idle-monitor-xsync.h"
 #include "meta-monitor-manager-xrandr.h"
@@ -43,6 +50,7 @@ struct _MetaBackendX11Private
 {
   /* The host X11 display */
   Display *xdisplay;
+  xcb_connection_t *xcb;
   GSource *source;
 
   int xsync_event_base;
@@ -52,6 +60,9 @@ struct _MetaBackendX11Private
   int xinput_event_base;
   int xinput_error_base;
   Time latest_evtime;
+
+  uint8_t xkb_event_base;
+  uint8_t xkb_error_base;
 };
 typedef struct _MetaBackendX11Private MetaBackendX11Private;
 
@@ -320,6 +331,17 @@ meta_backend_x11_post_init (MetaBackend *backend)
 
   take_touch_grab (backend);
 
+  priv->xcb = XGetXCBConnection (priv->xdisplay);
+  if (!xkb_x11_setup_xkb_extension (priv->xcb,
+                                    XKB_X11_MIN_MAJOR_XKB_VERSION,
+                                    XKB_X11_MIN_MINOR_XKB_VERSION,
+                                    XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
+                                    NULL, NULL,
+                                    &priv->xkb_event_base,
+                                    &priv->xkb_error_base))
+    meta_fatal ("X server doesn't have the XKB extension, version %d.%d or newer\n",
+                XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION);
+
   META_BACKEND_CLASS (meta_backend_x11_parent_class)->post_init (backend);
 }
 
@@ -414,6 +436,163 @@ meta_backend_x11_warp_pointer (MetaBackend *backend,
 }
 
 static void
+get_xkbrf_var_defs (Display           *xdisplay,
+                    const char        *layouts,
+                    const char        *variants,
+                    const char        *options,
+                    char             **rules_p,
+                    XkbRF_VarDefsRec  *var_defs)
+{
+  char *rules = NULL;
+
+  /* Get it from the X property or fallback on defaults */
+  if (!XkbRF_GetNamesProp (xdisplay, &rules, var_defs) || !rules)
+    {
+      rules = strdup (DEFAULT_XKB_RULES_FILE);
+      var_defs->model = strdup (DEFAULT_XKB_MODEL);
+      var_defs->layout = NULL;
+      var_defs->variant = NULL;
+      var_defs->options = NULL;
+    }
+
+  /* Swap in our new options... */
+  free (var_defs->layout);
+  var_defs->layout = strdup (layouts);
+  free (var_defs->variant);
+  var_defs->variant = strdup (variants);
+  free (var_defs->options);
+  var_defs->options = strdup (options);
+
+  /* Sometimes, the property is a file path, and sometimes it's
+     not. Normalize it so it's always a file path. */
+  if (rules[0] == '/')
+    *rules_p = g_strdup (rules);
+  else
+    *rules_p = g_build_filename (XKB_BASE, "rules", rules, NULL);
+
+  free (rules);
+}
+
+static void
+free_xkbrf_var_defs (XkbRF_VarDefsRec *var_defs)
+{
+  free (var_defs->model);
+  free (var_defs->layout);
+  free (var_defs->variant);
+  free (var_defs->options);
+}
+
+static void
+free_xkb_component_names (XkbComponentNamesRec *p)
+{
+  free (p->keymap);
+  free (p->keycodes);
+  free (p->types);
+  free (p->compat);
+  free (p->symbols);
+  free (p->geometry);
+}
+
+static void
+upload_xkb_description (Display              *xdisplay,
+                        const gchar          *rules_file_path,
+                        XkbRF_VarDefsRec     *var_defs,
+                        XkbComponentNamesRec *comp_names)
+{
+  XkbDescRec *xkb_desc;
+  gchar *rules_file;
+
+  /* Upload it to the X server using the same method as setxkbmap */
+  xkb_desc = XkbGetKeyboardByName (xdisplay,
+                                   XkbUseCoreKbd,
+                                   comp_names,
+                                   XkbGBN_AllComponentsMask,
+                                   XkbGBN_AllComponentsMask &
+                                   (~XkbGBN_GeometryMask), True);
+  if (!xkb_desc)
+    {
+      g_warning ("Couldn't upload new XKB keyboard description");
+      return;
+    }
+
+  XkbFreeKeyboard (xkb_desc, 0, True);
+
+  rules_file = g_path_get_basename (rules_file_path);
+
+  if (!XkbRF_SetNamesProp (xdisplay, rules_file, var_defs))
+    g_warning ("Couldn't update the XKB root window property");
+
+  g_free (rules_file);
+}
+
+static void
+meta_backend_x11_set_keymap (MetaBackend *backend,
+                             const char  *layouts,
+                             const char  *variants,
+                             const char  *options)
+{
+  MetaBackendX11 *x11 = META_BACKEND_X11 (backend);
+  MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11);
+  XkbRF_RulesRec *xkb_rules;
+  XkbRF_VarDefsRec xkb_var_defs = { 0 };
+  gchar *rules_file_path;
+
+  get_xkbrf_var_defs (priv->xdisplay,
+                      layouts,
+                      variants,
+                      options,
+                      &rules_file_path,
+                      &xkb_var_defs);
+
+  xkb_rules = XkbRF_Load (rules_file_path, NULL, True, True);
+  if (xkb_rules)
+    {
+      XkbComponentNamesRec xkb_comp_names = { 0 };
+
+      XkbRF_GetComponents (xkb_rules, &xkb_var_defs, &xkb_comp_names);
+      upload_xkb_description (priv->xdisplay, rules_file_path, &xkb_var_defs, &xkb_comp_names);
+
+      free_xkb_component_names (&xkb_comp_names);
+      XkbRF_Free (xkb_rules, True);
+    }
+  else
+    {
+      g_warning ("Couldn't load XKB rules");
+    }
+
+  free_xkbrf_var_defs (&xkb_var_defs);
+  g_free (rules_file_path);
+}
+
+static struct xkb_keymap *
+meta_backend_x11_get_keymap (MetaBackend *backend)
+{
+  MetaBackendX11 *x11 = META_BACKEND_X11 (backend);
+  MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11);
+  struct xkb_keymap *keymap;
+  struct xkb_context *context;
+
+  context = xkb_context_new (XKB_CONTEXT_NO_FLAGS);
+  keymap = xkb_x11_keymap_new_from_device (context,
+                                           priv->xcb,
+                                           xkb_x11_get_core_keyboard_device_id (priv->xcb),
+                                           XKB_KEYMAP_COMPILE_NO_FLAGS);
+  xkb_context_unref (context);
+
+  return keymap;
+}
+
+static void
+meta_backend_x11_lock_layout_group (MetaBackend *backend,
+                                    guint        idx)
+{
+  MetaBackendX11 *x11 = META_BACKEND_X11 (backend);
+  MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11);
+
+  XkbLockGroup (priv->xdisplay, XkbUseCoreKbd, idx);
+}
+
+static void
 meta_backend_x11_class_init (MetaBackendX11Class *klass)
 {
   MetaBackendClass *backend_class = META_BACKEND_CLASS (klass);
@@ -426,6 +605,9 @@ meta_backend_x11_class_init (MetaBackendX11Class *klass)
   backend_class->grab_device = meta_backend_x11_grab_device;
   backend_class->ungrab_device = meta_backend_x11_ungrab_device;
   backend_class->warp_pointer = meta_backend_x11_warp_pointer;
+  backend_class->set_keymap = meta_backend_x11_set_keymap;
+  backend_class->get_keymap = meta_backend_x11_get_keymap;
+  backend_class->lock_layout_group = meta_backend_x11_lock_layout_group;
 }
 
 static void


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