[gnome-settings-daemon/wip/input-sources: 6/6] keyboard: Always add a latin and a UI language XKB layouts
- From: Rui Matos <rtcm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-settings-daemon/wip/input-sources: 6/6] keyboard: Always add a latin and a UI language XKB layouts
- Date: Tue, 15 May 2012 00:11:42 +0000 (UTC)
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 = ¤t_parser_variant->xkb_name;
+ else if (current_parser_layout)
+ current_parser_text = ¤t_parser_layout->xkb_name;
+ }
+ else if (strcmp (element_name, "description") == 0)
+ {
+ if (current_parser_variant)
+ current_parser_text = ¤t_parser_variant->id;
+ else if (current_parser_layout)
+ current_parser_text = ¤t_parser_layout->id;
+ }
+ else if (strcmp (element_name, "shortDescription") == 0)
+ {
+ if (current_parser_variant)
+ current_parser_text = ¤t_parser_variant->short_id;
+ else if (current_parser_layout)
+ current_parser_text = ¤t_parser_layout->short_id;
+ }
+ else if (strcmp (element_name, "iso639Id") == 0)
+ {
+ current_parser_text = ¤t_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]