---
 data/org.freedesktop.Accounts.User.xml |   45 ++++++++++++++++
 src/libaccountsservice/act-user.c      |   91 +++++++++++++++++++++++++++++++++
 src/user.c                             |   80 +++++++++++++++++++++++++++++
 src/user.h                             |    3 +
 4 files changed, 219 insertions(+)

--- accountsservice.orig/data/org.freedesktop.Accounts.User.xml
+++ accountsservice/data/org.freedesktop.Accounts.User.xml
@@ -331,6 +331,41 @@
     </doc:doc>
   </method>
 
+  <method name="SetXKeyboardLayouts">
+    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    <arg name="layouts" direction="in" type="as">
+      <doc:doc>
+        <doc:summary>
+          The name of the keyboard layouts to use as the user's keyboard layout.  The first is the default.
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Sets the user's keyboard layouts.
+        </doc:para>
+      </doc:description>
+      <doc:permission>
+        The caller needs one of the following PolicyKit authorizations:
+        <doc:list>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+            <doc:definition>To change his or her own keyboard layouts</doc:definition>
+          </doc:item>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+            <doc:definition>To change the keyboard layouts of another user</doc:definition>
+          </doc:item>
+        </doc:list>
+      </doc:permission>
+      <doc:errors>
+        <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+        <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+      </doc:errors>
+    </doc:doc>
+  </method>
+
   <method name="SetBackgroundFile">
     <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
     <arg name="filename" direction="in" type="s">
@@ -731,6 +766,16 @@
         </doc:para>
       </doc:description>
     </doc:doc>
+  </property>
+
+  <property name="XKeyboardLayouts" type="as" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+           The names of the user's keyboard layouts.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
   </property>
 
   <property name="BackgroundFile" type="s" access="read">
--- accountsservice.orig/src/user.c
+++ accountsservice/src/user.c
@@ -66,6 +66,7 @@ enum {
         PROP_PASSWORD_MODE,
         PROP_PASSWORD_HINT,
         PROP_LOGIN_FREQUENCY,
+        PROP_X_KEYBOARD_LAYOUTS,
         PROP_BACKGROUND_FILE,
         PROP_ICON_FILE,
         PROP_LOCKED,
@@ -103,6 +104,7 @@ struct User {
         gchar        *x_session;
         gchar        *location;
         guint64       login_frequency;
+        gchar       **x_keyboard_layouts;
         gchar        *background_file;
         gchar        *icon_file;
         gboolean      locked;
@@ -214,6 +216,9 @@ user_get_property (GObject    *object,
         case PROP_LOCATION:
                 g_value_set_string (value, user->location);
                 break;
+        case PROP_X_KEYBOARD_LAYOUTS:
+                g_value_set_boxed (value, g_strdupv (user->x_keyboard_layouts));
+                break;
         case PROP_BACKGROUND_FILE:
                 g_value_set_string (value, user->background_file);
                 break;
@@ -377,6 +382,13 @@ user_class_init (UserClass *class)
                                                               0,
                                                               G_PARAM_READWRITE));
         g_object_class_install_property (gobject_class,
+                                         PROP_X_KEYBOARD_LAYOUTS,
+                                         g_param_spec_boxed ("x-keyboard-layouts",
+                                                             "Keyboard layouts",
+                                                             "The keyboard layouts to use for this user.",
+                                                             G_TYPE_STRV,
+                                                             G_PARAM_READABLE));
+        g_object_class_install_property (gobject_class,
                                          PROP_BACKGROUND_FILE,
                                          g_param_spec_string ("background-file",
                                                               "Background file",
@@ -427,6 +439,7 @@ user_init (User *user)
         user->account_type = ACCOUNT_TYPE_STANDARD;
         user->home_dir = NULL;
         user->shell = NULL;
+        user->x_keyboard_layouts = NULL;
         user->background_file = NULL;
         user->icon_file = NULL;
         user->email = NULL;
@@ -453,6 +466,7 @@ user_finalize (GObject *object)
         g_free (user->real_name);
         g_free (user->home_dir);
         g_free (user->shell);
+        g_strfreev (user->x_keyboard_layouts);
         g_free (user->background_file);
         g_free (user->icon_file);
         g_free (user->email);
@@ -703,6 +717,12 @@ user_local_update_from_keyfile (User
                 user->password_hint = s;
         }
 
+        s = g_key_file_get_string_list (keyfile, "User", "XKeyboardLayouts", NULL, NULL);
+        if (s != NULL) {
+                g_strfreev (user->x_keyboard_layouts);
+                user->x_keyboard_layouts = s;
+        }
+
         s = g_key_file_get_string (keyfile, "User", "Background", NULL);
         if (s != NULL) {
                 g_free (user->background_file);
@@ -740,6 +760,9 @@ user_local_save_to_keyfile (User     *us
         if (user->password_hint)
                 g_key_file_set_string (keyfile, "User", "PasswordHint", user->password_hint);
 
+        if (user->x_keyboard_layouts)
+                g_key_file_set_string_list (keyfile, "User", "XKeyboardLayouts", (const gchar * const *) user->x_keyboard_layouts, g_strv_length (user->x_keyboard_layouts));
+
         if (user->background_file)
                 g_key_file_set_string (keyfile, "User", "Background", user->background_file);
 
@@ -1955,6 +1978,63 @@ become_user (gpointer data)
 }
 
 static void
+user_change_x_keyboard_layouts_authorized_cb (Daemon                *daemon,
+                                              User                  *user,
+                                              DBusGMethodInvocation *context,
+                                              gpointer               data)
+{
+        g_strfreev (user->x_keyboard_layouts);
+        user->x_keyboard_layouts = g_strdupv (data);
+
+        save_extra_data (user);
+
+        g_signal_emit (user, signals[CHANGED], 0);
+
+        g_object_notify (G_OBJECT (user), "x-keyboard-layouts");
+
+        dbus_g_method_return (context);
+}
+
+gboolean
+user_set_xkeyboard_layouts (User                  *user,
+                            const gchar * const   *layouts,
+                            DBusGMethodInvocation *context)
+{
+        gchar *sender;
+        DBusConnection *connection;
+        DBusError dbus_error;
+        uid_t uid;
+        const gchar *action_id;
+
+        connection = dbus_g_connection_get_connection (user->system_bus_connection);
+        sender = dbus_g_method_get_sender (context);
+        dbus_error_init (&dbus_error);
+        uid = dbus_bus_get_unix_user (connection, sender, &dbus_error);
+        if (dbus_error_is_set (&dbus_error)) {
+                throw_error (context, ERROR_FAILED, dbus_error.message);
+                dbus_error_free (&dbus_error);
+
+                return TRUE;
+        }
+
+        if (user->uid == uid)
+                action_id = "org.freedesktop.accounts.change-own-user-data";
+        else
+                action_id = "org.freedesktop.accounts.user-administration";
+
+        daemon_local_check_auth (user->daemon,
+                                 user,
+                                 action_id,
+                                 TRUE,
+                                 user_change_x_keyboard_layouts_authorized_cb,
+                                 context,
+                                 g_strdupv (layouts),
+                                 (GDestroyNotify)g_strfreev);
+
+        return TRUE;
+}
+
+static void
 user_change_background_file_authorized_cb (Daemon                *daemon,
                                            User                  *user,
                                            DBusGMethodInvocation *context,
--- accountsservice.orig/src/user.h
+++ accountsservice/src/user.h
@@ -95,6 +95,9 @@ gboolean       user_set_home_directory (
 gboolean       user_set_shell          (User                  *user,
                                         const gchar           *shell,
                                         DBusGMethodInvocation *context);
+gboolean       user_set_xkeyboard_layouts(User                  *user,
+                                          const gchar * const   *layouts,
+                                          DBusGMethodInvocation *context);
 gboolean       user_set_background_file(User                  *user,
                                         const gchar           *filename,
                                         DBusGMethodInvocation *context);
--- accountsservice.orig/src/libaccountsservice/act-user.c
+++ accountsservice/src/libaccountsservice/act-user.c
@@ -57,6 +57,7 @@ enum {
         PROP_AUTOMATIC_LOGIN,
         PROP_SYSTEM_ACCOUNT,
         PROP_LOGIN_FREQUENCY,
+        PROP_X_KEYBOARD_LAYOUTS,
         PROP_BACKGROUND_FILE,
         PROP_ICON_FILE,
         PROP_LANGUAGE,
@@ -88,6 +89,7 @@ struct _ActUser {
         char           *shell;
         char           *email;
         char           *location;
+        char          **x_keyboard_layouts;
         char           *background_file;
         char           *icon_file;
         char           *language;
@@ -235,6 +237,9 @@ act_user_get_property (GObject    *objec
         case PROP_LOCATION:
                 g_value_set_string (value, user->location);
                 break;
+        case PROP_X_KEYBOARD_LAYOUTS:
+                g_value_set_boxed (value, g_strdupv (user->x_keyboard_layouts));
+                break;
         case PROP_BACKGROUND_FILE:
                 g_value_set_string (value, user->background_file);
                 break;
@@ -367,6 +372,13 @@ act_user_class_init (ActUserClass *class
                                                            0,
                                                            G_PARAM_READABLE));
         g_object_class_install_property (gobject_class,
+                                         PROP_X_KEYBOARD_LAYOUTS,
+                                         g_param_spec_boxed ("x-keyboard-layouts",
+                                                             "Keyboard layouts",
+                                                             "The name of keyboard layouts for this user.",
+                                                             G_TYPE_STRV,
+                                                             G_PARAM_READABLE));
+        g_object_class_install_property (gobject_class,
                                          PROP_BACKGROUND_FILE,
                                          g_param_spec_string ("background-file",
                                                               "Background File",
@@ -476,6 +488,7 @@ act_user_finalize (GObject *object)
 
         g_free (user->user_name);
         g_free (user->real_name);
+        g_strfreev (user->x_keyboard_layouts);
         g_free (user->background_file);
         g_free (user->icon_file);
         g_free (user->language);
@@ -828,6 +841,22 @@ act_user_is_system_account (ActUser *use
 }
 
 /**
+ * act_user_get_x_keyboard_layouts:
+ * @user: a #ActUser
+ *
+ * Returns the name of the account keyboard layouts belonging to @user.
+ *
+ * Returns: (transfer none): names of keyboard layouts
+ */
+const char * const *
+act_user_get_x_keyboard_layouts (ActUser *user)
+{
+        g_return_val_if_fail (ACT_IS_USER (user), NULL);
+
+        return (const char * const *) user->x_keyboard_layouts;
+}
+
+/**
  * act_user_get_background_file:
  * @user: a #ActUser
  *
@@ -947,6 +976,28 @@ act_user_get_primary_session_id (ActUser
         return user->sessions->data;
 }
 
+static gboolean
+strv_equal (const char **a, const char **b)
+{
+        gint i;
+        gboolean same = TRUE;
+
+        if (a == b)
+                return TRUE;
+        else if (a == NULL || b == NULL)
+                return FALSE;
+
+        for (i = 0; a[i]; i++) {
+                if (g_strcmp0 (a[i], b[i]) != 0)
+                        return FALSE;
+        }
+
+        if (g_strcmp0 (a[i], b[i]) != 0)
+                return FALSE;
+
+        return TRUE;
+}
+
 static void
 collect_props (const gchar    *key,
                const GValue   *value,
@@ -1073,6 +1124,15 @@ collect_props (const gchar    *key,
                         user->login_frequency = new_login_frequency;
                         g_object_notify (G_OBJECT (user), "login-frequency");
                 }
+        } else if (strcmp (key, "XKeyboardLayouts") == 0) {
+                const char **new_x_keyboard_layouts;
+
+                new_x_keyboard_layouts = g_value_get_boxed (value);
+                if (!strv_equal (user->x_keyboard_layouts, new_x_keyboard_layouts)) {
+                        g_strfreev (user->x_keyboard_layouts);
+                        user->x_keyboard_layouts = g_strdupv (g_value_get_boxed (value));
+                        g_object_notify (G_OBJECT (user), "x-keyboard-layouts");
+                }
         } else if (strcmp (key, "BackgroundFile") == 0) {
                 const char *new_background_file;
 
@@ -1458,6 +1518,37 @@ act_user_set_user_name (ActUser    *user
                 g_error_free (error);
                 return;
         }
+}
+
+/**
+ * act_user_set_x_keyboard_layouts:
+ * @user: the user object to alter.
+ * @keyboard_layouts: names of keyboard layouts
+ *
+ * Assigns a new set of keyboard layouts for @user.
+ *
+ * Note this function is synchronous and ignores errors.
+ **/
+void
+act_user_set_x_keyboard_layouts (ActUser    *user,
+                                 const char * const *keyboard_layouts)
+{
+        GError *error = NULL;
+
+        g_return_if_fail (ACT_IS_USER (user));
+        g_return_if_fail (keyboard_layouts != NULL);
+        g_return_if_fail (DBUS_IS_G_PROXY (user->accounts_proxy));
+
+        if (!dbus_g_proxy_call (user->accounts_proxy,
+                                "SetXKeyboardLayouts",
+                                &error,
+                                G_TYPE_STRV, keyboard_layouts,
+                                G_TYPE_INVALID,
+                                G_TYPE_INVALID)) {
+                g_warning ("SetXKeyboardLayouts call failed: %s", error->message);
+                g_error_free (error);
+                return;
+        }
 }
 
 /**
