[gnome-settings-daemon/wip/input-sources: 6/6] keyboard: Always add a latin and a UI language XKB layouts



commit 0a1ca8584b73ce396dd3b9297c15079dda9a01a6
Author: Rui Matos <tiagomatos gmail com>
Date:   Tue May 15 01:52:22 2012 +0200

    keyboard: Always add a latin and a UI language XKB layouts
    
    Toolkits need to know about both a latin layout to handle
    accelerators which are usually defined like Ctrl+C and a
    layout with the symbols for the language used in UI strings
    to handle mnemonics like Alt+Ð, so we try to find and add
    them in XKB group slots after the layout which the user
    actually intends to type with.

 plugins/keyboard/Makefile.am            |    4 +
 plugins/keyboard/gsd-keyboard-manager.c |   89 +++++-
 plugins/keyboard/xkb-rules-db.c         |  529 +++++++++++++++++++++++++++++++
 plugins/keyboard/xkb-rules-db.h         |   38 +++
 4 files changed, 653 insertions(+), 7 deletions(-)
---
diff --git a/plugins/keyboard/Makefile.am b/plugins/keyboard/Makefile.am
index 2b90548..077d12e 100644
--- a/plugins/keyboard/Makefile.am
+++ b/plugins/keyboard/Makefile.am
@@ -20,6 +20,8 @@ libkeyboard_la_SOURCES = 	\
 	gsd-keyboard-plugin.c	\
 	gsd-keyboard-manager.h	\
 	gsd-keyboard-manager.c	\
+	xkb-rules-db.c		\
+	xkb-rules-db.h		\
 	$(NULL)
 
 XKBCONFIGROOT= XKBCONFIGROOT@
@@ -57,6 +59,8 @@ test_keyboard_SOURCES =		\
 	test-keyboard.c		\
 	gsd-keyboard-manager.h	\
 	gsd-keyboard-manager.c	\
+	xkb-rules-db.c		\
+	xkb-rules-db.h		\
 	$(NULL)
 
 test_keyboard_CFLAGS = $(libkeyboard_la_CFLAGS)
diff --git a/plugins/keyboard/gsd-keyboard-manager.c b/plugins/keyboard/gsd-keyboard-manager.c
index 5609520..202ba68 100644
--- a/plugins/keyboard/gsd-keyboard-manager.c
+++ b/plugins/keyboard/gsd-keyboard-manager.c
@@ -50,6 +50,7 @@
 #include "gsd-keyboard-manager.h"
 #include "gsd-input-helper.h"
 #include "gsd-enums.h"
+#include "xkb-rules-db.h"
 
 #define GSD_KEYBOARD_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_KEYBOARD_MANAGER, GsdKeyboardManagerPrivate))
 
@@ -296,6 +297,86 @@ upload_xkb_description (gchar                *rules_file,
                 g_warning ("Couldn't update the XKB root window property");
 }
 
+static gchar *
+language_code_from_locale (const gchar *locale)
+{
+        if (!locale || !locale[0] || !locale[1])
+                return NULL;
+
+        if (!locale[2] || locale[2] == '_' || locale[2] == '.')
+                return g_strndup (locale, 2);
+
+        if (!locale[3] || locale[3] == '_' || locale[3] == '.')
+                return g_strndup (locale, 3);
+
+        return NULL;
+}
+
+static void
+replace_layout_and_variant (XkbRF_VarDefsRec *xkb_var_defs,
+                            const gchar      *layout,
+                            const gchar      *variant)
+{
+        /* Toolkits need to know about both a latin layout to handle
+         * accelerators which are usually defined like Ctrl+C and a
+         * layout with the symbols for the language used in UI strings
+         * to handle mnemonics like Alt+Ð, so we try to find and add
+         * them in XKB group slots after the layout which the user
+         * actually intends to type with. */
+        const gchar *latin_layout = "us";
+        const gchar *latin_variant = "";
+        const gchar *locale_layout;
+        const gchar *locale_variant;
+        const gchar *locale = setlocale (LC_MESSAGES, NULL);
+        gchar *language = language_code_from_locale (locale);
+
+        xkb_rules_db_get_layout_info_for_language (language,
+                                                   NULL,
+                                                   NULL,
+                                                   &locale_layout,
+                                                   &locale_variant);
+        g_free (language);
+
+        if ((g_strcmp0 (latin_layout, locale_layout) == 0 &&
+             g_strcmp0 (latin_variant, locale_variant) == 0)
+            ||
+            (g_strcmp0 (latin_layout, layout) == 0 &&
+             g_strcmp0 (latin_variant, variant) == 0)) {
+                latin_layout = NULL;
+                latin_variant = NULL;
+        }
+
+        if (g_strcmp0 (locale_layout, layout) == 0 &&
+            g_strcmp0 (locale_variant, variant) == 0) {
+                locale_layout = NULL;
+                locale_variant = NULL;
+        }
+
+        if (xkb_var_defs->layout)
+                free (xkb_var_defs->layout);
+
+        xkb_var_defs->layout =
+                locale_layout && latin_layout ?
+                g_strdup_printf ("%s,%s,%s", layout, locale_layout, latin_layout) :
+                locale_layout ?
+                g_strdup_printf ("%s,%s", layout, locale_layout) :
+                latin_layout ?
+                g_strdup_printf ("%s,%s", layout, latin_layout) :
+                g_strdup_printf ("%s", layout);
+
+        if (xkb_var_defs->variant)
+                free (xkb_var_defs->variant);
+
+        xkb_var_defs->variant =
+                locale_variant && latin_variant ?
+                g_strdup_printf ("%s,%s,%s", variant, locale_variant, latin_variant) :
+                locale_variant ?
+                g_strdup_printf ("%s,%s", variant, locale_variant) :
+                latin_variant ?
+                g_strdup_printf ("%s,%s", variant, latin_variant) :
+                g_strdup_printf ("%s", variant);
+}
+
 static void
 apply_xkb_layout (GsdKeyboardManager *manager,
                   const gchar        *layout,
@@ -317,13 +398,7 @@ apply_xkb_layout (GsdKeyboardManager *manager,
                                               DFLT_XKB_CONFIG_ROOT,
                                               rules_file);
 
-        /* Replace the layout with our current setting */
-        if (xkb_var_defs->layout)
-                free (xkb_var_defs->layout);
-        xkb_var_defs->layout = strdup (layout);
-        if (xkb_var_defs->variant)
-                free (xkb_var_defs->variant);
-        xkb_var_defs->variant = strdup (variant);
+        replace_layout_and_variant (xkb_var_defs, layout, variant);
 
         xkb_rules = XkbRF_Load (rules_path, "C", True, True);
         if (xkb_rules) {
diff --git a/plugins/keyboard/xkb-rules-db.c b/plugins/keyboard/xkb-rules-db.c
new file mode 100644
index 0000000..9715eb5
--- /dev/null
+++ b/plugins/keyboard/xkb-rules-db.c
@@ -0,0 +1,529 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Written by: Rui Matos <rmatos redhat com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <X11/XKBlib.h>
+#include <X11/extensions/XKBrules.h>
+
+#include <gdk/gdkx.h>
+
+#include "xkb-rules-db.h"
+
+#ifndef DFLT_XKB_CONFIG_ROOT
+#define DFLT_XKB_CONFIG_ROOT "/usr/share/X11/xkb"
+#endif
+#ifndef DFLT_XKB_RULES_FILE
+#define DFLT_XKB_RULES_FILE "base"
+#endif
+#ifndef DFLT_XKB_LAYOUT
+#define DFLT_XKB_LAYOUT "us"
+#endif
+#ifndef DFLT_XKB_MODEL
+#define DFLT_XKB_MODEL "pc105"
+#endif
+
+typedef struct _Layout Layout;
+struct _Layout
+{
+  gchar *id;
+  gchar *xkb_name;
+  gchar *short_id;
+  gboolean is_variant;
+  const Layout *main_layout;
+};
+
+static GHashTable *layouts_by_short_id = NULL;
+static GHashTable *layouts_by_iso639 = NULL;
+static GHashTable *layouts_table = NULL;
+static Layout *current_parser_layout = NULL;
+static Layout *current_parser_variant = NULL;
+static gchar **current_parser_text = NULL;
+static gchar *current_parser_iso639Id = NULL;
+
+static void
+free_layout (gpointer data)
+{
+  Layout *layout = data;
+
+  g_free (layout->id);
+  g_free (layout->xkb_name);
+  g_free (layout->short_id);
+  g_free (layout);
+}
+
+static void
+get_xkb_values (gchar            **rules,
+                XkbRF_VarDefsRec  *var_defs)
+{
+  Display *display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+  *rules = NULL;
+
+  /* Get it from the X property or fallback on defaults */
+  if (!XkbRF_GetNamesProp (display, rules, var_defs) || !*rules) {
+    *rules = strdup (DFLT_XKB_RULES_FILE);
+    var_defs->model = strdup (DFLT_XKB_MODEL);
+    var_defs->layout = strdup (DFLT_XKB_LAYOUT);
+    var_defs->variant = NULL;
+    var_defs->options = NULL;
+  }
+}
+
+static void
+free_xkb_var_defs (XkbRF_VarDefsRec *p)
+{
+  if (p->model)
+    free (p->model);
+  if (p->layout)
+    free (p->layout);
+  if (p->variant)
+    free (p->variant);
+  if (p->options)
+    free (p->options);
+  free (p);
+}
+
+static gchar *
+get_rules_file_path (void)
+{
+  XkbRF_VarDefsRec *xkb_var_defs;
+  gchar *rules_file;
+  gchar *rules_path;
+
+  xkb_var_defs = calloc (1, sizeof (XkbRF_VarDefsRec));
+  get_xkb_values (&rules_file, xkb_var_defs);
+
+  if (rules_file[0] == '/')
+    rules_path = g_strdup_printf ("%s.xml", rules_file);
+  else
+    rules_path = g_strdup_printf ("%s/rules/%s.xml",
+                                  DFLT_XKB_CONFIG_ROOT,
+                                  rules_file);
+
+  free_xkb_var_defs (xkb_var_defs);
+  free (rules_file);
+
+  return rules_path;
+}
+
+static void
+parse_start_element (GMarkupParseContext  *context,
+                     const gchar          *element_name,
+                     const gchar         **attribute_names,
+                     const gchar         **attribute_values,
+                     gpointer              data,
+                     GError              **error)
+{
+  if (current_parser_text)
+    {
+      g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                   "Expected character data but got element '%s'", element_name);
+      return;
+    }
+
+  if (strcmp (element_name, "name") == 0)
+    {
+      if (current_parser_variant)
+        current_parser_text = &current_parser_variant->xkb_name;
+      else if (current_parser_layout)
+        current_parser_text = &current_parser_layout->xkb_name;
+    }
+  else if (strcmp (element_name, "description") == 0)
+    {
+      if (current_parser_variant)
+        current_parser_text = &current_parser_variant->id;
+      else if (current_parser_layout)
+        current_parser_text = &current_parser_layout->id;
+    }
+  else if (strcmp (element_name, "shortDescription") == 0)
+    {
+      if (current_parser_variant)
+        current_parser_text = &current_parser_variant->short_id;
+      else if (current_parser_layout)
+        current_parser_text = &current_parser_layout->short_id;
+    }
+  else if (strcmp (element_name, "iso639Id") == 0)
+    {
+      current_parser_text = &current_parser_iso639Id;
+    }
+  else if (strcmp (element_name, "layout") == 0)
+    {
+      if (current_parser_layout)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'layout' elements can't be nested");
+          return;
+        }
+
+      current_parser_layout = g_new0 (Layout, 1);
+    }
+  else if (strcmp (element_name, "variant") == 0)
+    {
+      if (current_parser_variant)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'variant' elements can't be nested");
+          return;
+        }
+
+      if (!current_parser_layout)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'variant' elements must be inside 'layout' elements");
+          return;
+        }
+
+      current_parser_variant = g_new0 (Layout, 1);
+      current_parser_variant->is_variant = TRUE;
+      current_parser_variant->main_layout = current_parser_layout;
+    }
+}
+
+static void
+maybe_replace (GHashTable *table,
+               gchar      *key,
+               Layout     *new_layout)
+{
+  Layout *layout;
+  gboolean exists;
+  gboolean replace = TRUE;
+
+  exists = g_hash_table_lookup_extended (table, key, NULL, (gpointer *)&layout);
+  if (exists)
+    replace = strlen (new_layout->id) < strlen (layout->id);
+  if (replace)
+    g_hash_table_replace (table, key, new_layout);
+}
+
+static void
+parse_end_element (GMarkupParseContext  *context,
+                   const gchar          *element_name,
+                   gpointer              data,
+                   GError              **error)
+{
+  if (strcmp (element_name, "layout") == 0)
+    {
+      if (!current_parser_layout->id || !current_parser_layout->xkb_name)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'layout' elements must enclose 'description' and 'name' elements");
+          return;
+        }
+
+      if (current_parser_layout->short_id)
+        maybe_replace (layouts_by_short_id,
+                       current_parser_layout->short_id, current_parser_layout);
+
+      g_hash_table_replace (layouts_table,
+                            current_parser_layout->id,
+                            current_parser_layout);
+      current_parser_layout = NULL;
+    }
+  else if (strcmp (element_name, "variant") == 0)
+    {
+      if (!current_parser_variant->id || !current_parser_variant->xkb_name)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'variant' elements must enclose 'description' and 'name' elements");
+          return;
+        }
+
+      if (current_parser_variant->short_id)
+        maybe_replace (layouts_by_short_id,
+                       current_parser_variant->short_id, current_parser_variant);
+
+      g_hash_table_replace (layouts_table,
+                            current_parser_variant->id,
+                            current_parser_variant);
+      current_parser_variant = NULL;
+    }
+  else if (strcmp (element_name, "iso639Id") == 0)
+    {
+      if (!current_parser_iso639Id)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'iso639Id' elements must enclose text");
+          return;
+        }
+
+      if (current_parser_layout)
+        maybe_replace (layouts_by_iso639,
+                       current_parser_iso639Id, current_parser_layout);
+      else if (current_parser_variant)
+        maybe_replace (layouts_by_iso639,
+                       current_parser_iso639Id, current_parser_variant);
+      else
+        g_free (current_parser_iso639Id);
+
+      current_parser_iso639Id = NULL;
+    }
+}
+
+static void
+parse_text (GMarkupParseContext  *context,
+            const gchar          *text,
+            gsize                 text_len,
+            gpointer              data,
+            GError              **error)
+{
+  if (current_parser_text)
+    {
+      *current_parser_text = g_strndup (text, text_len);
+      current_parser_text = NULL;
+    }
+}
+
+static void
+parse_error (GMarkupParseContext *context,
+             GError              *error,
+             gpointer             data)
+{
+  free_layout (current_parser_layout);
+  free_layout (current_parser_variant);
+  g_free (current_parser_iso639Id);
+}
+
+static const GMarkupParser markup_parser = {
+  parse_start_element,
+  parse_end_element,
+  parse_text,
+  NULL,
+  parse_error
+};
+
+static void
+parse_rules_file (void)
+{
+  gchar *buffer;
+  gsize length;
+  GMarkupParseContext *context;
+  GError *error = NULL;
+  gchar *full_path = get_rules_file_path ();
+
+  g_file_get_contents (full_path, &buffer, &length, &error);
+  g_free (full_path);
+  if (error)
+    {
+      g_warning ("Failed to read XKB rules file: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  layouts_by_short_id = g_hash_table_new (g_str_hash, g_str_equal);
+  layouts_by_iso639 = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+  /* This is the "master" table so it assumes memory "ownership". */
+  layouts_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, free_layout);
+
+  context = g_markup_parse_context_new (&markup_parser, 0, NULL, NULL);
+  g_markup_parse_context_parse (context, buffer, length, &error);
+  g_markup_parse_context_free (context);
+  g_free (buffer);
+  if (error)
+    {
+      g_warning ("Failed to parse XKB rules file: %s", error->message);
+      g_error_free (error);
+      g_hash_table_destroy (layouts_by_short_id);
+      g_hash_table_destroy (layouts_by_iso639);
+      g_hash_table_destroy (layouts_table);
+      layouts_table = NULL;
+      return;
+    }
+}
+
+static gboolean
+ensure_rules_are_parsed (void)
+{
+  if (!layouts_table)
+    parse_rules_file ();
+
+  return !!layouts_table;
+}
+
+static void
+add_name_to_list (gpointer key,
+                  gpointer value,
+                  gpointer data)
+{
+  GSList **list = data;
+
+  *list = g_slist_prepend (*list, key);
+}
+
+/**
+ * xkb_rules_db_get_all_layout_names:
+ *
+ * Returns a list of all layout names we know about.
+ *
+ * Return value: (transfer container): the list of layout names. The
+ * caller takes ownership of the #GSList but not of the strings
+ * themselves, those are internally allocated and must not be
+ * modified.
+ */
+GSList *
+xkb_rules_db_get_all_layout_names (void)
+{
+  GSList *layout_names = NULL;
+
+  if (!ensure_rules_are_parsed ())
+    return NULL;
+
+  g_hash_table_foreach (layouts_table, add_name_to_list, &layout_names);
+
+  return layout_names;
+}
+
+/**
+ * xkb_rules_db_get_layout_info:
+ * @name: layout's name about which to retrieve the info
+ * @short_name: (out) (allow-none) (transfer none): location to store
+ * the layout's short name, or %NULL
+ * @xkb_layout: (out) (allow-none) (transfer none): location to store
+ * the layout's XKB name, or %NULL
+ * @xkb_variant: (out) (allow-none) (transfer none): location to store
+ * the layout's XKB variant, or %NULL
+ *
+ * Retrieves information about a layout. Some layouts don't provide a
+ * short name (2 or 3 letters) or don't specify a XKB variant, in
+ * those cases @short_name or @xkb_variant are empty strings, i.e. "".
+ *
+ * If the given layout doesn't exist the return value is %FALSE and
+ * all the (out) parameters are set to %NULL.
+ *
+ * Return value: %TRUE if the layout exists or %FALSE otherwise.
+ */
+gboolean
+xkb_rules_db_get_layout_info (const gchar  *name,
+                              const gchar **short_name,
+                              const gchar **xkb_layout,
+                              const gchar **xkb_variant)
+{
+  const Layout *layout;
+
+  if (short_name)
+    *short_name = NULL;
+  if (xkb_layout)
+    *xkb_layout = NULL;
+  if (xkb_variant)
+    *xkb_variant = NULL;
+
+  if (!ensure_rules_are_parsed ())
+    return FALSE;
+
+  if (!g_hash_table_lookup_extended (layouts_table, name, NULL, (gpointer *)&layout))
+    return FALSE;
+
+  if (!layout->is_variant)
+    {
+      if (short_name)
+        *short_name = layout->short_id ? layout->short_id : "";
+      if (xkb_layout)
+        *xkb_layout = layout->xkb_name;
+      if (xkb_variant)
+        *xkb_variant = "";
+    }
+  else
+    {
+      if (short_name)
+        *short_name = layout->short_id ? layout->short_id :
+          layout->main_layout->short_id ? layout->main_layout->short_id : "";
+      if (xkb_layout)
+        *xkb_layout = layout->main_layout->xkb_name;
+      if (xkb_variant)
+        *xkb_variant = layout->xkb_name;
+    }
+
+  return TRUE;
+}
+
+/**
+ * xkb_rules_db_get_layout_info_for_language:
+ * @language: an ISO 639 code
+ * @name: (out) (allow-none) (transfer none): location to store the
+ * layout's name, or %NULL
+ * @short_name: (out) (allow-none) (transfer none): location to store
+ * the layout's short name, or %NULL
+ * @xkb_layout: (out) (allow-none) (transfer none): location to store
+ * the layout's XKB name, or %NULL
+ * @xkb_variant: (out) (allow-none) (transfer none): location to store
+ * the layout's XKB variant, or %NULL
+ *
+ * Retrieves the layout that better fits @language. It also fetches
+ * information about that layout like xkb_rules_db_get_layout_info().
+ *
+ * If the a layout can't be found the return value is %FALSE and all
+ * the (out) parameters are set to %NULL.
+ *
+ * Return value: %TRUE if a layout exists or %FALSE otherwise.
+ */
+gboolean
+xkb_rules_db_get_layout_info_for_language (const gchar  *language,
+                                           const gchar **name,
+                                           const gchar **short_name,
+                                           const gchar **xkb_layout,
+                                           const gchar **xkb_variant)
+{
+  const Layout *layout;
+
+  if (name)
+    *name = NULL;
+  if (short_name)
+    *short_name = NULL;
+  if (xkb_layout)
+    *xkb_layout = NULL;
+  if (xkb_variant)
+    *xkb_variant = NULL;
+
+  if (!ensure_rules_are_parsed ())
+    return FALSE;
+
+  if (!g_hash_table_lookup_extended (layouts_by_iso639, language, NULL, (gpointer *)&layout))
+    if (!g_hash_table_lookup_extended (layouts_by_short_id, language, NULL, (gpointer *)&layout))
+      return FALSE;
+
+  if (name)
+    *name = layout->id;
+
+  if (!layout->is_variant)
+    {
+      if (short_name)
+        *short_name = layout->short_id ? layout->short_id : "";
+      if (xkb_layout)
+        *xkb_layout = layout->xkb_name;
+      if (xkb_variant)
+        *xkb_variant = "";
+    }
+  else
+    {
+      if (short_name)
+        *short_name = layout->short_id ? layout->short_id :
+          layout->main_layout->short_id ? layout->main_layout->short_id : "";
+      if (xkb_layout)
+        *xkb_layout = layout->main_layout->xkb_name;
+      if (xkb_variant)
+        *xkb_variant = layout->xkb_name;
+    }
+
+  return TRUE;
+}
diff --git a/plugins/keyboard/xkb-rules-db.h b/plugins/keyboard/xkb-rules-db.h
new file mode 100644
index 0000000..4387c45
--- /dev/null
+++ b/plugins/keyboard/xkb-rules-db.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Written by: Rui Matos <rmatos redhat com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __XKB_RULES_DB_H__
+#define __XKB_RULES_DB_H__
+
+#include <glib.h>
+
+GSList         *xkb_rules_db_get_all_layout_names       (void);
+gboolean        xkb_rules_db_get_layout_info            (const gchar  *name,
+                                                         const gchar **short_name,
+                                                         const gchar **xkb_layout,
+                                                         const gchar **xkb_variant);
+gboolean        xkb_rules_db_get_layout_info_for_language (const gchar  *language,
+                                                           const gchar **name,
+                                                           const gchar **short_name,
+                                                           const gchar **xkb_layout,
+                                                           const gchar **xkb_variant);
+
+#endif  /* __XKB_RULES_DB_H__ */



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