[gnome-desktop] xkbinfo: use libxkbregistry to parse the rules files for us



commit a980a4c83aab9d2f985d28be3e9263444a8beb41
Author: Peter Hutterer <peter hutterer who-t net>
Date:   Wed May 13 15:59:49 2020 +1000

    xkbinfo: use libxkbregistry to parse the rules files for us
    
    Version 2, this time with libxkbregistry build-time conditional, see
    4f6bec60bfc781c59d5afb6f968fc94ad859e5b9 for the first commit, reverted in
    a8c94b74a8182c48eb5cd5e2cc9b03d5ee52d4bd due to
    https://gitlab.gnome.org/GNOME/gnome-build-meta/-/issues/329.
    
    Available in libxkbcommon 1.0.0 and later, libxkbregistry is a library wrapper
    around the evdev.xml rules file that we used to parse directly here. It
    provides a basic iteration API - load the evdev ruleset, then iterate through
    the layouts and options and copy the values over into our data structures as
    needed. This removes the need for XML parsing and error-checking, we can now
    rely on libxkbregistry to do this for us.
    
    The side-effect of this (and motivation for libxkbregistry) is that we
    automatically load user-specific XKB RMLVO as well where they are present.
    Together with mutter commit f71238732508d91bdfcb581c84697a516499a1eb this
    allows a user to drop up their custom XKB layouts in
    $XDG_CONFIG_DIR/xkb and have them both listed in the GUIs and working.
    
    See original MR at
    https://gitlab.gnome.org/GNOME/gnome-desktop/-/merge_requests/79
    
    https://gitlab.gnome.org/GNOME/gnome-desktop/-/merge_requests/88

 .gitlab-ci.yml                    |   2 +-
 config.h.meson                    |   3 +
 libgnome-desktop/gnome-xkb-info.c | 145 ++++++++++++++++++++++++++++++++++++++
 libgnome-desktop/meson.build      |   1 +
 meson.build                       |   3 +
 5 files changed, 153 insertions(+), 1 deletion(-)
---
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b1a5d8e4..6fe953d9 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -3,7 +3,7 @@ image: fedora:rawhide
 variables:
   LAST_ABI_BREAK: 9d01763ba2a3f71b7c0aade04d2ffa6a883e308d
   DEPENDENCIES: gtk3-devel gsettings-desktop-schemas-devel gettext
-                gtk-doc xkeyboard-config-devel itstool
+                gtk-doc libxkbcommon-devel xkeyboard-config-devel itstool
                 gobject-introspection-devel systemd-devel iso-codes-devel
                 libseccomp-devel gcc gcc-c++ glibc-devel
                 meson redhat-rpm-config
diff --git a/config.h.meson b/config.h.meson
index 7a8d8d96..75b0170d 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -22,5 +22,8 @@
 /* define if udev is available */
 #mesondefine HAVE_UDEV
 
+/* define if libxkbregistry is available */
+#mesondefine HAVE_XKBREGISTRY
+
 /* Define to include GNU extensions */
 #mesondefine _GNU_SOURCE
diff --git a/libgnome-desktop/gnome-xkb-info.c b/libgnome-desktop/gnome-xkb-info.c
index bcee465a..61409210 100644
--- a/libgnome-desktop/gnome-xkb-info.c
+++ b/libgnome-desktop/gnome-xkb-info.c
@@ -21,6 +21,10 @@
 
 #include <config.h>
 
+#ifdef HAVE_XKBREGISTRY
+#include <xkbcommon/xkbregistry.h>
+#endif
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -74,6 +78,7 @@ struct _GnomeXkbInfoPrivate
   GHashTable *layouts_by_language;
   GHashTable *layouts_table;
 
+#ifndef HAVE_XKBREGISTRY
   /* Only used while parsing */
   XkbOptionGroup *current_parser_group;
   XkbOption *current_parser_option;
@@ -82,6 +87,7 @@ struct _GnomeXkbInfoPrivate
   gchar  *current_parser_iso639Id;
   gchar  *current_parser_iso3166Id;
   gchar **current_parser_text;
+#endif
 };
 
 G_DEFINE_TYPE_WITH_CODE (GnomeXkbInfo, gnome_xkb_info, G_TYPE_OBJECT,
@@ -192,6 +198,143 @@ add_layout_to_locale_tables (Layout     *layout,
     }
 }
 
+#ifdef HAVE_XKBREGISTRY
+typedef enum {
+  ONLY_MAIN_LAYOUTS,
+  ONLY_VARIANTS,
+} LayoutSubset;
+
+static void
+add_layouts (GnomeXkbInfo        *self,
+             struct rxkb_context *ctx,
+             LayoutSubset      which)
+{
+  GnomeXkbInfoPrivate *priv = self->priv;
+  struct rxkb_layout *layout;
+
+  for (layout = rxkb_layout_first (ctx);
+       layout;
+       layout = rxkb_layout_next (layout))
+    {
+      struct rxkb_iso639_code *iso639;
+      struct rxkb_iso3166_code *iso3166;
+      const char *name, *variant;
+      Layout *l;
+
+      name = rxkb_layout_get_name (layout);
+      variant = rxkb_layout_get_variant (layout);
+
+      if ((which == ONLY_VARIANTS && variant == NULL) ||
+          (which == ONLY_MAIN_LAYOUTS && variant != NULL))
+          continue;
+
+      l = g_slice_new0 (Layout);
+      if (variant)
+        {
+          /* This relies on the main layouts being added first */
+          l->main_layout = g_hash_table_lookup (priv->layouts_table, name);
+          if (l->main_layout == NULL)
+           {
+               /* This is a bug in libxkbregistry */
+               g_warning ("Ignoring variant '%s(%s)' without a main layout",
+                          name, variant);
+               g_free (l);
+               continue;
+           }
+
+          l->xkb_name = g_strdup (variant);
+          l->is_variant = TRUE;
+          l->id = g_strjoin ("+", name, variant, NULL);
+        }
+      else
+        {
+          l->xkb_name = g_strdup (name);
+          l->id = g_strdup (name);
+        }
+      l->description = g_strdup (rxkb_layout_get_description (layout));
+      l->short_desc = g_strdup (rxkb_layout_get_brief (layout));
+      for (iso639 = rxkb_layout_get_iso639_first (layout);
+           iso639;
+           iso639 = rxkb_iso639_code_next (iso639))
+        {
+          char *id = g_strdup (rxkb_iso639_code_get_code (iso639));
+          l->iso3166Ids = g_slist_prepend (l->iso3166Ids, id);
+        }
+      for (iso3166 = rxkb_layout_get_iso3166_first (layout);
+           iso3166;
+           iso3166 = rxkb_iso3166_code_next (iso3166))
+        {
+          char *id = g_strdup (rxkb_iso3166_code_get_code (iso3166));
+          l->iso3166Ids = g_slist_prepend (l->iso3166Ids, id);
+        }
+
+      g_hash_table_replace (priv->layouts_table, l->id, l);
+      add_layout_to_locale_tables (l,
+                                   priv->layouts_by_language,
+                                   priv->layouts_by_country);
+   }
+}
+
+static gboolean
+parse_rules_file (GnomeXkbInfo  *self,
+                  const gchar   *ruleset,
+                  gboolean       include_extras)
+{
+  GnomeXkbInfoPrivate *priv = self->priv;
+  struct rxkb_context *ctx;
+  struct rxkb_option_group *group;
+  enum rxkb_context_flags flags = RXKB_CONTEXT_NO_FLAGS;
+
+  if (include_extras)
+      flags |= RXKB_CONTEXT_LOAD_EXOTIC_RULES;
+
+  ctx = rxkb_context_new (flags);
+  if (!rxkb_context_parse (ctx, ruleset)) {
+      rxkb_context_unref (ctx);
+      return FALSE;
+  }
+
+  /* libxkbregistry doesn't guarantee a sorting order of the layouts but we
+   * want to reference the main layout from the variants. So populate with
+   * the main layouts first, then add the variants */
+  add_layouts (self, ctx, ONLY_MAIN_LAYOUTS);
+  add_layouts (self, ctx, ONLY_VARIANTS);
+
+  for (group = rxkb_option_group_first (ctx);
+       group;
+       group = rxkb_option_group_next (group))
+    {
+        XkbOptionGroup *g;
+        struct rxkb_option *option;
+
+        g = g_slice_new (XkbOptionGroup);
+        g->id = g_strdup (rxkb_option_group_get_name (group));
+        g->description = g_strdup (rxkb_option_group_get_description (group));
+        g->options_table = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                  NULL, free_option);
+        g->allow_multiple_selection = rxkb_option_group_allows_multiple (group);
+        g_hash_table_replace (priv->option_groups_table, g->id, g);
+
+        for (option = rxkb_option_first (group);
+             option;
+             option = rxkb_option_next (option))
+          {
+            XkbOption *o;
+
+            o = g_slice_new (XkbOption);
+            o->id = g_strdup (rxkb_option_get_name (option));
+            o->description = g_strdup(rxkb_option_get_description (option));
+            g_hash_table_replace (g->options_table, o->id, o);
+          }
+    }
+
+  rxkb_context_unref (ctx);
+
+  return TRUE;
+}
+
+#else /* HAVE_XKBREGISTRY */
+
 static gchar *
 get_xml_rules_file_path (const gchar    *ruleset,
                          const gchar    *suffix)
@@ -585,6 +728,8 @@ parse_rules_file (GnomeXkbInfo  *self,
   return FALSE;
 }
 
+#endif /* HAVE_XKBREGISTRY */
+
 static void
 parse_rules (GnomeXkbInfo *self)
 {
diff --git a/libgnome-desktop/meson.build b/libgnome-desktop/meson.build
index ca1e4b2a..b10a18df 100644
--- a/libgnome-desktop/meson.build
+++ b/libgnome-desktop/meson.build
@@ -80,6 +80,7 @@ gnome_desktop_deps = [
   libsystemd_dep,
   schemas_dep,
   xkb_config_dep,
+  xkbregistry_dep,
   iso_codes_dep,
   udev_dep,
   seccomp_dep
diff --git a/meson.build b/meson.build
index 7ea72f71..2ec66471 100644
--- a/meson.build
+++ b/meson.build
@@ -47,6 +47,8 @@ gio_unix_dep = dependency('gio-unix-2.0', version: glib_req)
 schemas_dep = dependency('gsettings-desktop-schemas', version: schemas_req)
 fontconfig_dep = dependency('fontconfig')
 xkb_config_dep = dependency('xkeyboard-config')
+xkbregistry_dep = dependency('xkbregistry', required: false)
+
 iso_codes_dep = dependency('iso-codes')
 
 libsystemd_dep = dependency('libsystemd', required: get_option('systemd'))
@@ -88,6 +90,7 @@ conf.set('_GNU_SOURCE', seccomp_dep.found())
 
 conf.set('HAVE_SYSTEMD', libsystemd_dep.found())
 conf.set('HAVE_UDEV', udev_dep.found())
+conf.set('HAVE_XKBREGISTRY', xkbregistry_dep.found())
 
 conf.set('HAVE_TIMERFD', cc.has_function('timerfd_create'))
 conf.set('HAVE_OPENAT', cc.has_function('openat'))


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