8c2a95dbe2
* Mir starts up and is able to display system settings * x86_64 only for now, because at least ubuntu-app-test did not build on aarch64 Based on PureTryOut's work. Getting it to this stage was a huge effort (as it shows in the package count: 111(!)). See the merge request for details. [skip ci]: this won't finish in CI; ollieparanoid made sure that everything builds for x86_64.
570 lines
21 KiB
Diff
570 lines
21 KiB
Diff
From a1b09a0d0243ac3a7fbd26c176a274dd3716f391 Mon Sep 17 00:00:00 2001
|
|
From: Luca Weiss <luca@z3ntu.xyz>
|
|
Date: Thu, 27 Sep 2018 23:32:49 +0200
|
|
Subject: [PATCH 4/4] 0016-add-input-sources-support.patch
|
|
|
|
---
|
|
data/org.freedesktop.Accounts.User.xml | 45 ++++++
|
|
src/libaccountsservice/act-user.c | 82 ++++++++++
|
|
src/libaccountsservice/act-user.h | 3 +
|
|
src/user.c | 213 +++++++++++++++++++++++++
|
|
4 files changed, 343 insertions(+)
|
|
|
|
diff --git a/data/org.freedesktop.Accounts.User.xml b/data/org.freedesktop.Accounts.User.xml
|
|
index 103661c..ede2a19 100644
|
|
--- a/data/org.freedesktop.Accounts.User.xml
|
|
+++ b/data/org.freedesktop.Accounts.User.xml
|
|
@@ -185,6 +185,41 @@
|
|
</doc:doc>
|
|
</method>
|
|
|
|
+ <method name="SetInputSources">
|
|
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
|
|
+ <arg name="sources" direction="in" type="aa{ss}">
|
|
+ <doc:doc>
|
|
+ <doc:summary>
|
|
+ A list of input sources.
|
|
+ </doc:summary>
|
|
+ </doc:doc>
|
|
+ </arg>
|
|
+ <doc:doc>
|
|
+ <doc:description>
|
|
+ <doc:para>
|
|
+ Sets the user's input sources.
|
|
+ </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 own input sources</doc:definition>
|
|
+ </doc:item>
|
|
+ <doc:item>
|
|
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
|
|
+ <doc:definition>To change the input sources 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="SetXSession">
|
|
<annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
|
|
<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="user_set_x_session"/>
|
|
@@ -748,6 +783,16 @@
|
|
</doc:doc>
|
|
</property>
|
|
|
|
+ <property name="InputSources" type="aa{ss}" access="read">
|
|
+ <doc:doc>
|
|
+ <doc:description>
|
|
+ <doc:para>
|
|
+ The user's input sources.
|
|
+ </doc:para>
|
|
+ </doc:description>
|
|
+ </doc:doc>
|
|
+ </property>
|
|
+
|
|
<property name="XSession" type="s" access="read">
|
|
<doc:doc>
|
|
<doc:description>
|
|
diff --git a/src/libaccountsservice/act-user.c b/src/libaccountsservice/act-user.c
|
|
index 36d1ade..43fc0ff 100644
|
|
--- a/src/libaccountsservice/act-user.c
|
|
+++ b/src/libaccountsservice/act-user.c
|
|
@@ -95,6 +95,7 @@ enum {
|
|
PROP_ICON_FILE,
|
|
PROP_LANGUAGE,
|
|
PROP_FORMATS_LOCALE,
|
|
+ PROP_INPUT_SOURCES,
|
|
PROP_X_SESSION,
|
|
PROP_IS_LOADED
|
|
};
|
|
@@ -126,6 +127,7 @@ struct _ActUser {
|
|
char *icon_file;
|
|
char *language;
|
|
char *formats_locale;
|
|
+ GVariant *input_sources;
|
|
char *x_session;
|
|
GList *our_sessions;
|
|
GList *other_sessions;
|
|
@@ -318,6 +320,9 @@ act_user_get_property (GObject *object,
|
|
case PROP_FORMATS_LOCALE:
|
|
g_value_set_string (value, user->formats_locale);
|
|
break;
|
|
+ case PROP_INPUT_SOURCES:
|
|
+ g_value_set_variant (value, user->input_sources);
|
|
+ break;
|
|
case PROP_X_SESSION:
|
|
g_value_set_string (value, user->x_session);
|
|
break;
|
|
@@ -487,6 +492,14 @@ act_user_class_init (ActUserClass *class)
|
|
"User's regional formats.",
|
|
NULL,
|
|
G_PARAM_READABLE));
|
|
+ g_object_class_install_property (gobject_class,
|
|
+ PROP_INPUT_SOURCES,
|
|
+ g_param_spec_variant ("input-sources",
|
|
+ "Input sources",
|
|
+ "User's input sources.",
|
|
+ G_VARIANT_TYPE ("aa{ss}"),
|
|
+ NULL,
|
|
+ G_PARAM_READABLE));
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_X_SESSION,
|
|
g_param_spec_string ("x-session",
|
|
@@ -606,6 +619,8 @@ act_user_finalize (GObject *object)
|
|
g_free (user->shell);
|
|
g_free (user->email);
|
|
g_free (user->location);
|
|
+ if (user->input_sources)
|
|
+ g_variant_unref (user->input_sources);
|
|
if (user->login_history)
|
|
g_variant_unref (user->login_history);
|
|
g_free (user->formats_locale);
|
|
@@ -1109,6 +1124,22 @@ act_user_get_formats_locale (ActUser *user)
|
|
return user->formats_locale;
|
|
}
|
|
|
|
+/**
|
|
+ * act_user_get_input_sources:
|
|
+ * @user: a #ActUser
|
|
+ *
|
|
+ * Returns the input sources of @user.
|
|
+ *
|
|
+ * Returns: (transfer none): a list of input sources
|
|
+ */
|
|
+GVariant *
|
|
+act_user_get_input_sources (ActUser *user)
|
|
+{
|
|
+ g_return_val_if_fail (ACT_IS_USER (user), NULL);
|
|
+
|
|
+ return user->input_sources;
|
|
+}
|
|
+
|
|
/**
|
|
* act_user_get_x_session:
|
|
* @user: a #ActUser
|
|
@@ -1357,6 +1388,19 @@ collect_props (const gchar *key,
|
|
g_object_notify (G_OBJECT (user), "formats_locale");
|
|
}
|
|
|
|
+ } else if (strcmp (key, "InputSources") == 0) {
|
|
+ GVariant *sources;
|
|
+
|
|
+ g_variant_get (value, "@aa{ss}", &sources);
|
|
+
|
|
+ if (!user->input_sources || !g_variant_equal (sources, user->input_sources)) {
|
|
+ if (user->input_sources)
|
|
+ g_variant_unref (user->input_sources);
|
|
+ user->input_sources = g_variant_ref (sources);
|
|
+ g_object_notify (G_OBJECT (user), "input-sources");
|
|
+ }
|
|
+
|
|
+ g_variant_unref (sources);
|
|
} else if (strcmp (key, "XSession") == 0) {
|
|
const char *new_x_session;
|
|
|
|
@@ -1612,6 +1656,15 @@ _act_user_load_from_user (ActUser *user,
|
|
user->language = g_strdup (user_to_copy->language);
|
|
g_object_notify (G_OBJECT (user), "language");
|
|
|
|
+ if (user_to_copy->input_sources != user->input_sources) {
|
|
+ if (user->input_sources)
|
|
+ g_variant_unref (user->input_sources);
|
|
+ user->input_sources = user_to_copy->input_sources;
|
|
+ if (user->input_sources)
|
|
+ g_variant_ref (user->input_sources);
|
|
+ g_object_notify (G_OBJECT (user), "input-sources");
|
|
+ }
|
|
+
|
|
g_free (user->x_session);
|
|
user->x_session = g_strdup (user_to_copy->x_session);
|
|
g_object_notify (G_OBJECT (user), "x-session");
|
|
@@ -1812,6 +1865,35 @@ act_user_set_background_file (ActUser *user,
|
|
}
|
|
}
|
|
|
|
+/**
|
|
+ * act_user_set_input_sources:
|
|
+ * @user: the user object to alter.
|
|
+ * @sources: a list of input sources
|
|
+ *
|
|
+ * Assigns new input sources for @user.
|
|
+ *
|
|
+ * Note this function is synchronous and ignores errors.
|
|
+ **/
|
|
+void
|
|
+act_user_set_input_sources (ActUser *user,
|
|
+ GVariant *sources)
|
|
+{
|
|
+ GError *error = NULL;
|
|
+
|
|
+ g_return_if_fail (ACT_IS_USER (user));
|
|
+ g_return_if_fail (ACCOUNTS_IS_USER (user->accounts_proxy));
|
|
+ g_return_if_fail (g_variant_is_of_type (sources, G_VARIANT_TYPE ("aa{ss}")));
|
|
+
|
|
+ if (!accounts_user_call_set_input_sources_sync (user->accounts_proxy,
|
|
+ sources,
|
|
+ NULL,
|
|
+ &error)) {
|
|
+ g_warning ("SetInputSources call failed: %s", error->message);
|
|
+ g_error_free (error);
|
|
+ return;
|
|
+ }
|
|
+}
|
|
+
|
|
/**
|
|
* act_user_set_x_session:
|
|
* @user: the user object to alter.
|
|
diff --git a/src/libaccountsservice/act-user.h b/src/libaccountsservice/act-user.h
|
|
index e026ce2..de9a5a9 100644
|
|
--- a/src/libaccountsservice/act-user.h
|
|
+++ b/src/libaccountsservice/act-user.h
|
|
@@ -78,6 +78,7 @@ gboolean act_user_is_nonexistent (ActUser *user);
|
|
const char *act_user_get_icon_file (ActUser *user);
|
|
const char *act_user_get_language (ActUser *user);
|
|
const char *act_user_get_formats_locale (ActUser *user);
|
|
+GVariant *act_user_get_input_sources (ActUser *user);
|
|
const char *act_user_get_x_session (ActUser *user);
|
|
const char *act_user_get_primary_session_id (ActUser *user);
|
|
|
|
@@ -99,6 +100,8 @@ void act_user_set_language (ActUser *user,
|
|
const char *language);
|
|
void act_user_set_formats_locale (ActUser *user,
|
|
const char *formats_locale);
|
|
+void act_user_set_input_sources (ActUser *user,
|
|
+ GVariant *sources);
|
|
void act_user_set_x_session (ActUser *user,
|
|
const char *x_session);
|
|
void act_user_set_location (ActUser *user,
|
|
diff --git a/src/user.c b/src/user.c
|
|
index c32971e..17772f8 100644
|
|
--- a/src/user.c
|
|
+++ b/src/user.c
|
|
@@ -59,6 +59,7 @@ enum {
|
|
PROP_EMAIL,
|
|
PROP_LANGUAGE,
|
|
PROP_FORMATS_LOCALE,
|
|
+ PROP_INPUT_SOURCES,
|
|
PROP_X_SESSION,
|
|
PROP_LOCATION,
|
|
PROP_LOGIN_FREQUENCY,
|
|
@@ -96,6 +97,7 @@ struct User {
|
|
gchar *email;
|
|
gchar *language;
|
|
gchar *formats_locale;
|
|
+ GVariant *input_sources;
|
|
gchar *x_session;
|
|
gchar *location;
|
|
guint64 login_frequency;
|
|
@@ -307,6 +309,75 @@ user_update_from_pwent (User *user,
|
|
accounts_user_emit_changed (ACCOUNTS_USER (user));
|
|
}
|
|
|
|
+static gint
|
|
+intcmp (gconstpointer a,
|
|
+ gconstpointer b,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ return GPOINTER_TO_INT (a) - GPOINTER_TO_INT (b);
|
|
+}
|
|
+
|
|
+static GVariant *
|
|
+key_file_get_input_sources (GKeyFile *key_file)
|
|
+{
|
|
+ GVariantBuilder builder;
|
|
+ GSequence *indices;
|
|
+ GSequenceIter *indices_iter;
|
|
+ gchar **groups;
|
|
+ gchar **groups_iter;
|
|
+
|
|
+ indices = g_sequence_new (NULL);
|
|
+ groups = g_key_file_get_groups (key_file, NULL);
|
|
+
|
|
+ for (groups_iter = groups; *groups_iter; groups_iter++) {
|
|
+ if (g_str_has_prefix (*groups_iter, "InputSource") && (*groups_iter)[11]) {
|
|
+ gchar *end;
|
|
+ guint64 index;
|
|
+
|
|
+ index = g_ascii_strtoull (*groups_iter + 11, &end, 0);
|
|
+
|
|
+ if (!*end)
|
|
+ g_sequence_insert_sorted (indices, GINT_TO_POINTER (index), intcmp, NULL);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ g_strfreev (groups);
|
|
+
|
|
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{ss}"));
|
|
+
|
|
+ indices_iter = g_sequence_get_begin_iter (indices);
|
|
+
|
|
+ while (!g_sequence_iter_is_end (indices_iter)) {
|
|
+ guint index;
|
|
+ gchar *group;
|
|
+ gchar **keys;
|
|
+ gchar **keys_iter;
|
|
+
|
|
+ g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{ss}"));
|
|
+
|
|
+ index = GPOINTER_TO_UINT (g_sequence_get (indices_iter));
|
|
+ group = g_strdup_printf ("InputSource%u", index);
|
|
+ keys = g_key_file_get_keys (key_file, group, NULL, NULL);
|
|
+
|
|
+ for (keys_iter = keys; *keys_iter; keys_iter++) {
|
|
+ gchar *value = g_key_file_get_string (key_file, group, *keys_iter, NULL);
|
|
+ g_variant_builder_add (&builder, "{ss}", *keys_iter, value);
|
|
+ g_free (value);
|
|
+ }
|
|
+
|
|
+ g_strfreev (keys);
|
|
+ g_free (group);
|
|
+
|
|
+ g_variant_builder_close (&builder);
|
|
+
|
|
+ indices_iter = g_sequence_iter_next (indices_iter);
|
|
+ }
|
|
+
|
|
+ g_sequence_free (indices);
|
|
+
|
|
+ return g_variant_ref_sink (g_variant_builder_end (&builder));
|
|
+}
|
|
+
|
|
void
|
|
user_update_from_keyfile (User *user,
|
|
GKeyFile *keyfile)
|
|
@@ -330,6 +401,11 @@ user_update_from_keyfile (User *user,
|
|
g_object_notify (G_OBJECT (user), "formats-locale");
|
|
}
|
|
|
|
+ if (user->input_sources != NULL)
|
|
+ g_variant_unref (user->input_sources);
|
|
+ user->input_sources = key_file_get_input_sources (keyfile);
|
|
+ g_object_notify (G_OBJECT (user), "input-sources");
|
|
+
|
|
s = g_key_file_get_string (keyfile, "User", "XSession", NULL);
|
|
if (s != NULL) {
|
|
g_free (user->x_session);
|
|
@@ -408,6 +484,51 @@ user_update_system_account_property (User *user,
|
|
g_object_notify (G_OBJECT (user), "system-account");
|
|
}
|
|
|
|
+static void
|
|
+key_file_set_input_sources (GKeyFile *key_file,
|
|
+ GVariant *input_sources)
|
|
+{
|
|
+ gchar **groups;
|
|
+ gchar **groups_iter;
|
|
+ GVariantIter sources;
|
|
+ GVariantIter *source;
|
|
+ guint i;
|
|
+
|
|
+ /* Remove all groups matching regex "InputSource\d+". */
|
|
+
|
|
+ groups = g_key_file_get_groups (key_file, NULL);
|
|
+
|
|
+ for (groups_iter = groups; *groups_iter; groups_iter++) {
|
|
+ if (g_str_has_prefix (*groups_iter, "InputSource")) {
|
|
+ for (i = 11; g_ascii_isdigit ((*groups_iter)[i]); i++);
|
|
+
|
|
+ if (i > 11 && !(*groups_iter)[i])
|
|
+ g_key_file_remove_group (key_file, *groups_iter, NULL);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ g_strfreev (groups);
|
|
+
|
|
+ /* Write all input sources to key file. */
|
|
+
|
|
+ g_variant_iter_init (&sources, input_sources);
|
|
+
|
|
+ for (i = 0; g_variant_iter_next (&sources, "a{ss}", &source); i++) {
|
|
+ gchar *group;
|
|
+ const gchar *key;
|
|
+ const gchar *value;
|
|
+
|
|
+ group = g_strdup_printf ("InputSource%u", i);
|
|
+
|
|
+ while (g_variant_iter_next (source, "{&s&s}", &key, &value))
|
|
+ g_key_file_set_string (key_file, group, key, value);
|
|
+
|
|
+ g_free (group);
|
|
+
|
|
+ g_variant_iter_free (source);
|
|
+ }
|
|
+}
|
|
+
|
|
static void
|
|
user_save_to_keyfile (User *user,
|
|
GKeyFile *keyfile)
|
|
@@ -423,6 +544,9 @@ user_save_to_keyfile (User *user,
|
|
if (user->formats_locale)
|
|
g_key_file_set_string (keyfile, "User", "FormatsLocale", user->formats_locale);
|
|
|
|
+ if (user->input_sources)
|
|
+ key_file_set_input_sources (keyfile, user->input_sources);
|
|
+
|
|
if (user->x_session)
|
|
g_key_file_set_string (keyfile, "User", "XSession", user->x_session);
|
|
|
|
@@ -1182,6 +1306,67 @@ user_set_formats_locale (AccountsUser *auser,
|
|
return TRUE;
|
|
}
|
|
|
|
+static void
|
|
+user_change_input_sources_authorized_cb (Daemon *daemon,
|
|
+ User *user,
|
|
+ GDBusMethodInvocation *context,
|
|
+ gpointer data)
|
|
+
|
|
+{
|
|
+ GVariant *sources = data;
|
|
+
|
|
+ if (sources != user->input_sources &&
|
|
+ (!sources || !user->input_sources ||
|
|
+ !g_variant_equal (sources, user->input_sources))) {
|
|
+ if (user->input_sources)
|
|
+ g_variant_unref (user->input_sources);
|
|
+
|
|
+ user->input_sources = sources;
|
|
+
|
|
+ if (user->input_sources)
|
|
+ g_variant_ref (user->input_sources);
|
|
+
|
|
+ save_extra_data (user);
|
|
+
|
|
+ accounts_user_emit_changed (ACCOUNTS_USER (user));
|
|
+
|
|
+ g_object_notify (G_OBJECT (user), "input-sources");
|
|
+ }
|
|
+
|
|
+ accounts_user_complete_set_input_sources (ACCOUNTS_USER (user), context);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+user_set_input_sources (AccountsUser *auser,
|
|
+ GDBusMethodInvocation *context,
|
|
+ GVariant *sources)
|
|
+{
|
|
+ User *user = (User*)auser;
|
|
+ int uid;
|
|
+ const gchar *action_id;
|
|
+
|
|
+ if (!get_caller_uid (context, &uid)) {
|
|
+ throw_error (context, ERROR_FAILED, "identifying caller failed");
|
|
+ return FALSE;
|
|
+ }
|
|
+
|
|
+ if (user->uid == (uid_t) 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_input_sources_authorized_cb,
|
|
+ context,
|
|
+ sources ? g_variant_ref (sources) : NULL,
|
|
+ sources ? (GDestroyNotify) g_variant_unref : NULL);
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
static void
|
|
user_change_x_session_authorized_cb (Daemon *daemon,
|
|
User *user,
|
|
@@ -2281,6 +2466,12 @@ user_real_get_language (AccountsUser *user)
|
|
return USER (user)->language;
|
|
}
|
|
|
|
+static GVariant *
|
|
+user_real_get_input_sources (AccountsUser *user)
|
|
+{
|
|
+ return USER (user)->input_sources;
|
|
+}
|
|
+
|
|
static const gchar *
|
|
user_real_get_xsession (AccountsUser *user)
|
|
{
|
|
@@ -2377,6 +2568,9 @@ user_finalize (GObject *object)
|
|
if (user->login_history)
|
|
g_variant_unref (user->login_history);
|
|
|
|
+ if (user->input_sources)
|
|
+ g_variant_unref (user->input_sources);
|
|
+
|
|
if (G_OBJECT_CLASS (user_parent_class)->finalize)
|
|
(*G_OBJECT_CLASS (user_parent_class)->finalize) (object);
|
|
}
|
|
@@ -2388,6 +2582,7 @@ user_set_property (GObject *object,
|
|
GParamSpec *pspec)
|
|
{
|
|
User *user = USER (object);
|
|
+ GVariant *variant;
|
|
|
|
switch (param_id) {
|
|
case PROP_ACCOUNT_TYPE:
|
|
@@ -2398,6 +2593,18 @@ user_set_property (GObject *object,
|
|
break;
|
|
case PROP_FORMATS_LOCALE:
|
|
user->formats_locale = g_value_dup_string (value);
|
|
+ break;
|
|
+ case PROP_INPUT_SOURCES:
|
|
+ variant = g_value_get_variant (value);
|
|
+
|
|
+ if (variant != user->input_sources) {
|
|
+ if (user->input_sources)
|
|
+ g_variant_unref (user->input_sources);
|
|
+ user->input_sources = variant;
|
|
+ if (user->input_sources)
|
|
+ g_variant_ref (user->input_sources);
|
|
+ }
|
|
+
|
|
break;
|
|
case PROP_X_SESSION:
|
|
user->x_session = g_value_dup_string (value);
|
|
@@ -2470,6 +2677,9 @@ user_get_property (GObject *object,
|
|
case PROP_FORMATS_LOCALE:
|
|
g_value_set_string (value, user->formats_locale);
|
|
break;
|
|
+ case PROP_INPUT_SOURCES:
|
|
+ g_value_set_variant (value, user->input_sources);
|
|
+ break;
|
|
case PROP_X_SESSION:
|
|
g_value_set_string (value, user->x_session);
|
|
break;
|
|
@@ -2541,6 +2751,7 @@ user_accounts_user_iface_init (AccountsUserIface *iface)
|
|
iface->handle_set_home_directory = user_set_home_directory;
|
|
iface->handle_set_icon_file = user_set_icon_file;
|
|
iface->handle_set_language = user_set_language;
|
|
+ iface->handle_set_input_sources = user_set_input_sources;
|
|
iface->handle_set_location = user_set_location;
|
|
iface->handle_set_locked = user_set_locked;
|
|
iface->handle_set_password = user_set_password;
|
|
@@ -2559,6 +2770,7 @@ user_accounts_user_iface_init (AccountsUserIface *iface)
|
|
iface->get_shell = user_real_get_shell;
|
|
iface->get_email = user_real_get_email;
|
|
iface->get_language = user_real_get_language;
|
|
+ iface->get_input_sources = user_real_get_input_sources;
|
|
iface->get_xsession = user_real_get_xsession;
|
|
iface->get_location = user_real_get_location;
|
|
iface->get_login_frequency = user_real_get_login_frequency;
|
|
@@ -2588,6 +2800,7 @@ user_init (User *user)
|
|
user->email = NULL;
|
|
user->language = NULL;
|
|
user->formats_locale = NULL;
|
|
+ user->input_sources = NULL;
|
|
user->x_session = NULL;
|
|
user->location = NULL;
|
|
user->password_mode = PASSWORD_MODE_REGULAR;
|
|
--
|
|
2.20.1
|
|
|