Index: gnome-settings-daemon-2.27.4/plugins/keyboard/gsd-keyboard-xkb.c =================================================================== --- gnome-settings-daemon-2.27.4.orig/plugins/keyboard/gsd-keyboard-xkb.c +++ gnome-settings-daemon-2.27.4/plugins/keyboard/gsd-keyboard-xkb.c @@ -137,6 +137,103 @@ apply_desktop_settings (void) gkbd_desktop_config_activate (¤t_config); } +#define GROUP_SWITCHERS_GROUP "grp" +#define DEFAULT_GROUP_SWITCH "grp:shifts_toggle" + +static void +_maybe_add_layout_switcher (GSList *layouts, + GConfClient *conf_client) +{ + GSList *options; + GSList *option; + gboolean any_switcher; + + /* do we have more than one layout? */ + if (g_slist_length (layouts) > 1) + return; + + /* If yes, we need to make sure there's a way to change the layout + * Based on xkl_layout_chooser_add_default_switcher_if_necessary() in + * capplets/keyboard/gnome-keyboard-properties-xkbltadd.c + * (gnome-control-center) */ + options = gconf_client_get_list (conf_client, + GKBD_KEYBOARD_CONFIG_KEY_OPTIONS, + GCONF_VALUE_STRING, + NULL); + + if (options == NULL) { + /* nothing in gconf, get the current options from X */ + GkbdKeyboardConfig kbd_config; + + gkbd_keyboard_config_init (&kbd_config, + conf_client, + xkl_engine); + gkbd_keyboard_config_load_from_x_initial (&kbd_config, NULL); + + for (option = kbd_config.options; option != NULL; option = option->next) { + options = g_slist_prepend (options, + g_strdup (option->data)); + } + + options = g_slist_reverse (options); + + gkbd_keyboard_config_term (&kbd_config); + } + + any_switcher = FALSE; + + while (option != NULL) { + char *g, *o; + + if (gkbd_keyboard_config_split_items (option->data, &g, &o)) { + if (!g_ascii_strcasecmp (g, GROUP_SWITCHERS_GROUP)) { + any_switcher = TRUE; + break; + } + } + option = option->next; + } + + /* no option to switch between layouts, let's add one */ + if (!any_switcher) { + XklConfigItem *ci = xkl_config_item_new (); + + g_snprintf (ci->name, XKL_MAX_CI_NAME_LENGTH, + DEFAULT_GROUP_SWITCH); + + /* we make sure the option we want to add is known */ + if (!xkl_registry) { + xkl_registry = xkl_config_registry_get_instance (xkl_engine); + if (!xkl_config_registry_load (xkl_registry, TRUE)) { + g_object_unref (xkl_registry); + xkl_registry = NULL; + } + } + + if (xkl_registry && + xkl_config_registry_find_option (xkl_registry, + GROUP_SWITCHERS_GROUP, + ci)) { + const char *id; + + id = gkbd_keyboard_config_merge_items + (GROUP_SWITCHERS_GROUP, + DEFAULT_GROUP_SWITCH); + + options = g_slist_append (options, g_strdup (id)); + gconf_client_set_list (conf_client, + GKBD_KEYBOARD_CONFIG_KEY_OPTIONS, + GCONF_VALUE_STRING, options, + NULL); + } + + g_object_unref (G_OBJECT (ci)); + } + + g_slist_foreach (options, (GFunc) g_free, NULL); + g_slist_free (options); +} + static gboolean try_activating_xkb_config_if_new (GkbdKeyboardConfig *current_sys_kbd_config) { @@ -211,6 +308,38 @@ filter_xkb_config (void) return any_change; } +static int +_xkb_layout_strcmp (const char *a, + const char *b) +{ + char *layout_a; + char *layout_b; + char *p; + int ret; + + if (!a) + return -(a != b); + if (!b) + return (a != b); + + layout_a = g_strdup (a); + p = strchr (layout_a, '\t'); + if (p != NULL) + p[0] = '\0'; + + layout_b = g_strdup (b); + p = strchr (layout_b, '\t'); + if (p != NULL) + p[0] = '\0'; + + ret = strcmp (layout_a, layout_b); + + g_free (layout_a); + g_free (layout_b); + + return ret; +} + static void apply_xkb_settings (void) { @@ -233,18 +362,44 @@ apply_xkb_settings (void) gdm_keyboard_layout = NULL; if (gdm_layout != NULL) { GSList *layouts; + GSList *found_node; + layouts = gconf_client_get_list (conf_client, GKBD_KEYBOARD_CONFIG_KEY_LAYOUTS, GCONF_VALUE_STRING, NULL); - if (layouts == NULL) { - layouts = - g_slist_append (layouts, - g_strdup (gdm_layout)); + + /* Add the layout if it doesn't already exist. XKB limits the + * total number of layouts to four. If we already have four + * layouts configured, we replace the last one. This prevents the + * list from becoming full if the user has a habit of selecting + * many different keyboard layouts in GDM. */ + + found_node = g_slist_find_custom (layouts, gdm_layout, _xkb_layout_strcmp); + + if (found_node) { + if (g_slist_position (layouts, found_node) > 3) { + /* If the layout we found appears after the + * fourth entry, we move it up to fourth place. + * Otherwise, XKB will not activate it. */ + g_free (found_node->data); + layouts = g_slist_delete_link (layouts, found_node); + found_node = NULL; + } + } + + if (!found_node) { + /* Insert in fourth slot or at the end of list, + * whichever comes first */ + layouts = g_slist_insert (layouts, g_strdup (gdm_layout), 3); + + _maybe_add_layout_switcher (layouts, conf_client); + gconf_client_set_list (conf_client, GKBD_KEYBOARD_CONFIG_KEY_LAYOUTS, GCONF_VALUE_STRING, layouts, NULL); } + g_slist_foreach (layouts, (GFunc) g_free, NULL); g_slist_free (layouts); }