From edb06ad519f139cb6d382fb4fb95727dafc01c7a Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Sat, 9 Sep 2023 17:07:46 -0400 Subject: [PATCH 01/16] keyboard: Don't require localed for existing user mode If we're in existing user mode, the user may not have permission to set the system keymap. This commit makes sure that lack of permission doesn't prevent the keyboard page from completing. --- gnome-initial-setup/pages/keyboard/gis-keyboard-page.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c index fa41230f..da384495 100644 --- a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c +++ b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c @@ -415,8 +415,13 @@ update_page_complete (GisKeyboardPage *self) GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); gboolean complete; - complete = (priv->localed != NULL && - cc_input_chooser_get_input_id (CC_INPUT_CHOOSER (priv->input_chooser)) != NULL); + if (gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER) { + complete = (priv->localed != NULL && + cc_input_chooser_get_input_id (CC_INPUT_CHOOSER (priv->input_chooser)) != NULL); + } else { + complete = cc_input_chooser_get_input_id (CC_INPUT_CHOOSER (priv->input_chooser)) != NULL; + } + gis_page_set_complete (GIS_PAGE (self), complete); } -- 2.44.0 From fda3cd24142f21c14f96f339a2fa10b5f923f50d Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 8 Sep 2023 11:02:39 -0400 Subject: [PATCH 02/16] language: Don't proceed until localed has set locale In sysmte modes, the keyboard page requires reading the locale from localed, so we need to make sure the setting has been applied before proceeding to the keyboard page from the language page. This commit changes the Next button to desensitize if a set locale operation is pending. --- .../pages/language/gis-language-page.c | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/gnome-initial-setup/pages/language/gis-language-page.c b/gnome-initial-setup/pages/language/gis-language-page.c index 0ac8281d..f8f82097 100644 --- a/gnome-initial-setup/pages/language/gis-language-page.c +++ b/gnome-initial-setup/pages/language/gis-language-page.c @@ -56,6 +56,23 @@ typedef struct _GisLanguagePagePrivate GisLanguagePagePrivate; G_DEFINE_TYPE_WITH_PRIVATE (GisLanguagePage, gis_language_page, GIS_TYPE_PAGE); +static void +on_locale_set (GDBusProxy *proxy, + GAsyncResult *result, + GisLanguagePage *self) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) call_result = NULL; + + call_result = g_dbus_proxy_call_finish (proxy, result, &error); + + if (error != NULL) { + g_warning ("Could not set system locale: %s", error->message); + } + + gis_page_set_complete (GIS_PAGE (self), TRUE); +} + static void set_localed_locale (GisLanguagePage *self) { @@ -72,7 +89,9 @@ set_localed_locale (GisLanguagePage *self) "SetLocale", g_variant_new ("(asb)", b, TRUE), G_DBUS_CALL_FLAGS_NONE, - -1, NULL, NULL, NULL); + -1, priv->cancellable, + (GAsyncReadyCallback) on_locale_set, + self); g_variant_builder_unref (b); } @@ -126,6 +145,9 @@ language_changed (CcLanguageChooser *chooser, gis_driver_set_user_language (driver, priv->new_locale_id, TRUE); if (gis_driver_get_mode (driver) == GIS_DRIVER_MODE_NEW_USER) { + + gis_page_set_complete (GIS_PAGE (page), FALSE); + if (g_permission_get_allowed (priv->permission)) { set_localed_locale (page); } @@ -176,6 +198,7 @@ localed_proxy_ready (GObject *source, } priv->localed = proxy; + gis_page_set_complete (GIS_PAGE (self), TRUE); } static void @@ -250,8 +273,10 @@ gis_language_page_constructed (GObject *object) object); g_object_unref (bus); } - - gis_page_set_complete (GIS_PAGE (page), TRUE); + else + { + gis_page_set_complete (GIS_PAGE (page), TRUE); + } gtk_widget_set_visible (GTK_WIDGET (page), TRUE); } -- 2.44.0 From 633b1c51b2947ab4bc27eeac1384e8efda2b6011 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Thu, 24 Aug 2023 21:19:40 -0400 Subject: [PATCH 03/16] keyboard: Get default input sources from gnome-desktop Right now, we figure out the default input sources ourselves, based on the current locale and layout information coming from localed. This logic needs to be duplicated in several components, so its now provided by gnome-desktop. This commit changes it over to use gnome-desktop APIs. The same time if leverages a gnome-desktop API to fix a bug where cyrillic layouts were getting added without a latin counterpart. --- .../pages/keyboard/gis-keyboard-page.c | 475 +++++++++--------- 1 file changed, 239 insertions(+), 236 deletions(-) diff --git a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c index da384495..ea9b9f35 100644 --- a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c +++ b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c @@ -44,6 +44,8 @@ #define GNOME_DESKTOP_INPUT_SOURCES_DIR "org.gnome.desktop.input-sources" #define KEY_CURRENT_INPUT_SOURCE "current" #define KEY_INPUT_SOURCES "sources" +#define KEY_MRU_SOURCES "mru-sources" +#define KEY_INPUT_OPTIONS "xkb-options" struct _GisKeyboardPagePrivate { GtkWidget *input_chooser; @@ -52,8 +54,14 @@ struct _GisKeyboardPagePrivate { GCancellable *cancellable; GPermission *permission; GSettings *input_settings; - - GSList *system_sources; + char **default_input_source_ids; + char **default_input_source_types; + char **default_options; + char **system_layouts; + char **system_variants; + char **system_options; + + gboolean should_skip; }; typedef struct _GisKeyboardPagePrivate GisKeyboardPagePrivate; @@ -72,98 +80,191 @@ gis_keyboard_page_finalize (GObject *object) g_clear_object (&priv->permission); g_clear_object (&priv->localed); g_clear_object (&priv->input_settings); - - g_slist_free_full (priv->system_sources, g_free); + g_clear_pointer (&priv->default_input_source_ids, g_strfreev); + g_clear_pointer (&priv->default_input_source_types, g_strfreev); + g_clear_pointer (&priv->default_options, g_strfreev); + g_clear_pointer (&priv->system_layouts, g_strfreev); + g_clear_pointer (&priv->system_variants, g_strfreev); + g_clear_pointer (&priv->system_options, g_strfreev); G_OBJECT_CLASS (gis_keyboard_page_parent_class)->finalize (object); } static void -set_input_settings (GisKeyboardPage *self) +add_defaults_to_variant_builder (GisKeyboardPage *self, + const char *already_added_type, + const char *already_added_id, + GVariantBuilder *input_source_builder, + char **default_layout) + { GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); - const gchar *type; - const gchar *id; - GVariantBuilder builder; - GSList *l; - gboolean is_xkb_source = FALSE; + size_t i; - type = cc_input_chooser_get_input_type (CC_INPUT_CHOOSER (priv->input_chooser)); - id = cc_input_chooser_get_input_id (CC_INPUT_CHOOSER (priv->input_chooser)); + for (i = 0; priv->default_input_source_ids && priv->default_input_source_ids[i] != NULL; i++) { + if (g_strcmp0 (already_added_id, priv->default_input_source_ids[i]) == 0 && g_strcmp0 (already_added_type, priv->default_input_source_types[i]) == 0) { + continue; + } - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss)")); + g_variant_builder_add (input_source_builder, "(ss)", priv->default_input_source_types[i], priv->default_input_source_ids[i]); - if (g_str_equal (type, "xkb")) { - g_variant_builder_add (&builder, "(ss)", type, id); - is_xkb_source = TRUE; + if (*default_layout != NULL) { + if (!gnome_input_source_is_non_latin (priv->default_input_source_types[i], priv->default_input_source_ids[i])) { + *default_layout = g_strdup (priv->default_input_source_ids[i]); + } + } } +} - for (l = priv->system_sources; l; l = l->next) { - const gchar *sid = l->data; - if (g_str_equal (id, sid) && g_str_equal (type, "xkb")) - continue; +static void +add_input_source_to_arrays (GisKeyboardPage *self, + const char *type, + const char *id, + GPtrArray *layouts_array, + GPtrArray *variants_array) +{ + g_auto(GStrv) layout_and_variant = NULL; + const char *layout, *variant; + + if (!g_str_equal (type, "xkb")) { + return; + } + + layout_and_variant = g_strsplit (id, "+", -1); + + layout = layout_and_variant[0]; + variant = layout_and_variant[1]?: ""; + + if (g_ptr_array_find_with_equal_func (layouts_array, layout, g_str_equal, NULL) && + g_ptr_array_find_with_equal_func (variants_array, variant, g_str_equal, NULL)) { + return; + } + + g_ptr_array_add (layouts_array, g_strdup (layout)); + g_ptr_array_add (variants_array, g_strdup (variant)); +} + +static void +add_defaults_to_arrays (GisKeyboardPage *self, + GPtrArray *layouts_array, + GPtrArray *variants_array) +{ + GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); + size_t i; + + for (i = 0; priv->default_input_source_ids && priv->default_input_source_ids[i] != NULL; i++) { + add_input_source_to_arrays (self, priv->default_input_source_types[i], priv->default_input_source_ids[i], layouts_array, variants_array); + } +} + +static void +set_input_settings (GisKeyboardPage *self, + const char *type, + const char *id) +{ + GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); + g_autofree char *layout = NULL; + g_autofree char *variant = NULL; + g_autoptr(GVariant) default_input_sources = NULL; + g_autoptr(GVariant) input_sources = NULL; + g_autoptr(GPtrArray) layouts_array = NULL; + g_autoptr(GPtrArray) variants_array = NULL; + GVariantBuilder input_source_builder; + GVariantBuilder input_options_builder; + g_autoptr(GVariant) input_options = NULL; + gboolean is_system_mode; + size_t i; + g_autofree char *default_input_source_id = NULL; + + default_input_sources = g_settings_get_default_value (priv->input_settings, KEY_INPUT_SOURCES); + input_sources = g_settings_get_value (priv->input_settings, KEY_INPUT_SOURCES); + + if (!g_variant_equal (default_input_sources, input_sources)) + return; + + g_clear_pointer (&input_sources, g_variant_unref); + + g_variant_builder_init (&input_options_builder, G_VARIANT_TYPE ("as")); + + is_system_mode = gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER; + + layouts_array = g_ptr_array_new (); + variants_array = g_ptr_array_new (); + + /* Notice the added latin layout (if relevant) gets put first for gsettings + * (input_source_builder and last for localed (layouts_array/variants_array) + * This ensures we get a cyrillic layout on ttys, but a latin layout by default + * in the UI. + */ + g_variant_builder_init (&input_source_builder, G_VARIANT_TYPE ("a(ss)")); + if (type != NULL && id != NULL) { + add_input_source_to_arrays (self, type, id, layouts_array, variants_array); + + if (gnome_input_source_is_non_latin (type, id)) { + default_input_source_id = g_strdup ("us"); + add_input_source_to_arrays (self, "xkb", default_input_source_id, layouts_array, variants_array); + g_variant_builder_add (&input_source_builder, "(ss)", "xkb", default_input_source_id); + } else { + default_input_source_id = g_strdup (id); + } - g_variant_builder_add (&builder, "(ss)", "xkb", sid); + g_variant_builder_add (&input_source_builder, "(ss)", type, id); } - if (!is_xkb_source) - g_variant_builder_add (&builder, "(ss)", type, id); + if (default_input_source_id == NULL || !is_system_mode) { + add_defaults_to_variant_builder (self, type, id, &input_source_builder, &default_input_source_id); + } + input_sources = g_variant_builder_end (&input_source_builder); + + for (i = 0; priv->default_options[i] != NULL; i++) { + g_variant_builder_add (&input_options_builder, "s", priv->default_options[i]); + } + input_options = g_variant_builder_end (&input_options_builder); + + add_defaults_to_arrays (self, layouts_array, variants_array); + g_ptr_array_add (layouts_array, NULL); + g_ptr_array_add (variants_array, NULL); + + priv->system_layouts = (char **) g_ptr_array_steal (layouts_array, NULL); + priv->system_variants = (char **) g_ptr_array_steal (variants_array, NULL); - g_settings_set_value (priv->input_settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); - g_settings_set_uint (priv->input_settings, KEY_CURRENT_INPUT_SOURCE, 0); + g_variant_get (input_options, "^as", &priv->system_options); + + g_settings_set_value (priv->input_settings, KEY_INPUT_SOURCES, g_steal_pointer (&input_sources)); + g_settings_set_value (priv->input_settings, KEY_INPUT_OPTIONS, g_steal_pointer (&input_options)); + + if (default_input_source_id != NULL) { + GVariantBuilder mru_input_source_builder; + + g_variant_builder_init (&mru_input_source_builder, G_VARIANT_TYPE ("a(ss)")); + g_variant_builder_add (&mru_input_source_builder, "(ss)", type, default_input_source_id); + g_settings_set_value (priv->input_settings, KEY_MRU_SOURCES, g_variant_builder_end (&mru_input_source_builder)); + } - g_settings_apply (priv->input_settings); + g_settings_apply (priv->input_settings); } static void set_localed_input (GisKeyboardPage *self) { GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); - const gchar *layout, *variant; - GString *layouts; - GString *variants; - GSList *l; + g_autofree char *layouts = NULL; + g_autofree char *variants = NULL; + g_autofree char *options = NULL; if (!priv->localed) return; - cc_input_chooser_get_layout (CC_INPUT_CHOOSER (priv->input_chooser), &layout, &variant); - if (layout == NULL) - layout = ""; - if (variant == NULL) - variant = ""; - - layouts = g_string_new (layout); - variants = g_string_new (variant); - -#define LAYOUT(a) (a[0]) -#define VARIANT(a) (a[1] ? a[1] : "") - for (l = priv->system_sources; l; l = l->next) { - const gchar *sid = l->data; - gchar **lv = g_strsplit (sid, "+", -1); - - if (!g_str_equal (LAYOUT (lv), layout) || - !g_str_equal (VARIANT (lv), variant)) { - if (layouts->str[0]) { - g_string_append_c (layouts, ','); - g_string_append_c (variants, ','); - } - g_string_append (layouts, LAYOUT (lv)); - g_string_append (variants, VARIANT (lv)); - } - g_strfreev (lv); - } -#undef LAYOUT -#undef VARIANT + layouts = g_strjoinv (",", priv->system_layouts); + variants = g_strjoinv (",", priv->system_variants); + options = g_strjoinv (",", priv->system_options); g_dbus_proxy_call (priv->localed, "SetX11Keyboard", - g_variant_new ("(ssssbb)", layouts->str, "", variants->str, "", TRUE, TRUE), + g_variant_new ("(ssssbb)", layouts, "", variants, options, TRUE, TRUE), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); - g_string_free (layouts, TRUE); - g_string_free (variants, TRUE); } static void @@ -192,8 +293,13 @@ static void update_input (GisKeyboardPage *self) { GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); + const gchar *type; + const gchar *id; - set_input_settings (self); + type = cc_input_chooser_get_input_type (CC_INPUT_CHOOSER (priv->input_chooser)); + id = cc_input_chooser_get_input_id (CC_INPUT_CHOOSER (priv->input_chooser)); + + set_input_settings (self, type, id); if (gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER) { if (g_permission_get_allowed (priv->permission)) { @@ -215,119 +321,10 @@ gis_keyboard_page_apply (GisPage *page, return FALSE; } -static GSList * -get_localed_input (GDBusProxy *proxy) -{ - GVariant *v; - const gchar *s; - gchar *id; - guint i, n; - gchar **layouts = NULL; - gchar **variants = NULL; - GSList *sources = NULL; - - v = g_dbus_proxy_get_cached_property (proxy, "X11Layout"); - if (v) { - s = g_variant_get_string (v, NULL); - layouts = g_strsplit (s, ",", -1); - g_variant_unref (v); - } - - v = g_dbus_proxy_get_cached_property (proxy, "X11Variant"); - if (v) { - s = g_variant_get_string (v, NULL); - if (s && *s) - variants = g_strsplit (s, ",", -1); - g_variant_unref (v); - } - - if (variants && variants[0]) - n = MIN (g_strv_length (layouts), g_strv_length (variants)); - else if (layouts && layouts[0]) - n = g_strv_length (layouts); - else - n = 0; - - for (i = 0; i < n && layouts[i][0]; i++) { - if (variants && variants[i] && variants[i][0]) - id = g_strdup_printf ("%s+%s", layouts[i], variants[i]); - else - id = g_strdup (layouts[i]); - sources = g_slist_prepend (sources, id); - } - - g_strfreev (variants); - g_strfreev (layouts); - - return sources; -} - static void -add_default_keyboard_layout (GDBusProxy *proxy, - GVariantBuilder *builder) +add_default_input_sources (GisKeyboardPage *self) { - GSList *sources = get_localed_input (proxy); - sources = g_slist_reverse (sources); - - for (; sources; sources = sources->next) - g_variant_builder_add (builder, "(ss)", "xkb", - (const gchar *) sources->data); - - g_slist_free_full (sources, g_free); -} - -static void -add_default_input_sources (GisKeyboardPage *self, - GDBusProxy *proxy) -{ - const gchar *type; - const gchar *id; - gchar *language; - GVariantBuilder builder; - GSettings *input_settings; - - input_settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR); - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss)")); - - add_default_keyboard_layout (proxy, &builder); - - /* add other input sources */ - language = cc_common_language_get_current_language (); - if (gnome_get_input_source_from_locale (language, &type, &id)) { - if (!g_str_equal (type, "xkb")) - g_variant_builder_add (&builder, "(ss)", type, id); - } - g_free (language); - - g_settings_delay (input_settings); - g_settings_set_value (input_settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); - g_settings_set_uint (input_settings, KEY_CURRENT_INPUT_SOURCE, 0); - g_settings_apply (input_settings); - - g_object_unref (input_settings); -} - -static void -skip_proxy_ready (GObject *source, - GAsyncResult *res, - gpointer data) -{ - GisKeyboardPage *self = data; - GDBusProxy *proxy; - GError *error = NULL; - - proxy = g_dbus_proxy_new_finish (res, &error); - - if (!proxy) { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("Failed to contact localed: %s", error->message); - g_error_free (error); - return; - } - - add_default_input_sources (self, proxy); - - g_object_unref (proxy); + set_input_settings (self, NULL, NULL); } static void @@ -336,77 +333,49 @@ gis_keyboard_page_skip (GisPage *page) GisKeyboardPage *self = GIS_KEYBOARD_PAGE (page); GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); - g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, - NULL, - "org.freedesktop.locale1", - "/org/freedesktop/locale1", - "org.freedesktop.locale1", - priv->cancellable, - (GAsyncReadyCallback) skip_proxy_ready, - self); + priv->should_skip = TRUE; + + if (priv->default_input_source_ids != NULL) + add_default_input_sources (self); } static void preselect_input_source (GisKeyboardPage *self) { - const gchar *type; - const gchar *id; - gchar *language; - gboolean desktop_got_something; - gboolean desktop_got_input_method; + const char *language = NULL; GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); - GSList *sources = get_localed_input (priv->localed); - - /* These will be added silently after the user selection when - * writing out the settings. */ - g_slist_free_full (priv->system_sources, g_free); - priv->system_sources = g_slist_reverse (sources); - - /* We have two potential sources of information as to which - * source to pre-select here: the keyboard layout that is - * configured system-wide (read from priv->system_sources), - * and a gnome-desktop function that lets us look up a default - * input source for a given language. - * - * An important limitation here is that there is no system-wide - * configuration for input methods, so if the best choice for the - * language is an input method, we will only find it from the - * gnome-desktop lookup. But if both sources give us keyboard layouts, - * we want to prefer the one that's configured system-wide over the one - * from gnome-desktop. - * - * So we first do the gnome-desktop lookup, and keep track of what we - * got. - * - * - If we got an input method, we preselect that, and we're done. - * - If we got a keyboard layout, and there's no system-wide keyboard - * layout set, we preselect the layout we got from gnome-desktop. - * - If we didn't get an input method from gnome-desktop and there - * is a system-wide keyboard layout set, we preselect that. - * - If we got nothing from gnome-desktop and there's no system-wide - * keyboard layout set, we don't preselect anything. - * - * See: - * - https://bugzilla.gnome.org/show_bug.cgi?id=776189 - * - https://gitlab.gnome.org/GNOME/gnome-initial-setup/-/issues/104 - */ - language = cc_common_language_get_current_language (); - desktop_got_something = gnome_get_input_source_from_locale (language, &type, &id); - desktop_got_input_method = (desktop_got_something && g_strcmp0 (type, "xkb") != 0); + language = cc_common_language_get_current_language (); - if (desktop_got_something && (desktop_got_input_method || !priv->system_sources)) { - cc_input_chooser_set_input (CC_INPUT_CHOOSER (priv->input_chooser), - id, type); - } else if (priv->system_sources) { - cc_input_chooser_set_input (CC_INPUT_CHOOSER (priv->input_chooser), - (const gchar *) priv->system_sources->data, - "xkb"); + /* We deduce the initial input source from language if we're in a system mode + * (where preexisting system configuration may be stale) or if the language + * requires an input method (because there is no way for system configuration + * to denote the need for an input method) + * + * If it's a non-system mode we can trust the system configuration is probably + * a better bet than a heuristic based on locale. + */ + if (language != NULL) { + gboolean got_input_source; + const char *id, *type; + + got_input_source = gnome_get_input_source_from_locale (language, &type, &id); + + if (got_input_source) { + gboolean is_system_mode = gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER; + if (is_system_mode || g_str_equal (type, "ibus")) { + cc_input_chooser_set_input (CC_INPUT_CHOOSER (priv->input_chooser), + id, + type); + return; + } + } } - g_free (language); + cc_input_chooser_set_input (CC_INPUT_CHOOSER (priv->input_chooser), + priv->default_input_source_ids[0], + priv->default_input_source_types[0]); } static void @@ -445,9 +414,7 @@ localed_proxy_ready (GObject *source, } priv->localed = proxy; - - preselect_input_source (self); - update_page_complete (self); + update_page_complete (self); } static void @@ -464,6 +431,40 @@ input_changed (CcInputChooser *chooser, update_page_complete (self); } +static void +on_got_default_sources (GObject *source, + GAsyncResult *res, + gpointer data) +{ + GisKeyboardPage *self = data; + GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); + g_autoptr (GError) error = NULL; + gboolean success = FALSE; + g_auto (GStrv) ids = NULL; + g_auto (GStrv) types = NULL; + g_auto (GStrv) options = NULL; + + success = gnome_get_default_input_sources_finish (res, &ids, &types, &options, NULL, &error); + + if (!success) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to fetch default input sources: %s", error->message); + return; + } + + priv->default_input_source_ids = g_steal_pointer (&ids); + priv->default_input_source_types = g_steal_pointer (&types); + priv->default_options = g_steal_pointer (&options); + + if (priv->should_skip) { + add_default_input_sources (self); + return; + } + + preselect_input_source (self); + update_page_complete (self); +} + static void gis_keyboard_page_constructed (GObject *object) { @@ -492,6 +493,8 @@ gis_keyboard_page_constructed (GObject *object) (GAsyncReadyCallback) localed_proxy_ready, self); + gnome_get_default_input_sources (priv->cancellable, on_got_default_sources, self); + /* If we're in new user mode then we're manipulating system settings */ if (gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER) priv->permission = polkit_permission_new_sync ("org.freedesktop.locale1.set-keyboard", NULL, NULL, NULL); -- 2.44.0 From cd68a6bd404679af1eba0c8aea13dfe235d4ee1e Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Sun, 13 Aug 2023 09:09:56 -0400 Subject: [PATCH 04/16] driver: Specify mode via flags instead of boolean At the moment we just have system mode and new user mode, but we're actually going to want other modes (such as live user mode) as well. Currently the code distinguishes between its two available modes using a boolean `is_new_user`. That isn't extensible beyond two modes, so this commit changes it use bit flags instead. --- gnome-initial-setup/gis-driver.c | 17 ++- gnome-initial-setup/gis-driver.h | 8 +- gnome-initial-setup/gnome-initial-setup.c | 126 +++++++++++++--------- 3 files changed, 92 insertions(+), 59 deletions(-) diff --git a/gnome-initial-setup/gis-driver.c b/gnome-initial-setup/gis-driver.c index 52d5874c..1538f927 100644 --- a/gnome-initial-setup/gis-driver.c +++ b/gnome-initial-setup/gis-driver.c @@ -33,8 +33,6 @@ #include "cc-common-language.h" #include "gis-assistant.h" -#define GIS_TYPE_DRIVER_MODE (gis_driver_mode_get_type ()) - /* Statically include this for now. Maybe later * we'll generate this from glib-mkenums. */ GType @@ -42,12 +40,13 @@ gis_driver_mode_get_type (void) { static GType enum_type_id = 0; if (G_UNLIKELY (!enum_type_id)) { - static const GEnumValue values[] = { + static const GFlagsValue values[] = { { GIS_DRIVER_MODE_NEW_USER, "GIS_DRIVER_MODE_NEW_USER", "new_user" }, { GIS_DRIVER_MODE_EXISTING_USER, "GIS_DRIVER_MODE_EXISTING_USER", "existing_user" }, + { GIS_DRIVER_MODE_ALL, "GIS_DRIVER_MODE_ALL", "all" }, { 0, NULL, NULL } }; - enum_type_id = g_enum_register_static("GisDriverMode", values); + enum_type_id = g_flags_register_static("GisDriverMode", values); } return enum_type_id; } @@ -711,7 +710,7 @@ gis_driver_set_property (GObject *object, switch ((GisDriverProperty) prop_id) { case PROP_MODE: - driver->mode = g_value_get_enum (value); + driver->mode = g_value_get_flags (value); break; case PROP_USERNAME: g_free (driver->username); @@ -926,10 +925,10 @@ gis_driver_class_init (GisDriverClass *klass) G_TYPE_NONE, 0); obj_props[PROP_MODE] = - g_param_spec_enum ("mode", "", "", - GIS_TYPE_DRIVER_MODE, - GIS_DRIVER_MODE_EXISTING_USER, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_param_spec_flags ("mode", "", "", + GIS_TYPE_DRIVER_MODE, + GIS_DRIVER_MODE_EXISTING_USER, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_props[PROP_USERNAME] = g_param_spec_string ("username", "", "", diff --git a/gnome-initial-setup/gis-driver.h b/gnome-initial-setup/gis-driver.h index ca06391f..36bfa2dd 100644 --- a/gnome-initial-setup/gis-driver.h +++ b/gnome-initial-setup/gis-driver.h @@ -31,6 +31,7 @@ G_BEGIN_DECLS #define GIS_TYPE_DRIVER (gis_driver_get_type ()) +#define GIS_TYPE_DRIVER_MODE (gis_driver_mode_get_type ()) G_DECLARE_FINAL_TYPE (GisDriver, gis_driver, GIS, DRIVER, AdwApplication) @@ -41,10 +42,13 @@ typedef enum { } UmAccountMode; typedef enum { - GIS_DRIVER_MODE_NEW_USER, - GIS_DRIVER_MODE_EXISTING_USER, + GIS_DRIVER_MODE_NEW_USER = 1 << 0, + GIS_DRIVER_MODE_EXISTING_USER = 1 << 1, + GIS_DRIVER_MODE_ALL = (GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), } GisDriverMode; +GType gis_driver_mode_get_type (void); + GisAssistant *gis_driver_get_assistant (GisDriver *driver); void gis_driver_set_user_permissions (GisDriver *driver, diff --git a/gnome-initial-setup/gnome-initial-setup.c b/gnome-initial-setup/gnome-initial-setup.c index adb04075..a079c705 100644 --- a/gnome-initial-setup/gnome-initial-setup.c +++ b/gnome-initial-setup/gnome-initial-setup.c @@ -55,26 +55,26 @@ typedef GisPage *(*PreparePage) (GisDriver *driver); typedef struct { const gchar *page_id; PreparePage prepare_page_func; - gboolean new_user_only; + GisDriverMode modes; } PageData; -#define PAGE(name, new_user_only) { #name, gis_prepare_ ## name ## _page, new_user_only } +#define PAGE(name, modes) { #name, gis_prepare_ ## name ## _page, modes } static PageData page_table[] = { - PAGE (welcome, FALSE), - PAGE (language, FALSE), - PAGE (keyboard, FALSE), - PAGE (network, FALSE), - PAGE (privacy, FALSE), - PAGE (timezone, TRUE), - PAGE (software, TRUE), - PAGE (account, TRUE), - PAGE (password, TRUE), + PAGE (welcome, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), + PAGE (language, GIS_DRIVER_MODE_ALL), + PAGE (keyboard, GIS_DRIVER_MODE_ALL), + PAGE (network, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), + PAGE (privacy, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), + PAGE (timezone, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), + PAGE (software, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), + PAGE (account, GIS_DRIVER_MODE_NEW_USER), + PAGE (password, GIS_DRIVER_MODE_NEW_USER), #ifdef HAVE_PARENTAL_CONTROLS - PAGE (parental_controls, TRUE), - PAGE (parent_password, TRUE), + PAGE (parental_controls, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), + PAGE (parent_password, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), #endif - PAGE (summary, FALSE), + PAGE (summary, GIS_DRIVER_MODE_NEW_USER), { NULL }, }; @@ -105,26 +105,15 @@ should_skip_page (const gchar *page_id, } static gchar ** -strv_append (gchar **a, - gchar **b) +pages_to_skip_from_file (GisDriver *driver) { - guint n = g_strv_length (a); - guint m = g_strv_length (b); - - a = g_renew (gchar *, a, n + m + 1); - for (guint i = 0; i < m; i++) - a[n + i] = g_strdup (b[i]); - a[n + m] = NULL; - - return a; -} - -static gchar ** -pages_to_skip_from_file (GisDriver *driver, - gboolean is_new_user) -{ - GStrv skip_pages = NULL; - GStrv additional_skip_pages = NULL; + GisDriverMode driver_mode; + GisDriverMode other_modes; + g_autoptr(GStrvBuilder) builder = g_strv_builder_new(); + g_auto (GStrv) skip_pages = NULL; + g_autofree char *mode_group = NULL; + g_autoptr (GFlagsClass) driver_mode_flags_class = NULL; + const GFlagsValue *driver_mode_flags = NULL; /* This code will read the keyfile containing vendor customization options and * look for options under the "pages" group, and supports the following keys: @@ -132,28 +121,68 @@ pages_to_skip_from_file (GisDriver *driver, * - new_user_only (optional): list of pages to be skipped in existing user mode * - existing_user_only (optional): list of pages to be skipped in new user mode * + * In addition it will look for options under the "{mode} pages" group where {mode} is the + * current driver mode for the following keys: + * - skip (optional): list of pages to be skipped for the current mode + * * This is how this file might look on a vendor image: * * [pages] * skip=timezone + * + * [new_user pages] + * skip=language;keyboard + * + * Older files might look like so: + * + * [pages] + * skip=timezone * existing_user_only=language;keyboard */ skip_pages = gis_driver_conf_get_string_list (driver, VENDOR_PAGES_GROUP, VENDOR_SKIP_KEY, NULL); - additional_skip_pages = - gis_driver_conf_get_string_list (driver, VENDOR_PAGES_GROUP, - is_new_user ? VENDOR_EXISTING_USER_ONLY_KEY : VENDOR_NEW_USER_ONLY_KEY, - NULL); - - if (!skip_pages && additional_skip_pages) { - skip_pages = additional_skip_pages; - } else if (skip_pages && additional_skip_pages) { - skip_pages = strv_append (skip_pages, additional_skip_pages); - g_strfreev (additional_skip_pages); + if (skip_pages != NULL) + { + g_strv_builder_addv (builder, (const char **) skip_pages); + g_clear_pointer (&skip_pages, g_strfreev); + } + + driver_mode_flags_class = g_type_class_ref (GIS_TYPE_DRIVER_MODE); + + driver_mode = gis_driver_get_mode (driver); + driver_mode_flags = g_flags_get_first_value (driver_mode_flags_class, driver_mode); + + mode_group = g_strdup_printf ("%s pages", driver_mode_flags->value_nick); + skip_pages = gis_driver_conf_get_string_list (driver, mode_group, + VENDOR_SKIP_KEY, NULL); + if (skip_pages != NULL) + { + g_strv_builder_addv (builder, (const char **) skip_pages); + g_clear_pointer (&skip_pages, g_strfreev); + } + + other_modes = GIS_DRIVER_MODE_ALL & ~driver_mode; + while (other_modes) { + const GFlagsValue *other_mode_flags = g_flags_get_first_value (driver_mode_flags_class, other_modes); + + if (other_mode_flags != NULL) { + g_autofree char *vendor_key = g_strdup_printf ("%s_only", other_mode_flags->value_nick); + + skip_pages = gis_driver_conf_get_string_list (driver, VENDOR_PAGES_GROUP, + vendor_key, NULL); + + if (skip_pages != NULL) + { + g_strv_builder_addv (builder, (const char **) skip_pages); + g_clear_pointer (&skip_pages, g_strfreev); + } + + other_modes &= ~other_mode_flags->value; + } } - return skip_pages; + return g_strv_builder_end (builder); } static void @@ -196,7 +225,8 @@ rebuild_pages_cb (GisDriver *driver) GisAssistant *assistant; GisPage *current_page; gchar **skip_pages; - gboolean is_new_user, skipped; + GisDriverMode driver_mode; + gboolean skipped; assistant = gis_driver_get_assistant (driver); current_page = gis_assistant_get_current_page (assistant); @@ -215,13 +245,13 @@ rebuild_pages_cb (GisDriver *driver) ++page_data; } - is_new_user = (gis_driver_get_mode (driver) == GIS_DRIVER_MODE_NEW_USER); - skip_pages = pages_to_skip_from_file (driver, is_new_user); + driver_mode = gis_driver_get_mode (driver); + skip_pages = pages_to_skip_from_file (driver); for (; page_data->page_id != NULL; ++page_data) { skipped = FALSE; - if ((page_data->new_user_only && !is_new_user) || + if (((page_data->modes & driver_mode) == 0) || (should_skip_page (page_data->page_id, skip_pages))) skipped = TRUE; -- 2.44.0 From 26378d689b12ba869ecce05030b2a8035c536c20 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Wed, 30 Aug 2023 15:08:23 -0400 Subject: [PATCH 05/16] assistant: Show Back button on summary page commit f60b4622350468f7ef17f79d9bc6679bf8cce7b9 changed the assistant to no longer show the back and forward buttons on the last page. Hiding the forward button makes sense: there's no more pages go to. Hiding the back button doesn't really make sense: there are a bunch of pages the user could potentially want to revisit. This commit shows the back button on the last page (either the summary page or the install page). --- gnome-initial-setup/gis-assistant.c | 14 +++++++++----- .../pages/summary/gis-summary-page.c | 16 +++++++++------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/gnome-initial-setup/gis-assistant.c b/gnome-initial-setup/gis-assistant.c index a3122b71..8a7fc52b 100644 --- a/gnome-initial-setup/gis-assistant.c +++ b/gnome-initial-setup/gis-assistant.c @@ -59,6 +59,8 @@ struct _GisAssistant GList *pages; GisPage *current_page; + + gboolean data_saved; }; G_DEFINE_TYPE (GisAssistant, gis_assistant, GTK_TYPE_BOX) @@ -182,6 +184,7 @@ update_navigation_buttons (GisAssistant *assistant) { GisPage *page = assistant->current_page; GList *l; + gboolean is_first_page; gboolean is_last_page; if (page == NULL) @@ -189,11 +192,13 @@ update_navigation_buttons (GisAssistant *assistant) l = g_list_find (assistant->pages, page); + is_first_page = (l->prev == NULL); is_last_page = (l->next == NULL); + gtk_widget_set_visible (assistant->back, !is_first_page && !assistant->data_saved); + if (is_last_page) { - gtk_widget_set_visible (assistant->back, FALSE); gtk_widget_set_visible (assistant->forward, FALSE); gtk_widget_set_visible (assistant->skip, FALSE); gtk_widget_set_visible (assistant->cancel, FALSE); @@ -201,12 +206,8 @@ update_navigation_buttons (GisAssistant *assistant) } else { - gboolean is_first_page; GtkWidget *next_widget; - is_first_page = (l->prev == NULL); - gtk_widget_set_visible (assistant->back, !is_first_page); - if (gis_page_get_needs_accept (page)) next_widget = assistant->accept; else @@ -418,6 +419,9 @@ gis_assistant_save_data (GisAssistant *assistant, { GList *l; + assistant->data_saved = TRUE; + gtk_widget_set_visible (assistant->back, FALSE); + for (l = assistant->pages; l != NULL; l = l->next) { if (!gis_page_save_data (l->data, error)) diff --git a/gnome-initial-setup/pages/summary/gis-summary-page.c b/gnome-initial-setup/pages/summary/gis-summary-page.c index d3bb7999..9eaf90dd 100644 --- a/gnome-initial-setup/pages/summary/gis-summary-page.c +++ b/gnome-initial-setup/pages/summary/gis-summary-page.c @@ -180,6 +180,15 @@ log_user_in (GisSummaryPage *page) static void done_cb (GtkButton *button, GisSummaryPage *page) { + g_autoptr (GError) error = NULL; + + if (!gis_driver_save_data (GIS_PAGE (page)->driver, &error)) + { + /* FIXME: This should probably be shown to the user and some options + * provided to them. */ + g_warning ("Error saving data: %s", error->message); + } + gis_ensure_stamp_files (GIS_PAGE (page)->driver); switch (gis_driver_get_mode (GIS_PAGE (page)->driver)) @@ -202,13 +211,6 @@ gis_summary_page_shown (GisPage *page) GisSummaryPagePrivate *priv = gis_summary_page_get_instance_private (summary); g_autoptr(GError) local_error = NULL; - if (!gis_driver_save_data (GIS_PAGE (page)->driver, &local_error)) - { - /* FIXME: This should probably be shown to the user and some options - * provided to them. */ - g_warning ("Error saving data: %s", local_error->message); - } - gis_driver_get_user_permissions (GIS_PAGE (page)->driver, &priv->user_account, &priv->user_password); -- 2.44.0 From 8c26749626ff7f0391632cc5e01944642ca72692 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Sun, 13 Aug 2023 09:39:07 -0400 Subject: [PATCH 06/16] gnome-initial-setup: Add live user mode This commit adds a new "live user" mode meant to be run in live image environments. It asks questions the user should answer before the installer is started, and provides a way for the user to initiate the installer or just jump into the live session instead. --- data/20-gnome-initial-setup.rules.in | 3 +- gnome-initial-setup/gis-driver.c | 4 +- gnome-initial-setup/gis-driver.h | 4 +- gnome-initial-setup/gis-util.c | 122 ++++++ gnome-initial-setup/gis-util.h | 4 + gnome-initial-setup/gnome-initial-setup.c | 24 +- .../pages/account/gis-account-pages.c | 21 + .../pages/install/gis-install-page.c | 382 ++++++++++++++++++ .../pages/install/gis-install-page.css | 11 + .../pages/install/gis-install-page.h | 52 +++ .../pages/install/gis-install-page.ui | 51 +++ .../pages/install/install.gresource.xml | 8 + gnome-initial-setup/pages/install/meson.build | 9 + .../pages/keyboard/gis-keyboard-page.c | 10 +- .../pages/language/gis-language-page.c | 5 +- gnome-initial-setup/pages/meson.build | 1 + .../pages/password/gis-password-page.c | 6 + 17 files changed, 700 insertions(+), 17 deletions(-) create mode 100644 gnome-initial-setup/pages/install/gis-install-page.c create mode 100644 gnome-initial-setup/pages/install/gis-install-page.css create mode 100644 gnome-initial-setup/pages/install/gis-install-page.h create mode 100644 gnome-initial-setup/pages/install/gis-install-page.ui create mode 100644 gnome-initial-setup/pages/install/install.gresource.xml create mode 100644 gnome-initial-setup/pages/install/meson.build diff --git a/data/20-gnome-initial-setup.rules.in b/data/20-gnome-initial-setup.rules.in index 02fd21d0..881efde9 100644 --- a/data/20-gnome-initial-setup.rules.in +++ b/data/20-gnome-initial-setup.rules.in @@ -16,7 +16,8 @@ polkit.addRule(function(action, subject) { action.id.indexOf('org.freedesktop.timedate1.') === 0 || action.id.indexOf('org.freedesktop.realmd.') === 0 || action.id.indexOf('com.endlessm.ParentalControls.') === 0 || - action.id.indexOf('org.fedoraproject.thirdparty.') === 0); + action.id.indexOf('org.fedoraproject.thirdparty.') === 0 || + action.id.indexOf('org.freedesktop.login1.reboot') === 0); if (actionMatches) { if (subject.local) diff --git a/gnome-initial-setup/gis-driver.c b/gnome-initial-setup/gis-driver.c index 1538f927..1c22ba86 100644 --- a/gnome-initial-setup/gis-driver.c +++ b/gnome-initial-setup/gis-driver.c @@ -43,6 +43,8 @@ gis_driver_mode_get_type (void) { static const GFlagsValue values[] = { { GIS_DRIVER_MODE_NEW_USER, "GIS_DRIVER_MODE_NEW_USER", "new_user" }, { GIS_DRIVER_MODE_EXISTING_USER, "GIS_DRIVER_MODE_EXISTING_USER", "existing_user" }, + { GIS_DRIVER_MODE_LIVE_USER, "GIS_DRIVER_MODE_LIVE_USER", "live_user" }, + { GIS_DRIVER_MODE_SYSTEM, "GIS_DRIVER_MODE_SYSTEM", "system" }, { GIS_DRIVER_MODE_ALL, "GIS_DRIVER_MODE_ALL", "all" }, { 0, NULL, NULL } }; @@ -855,7 +857,7 @@ gis_driver_startup (GApplication *app) G_APPLICATION_CLASS (gis_driver_parent_class)->startup (app); - if (driver->mode == GIS_DRIVER_MODE_NEW_USER) + if (driver->mode & GIS_DRIVER_MODE_SYSTEM) connect_to_gdm (driver); driver->main_window = g_object_new (GTK_TYPE_APPLICATION_WINDOW, diff --git a/gnome-initial-setup/gis-driver.h b/gnome-initial-setup/gis-driver.h index 36bfa2dd..d4b42b10 100644 --- a/gnome-initial-setup/gis-driver.h +++ b/gnome-initial-setup/gis-driver.h @@ -44,7 +44,9 @@ typedef enum { typedef enum { GIS_DRIVER_MODE_NEW_USER = 1 << 0, GIS_DRIVER_MODE_EXISTING_USER = 1 << 1, - GIS_DRIVER_MODE_ALL = (GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), + GIS_DRIVER_MODE_LIVE_USER = 1 << 2, + GIS_DRIVER_MODE_SYSTEM = (GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_USER), + GIS_DRIVER_MODE_ALL = (GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER | GIS_DRIVER_MODE_LIVE_USER), } GisDriverMode; GType gis_driver_mode_get_type (void); diff --git a/gnome-initial-setup/gis-util.c b/gnome-initial-setup/gis-util.c index ac153fc1..424c26d7 100644 --- a/gnome-initial-setup/gis-util.c +++ b/gnome-initial-setup/gis-util.c @@ -31,3 +31,125 @@ gis_add_style_from_resource (const char *resource_path) GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); } + +gboolean +gis_kernel_command_line_has_argument (const char *arguments[]) +{ + GError *error = NULL; + g_autofree char *contents = NULL; + g_autoptr (GString) pattern = NULL; + gboolean has_argument = FALSE; + size_t i; + + if (!g_file_get_contents ("/proc/cmdline", &contents, NULL, &error)) { + g_error_free (error); + return FALSE; + } + + /* Build up the pattern by iterating through the alternatives, + * escaping all dots so they don't match any character but period, + * and adding word boundary specifiers around the arguments so + * substrings don't get matched. + * + * Also, add a | between each alternative. + */ + pattern = g_string_new (NULL); + for (i = 0; arguments[i] != NULL; i++) { + g_autofree char *escaped_argument = g_regex_escape_string (arguments[i], -1); + + if (i > 0) { + g_string_append (pattern, "|"); + } + + g_string_append (pattern, "\\b"); + + g_string_append (pattern, escaped_argument); + + g_string_append (pattern, "\\b"); + } + + has_argument = g_regex_match_simple (pattern->str, contents, 0, 0); + + return has_argument; +} + +static gboolean +is_valid_shell_identifier_character (char c, + gboolean first) +{ + return (!first && g_ascii_isdigit (c)) || + c == '_' || + g_ascii_isalpha (c); +} + +void +gis_substitute_variables_in_text (char **text, + GisVariableLookupFunc lookup_func, + gpointer user_data) +{ + GString *s = g_string_new (""); + const char *p, *start; + char c; + + p = *text; + while (*p) { + c = *p; + if (c == '\\') { + p++; + c = *p; + if (c != '\0') { + p++; + switch (c) { + case '\\': + g_string_append_c (s, '\\'); + break; + case '$': + g_string_append_c (s, '$'); + break; + default: + g_string_append_c (s, '\\'); + g_string_append_c (s, c); + break; + } + } + } else if (c == '$') { + gboolean brackets = FALSE; + p++; + if (*p == '{') { + brackets = TRUE; + p++; + } + start = p; + while (*p != '\0' && + is_valid_shell_identifier_character (*p, p == start)) { + p++; + } + if (p == start || (brackets && *p != '}')) { + g_string_append_c (s, '$'); + if (brackets) + g_string_append_c (s, '{'); + g_string_append_len (s, start, p - start); + } else { + g_autofree char *variable = NULL; + g_autofree char *value = NULL; + + variable = g_strndup (start, p - start); + + if (brackets && *p == '}') + p++; + + if (lookup_func) + value = lookup_func (variable, user_data); + if (value) { + g_string_append (s, value); + } + } + } else { + p++; + g_string_append_c (s, c); + } + } + g_free (*text); + *text = g_string_free (s, FALSE); +} + diff --git a/gnome-initial-setup/gis-util.h b/gnome-initial-setup/gis-util.h index 5041bddd..80d4f9a0 100644 --- a/gnome-initial-setup/gis-util.h +++ b/gnome-initial-setup/gis-util.h @@ -17,3 +17,7 @@ #pragma once void gis_add_style_from_resource (const char *path); +gboolean gis_kernel_command_line_has_argument (const char *arguments[]); + +typedef char * (* GisVariableLookupFunc) (const char *key, gpointer user_data); +void gis_substitute_variables_in_text (char **text, GisVariableLookupFunc lookup_func, gpointer user_data); diff --git a/gnome-initial-setup/gnome-initial-setup.c b/gnome-initial-setup/gnome-initial-setup.c index a079c705..02ca2808 100644 --- a/gnome-initial-setup/gnome-initial-setup.c +++ b/gnome-initial-setup/gnome-initial-setup.c @@ -40,13 +40,16 @@ #include "pages/parental-controls/gis-parental-controls-page.h" #include "pages/password/gis-password-page.h" #include "pages/summary/gis-summary-page.h" +#include "pages/install/gis-install-page.h" #define VENDOR_PAGES_GROUP "pages" #define VENDOR_SKIP_KEY "skip" #define VENDOR_NEW_USER_ONLY_KEY "new_user_only" #define VENDOR_EXISTING_USER_ONLY_KEY "existing_user_only" +#define VENDOR_LIVE_USER_ONLY_KEY "live_user_only" static gboolean force_existing_user_mode; +static gboolean force_live_user_mode; static GPtrArray *skipped_pages; @@ -64,17 +67,19 @@ static PageData page_table[] = { PAGE (welcome, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), PAGE (language, GIS_DRIVER_MODE_ALL), PAGE (keyboard, GIS_DRIVER_MODE_ALL), - PAGE (network, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), + PAGE (network, GIS_DRIVER_MODE_ALL), PAGE (privacy, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), - PAGE (timezone, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), + PAGE (timezone, GIS_DRIVER_MODE_ALL), PAGE (software, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), - PAGE (account, GIS_DRIVER_MODE_NEW_USER), + /* In live user mode, the account page isn't displayed, it just quietly creates the live user */ + PAGE (account, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_USER), PAGE (password, GIS_DRIVER_MODE_NEW_USER), #ifdef HAVE_PARENTAL_CONTROLS PAGE (parental_controls, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), PAGE (parent_password, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), #endif PAGE (summary, GIS_DRIVER_MODE_NEW_USER), + PAGE (install, GIS_DRIVER_MODE_LIVE_USER), { NULL }, }; @@ -118,8 +123,8 @@ pages_to_skip_from_file (GisDriver *driver) /* This code will read the keyfile containing vendor customization options and * look for options under the "pages" group, and supports the following keys: * - skip (optional): list of pages to be skipped always - * - new_user_only (optional): list of pages to be skipped in existing user mode - * - existing_user_only (optional): list of pages to be skipped in new user mode + * - new_user_only (optional): list of pages to be skipped for modes other than new_user + * - existing_user_only (optional): list of pages to be skipped for modes other than existing_user * * In addition it will look for options under the "{mode} pages" group where {mode} is the * current driver mode for the following keys: @@ -275,6 +280,8 @@ get_mode (void) { if (force_existing_user_mode) return GIS_DRIVER_MODE_EXISTING_USER; + else if (force_live_user_mode) + return GIS_DRIVER_MODE_LIVE_USER; else return GIS_DRIVER_MODE_NEW_USER; } @@ -308,6 +315,8 @@ main (int argc, char *argv[]) GOptionEntry entries[] = { { "existing-user", 0, 0, G_OPTION_ARG_NONE, &force_existing_user_mode, _("Force existing user mode"), NULL }, + { "live-user", 0, 0, G_OPTION_ARG_NONE, &force_live_user_mode, + _("Force live user mode"), NULL }, { NULL } }; @@ -326,6 +335,9 @@ main (int argc, char *argv[]) g_option_context_parse (context, &argc, &argv, NULL); + if (gis_kernel_command_line_has_argument ((const char *[]) { "rd.live.image", "endless.live_boot", NULL })) + force_live_user_mode = TRUE; + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); @@ -344,7 +356,7 @@ main (int argc, char *argv[]) * the keyring manually so that we can pass the credentials * along to the new user in the handoff. */ - if (mode == GIS_DRIVER_MODE_NEW_USER && !gis_get_mock_mode ()) + if ((mode & GIS_DRIVER_MODE_SYSTEM) && !gis_get_mock_mode ()) gis_ensure_login_keyring (); driver = gis_driver_new (mode); diff --git a/gnome-initial-setup/pages/account/gis-account-pages.c b/gnome-initial-setup/pages/account/gis-account-pages.c index d9cc8d9f..8b0d8e99 100644 --- a/gnome-initial-setup/pages/account/gis-account-pages.c +++ b/gnome-initial-setup/pages/account/gis-account-pages.c @@ -26,6 +26,27 @@ GisPage * gis_prepare_account_page (GisDriver *driver) { + GisDriverMode driver_mode; + + driver_mode = gis_driver_get_mode (driver); + + if (driver_mode == GIS_DRIVER_MODE_LIVE_USER && !gis_kernel_command_line_has_argument ((const char *[]) { "rd.live.overlay", NULL })) { + ActUserManager *act_client = act_user_manager_get_default (); + const char *username = "liveuser"; + g_autoptr(ActUser) user = NULL; + g_autoptr(GError) error = NULL; + + user = act_user_manager_create_user (act_client, username, username, ACT_USER_ACCOUNT_TYPE_ADMINISTRATOR, &error); + + if (user != NULL) { + act_user_set_password_mode (user, ACT_USER_PASSWORD_MODE_NONE); + gis_driver_set_username (driver, username); + gis_driver_set_account_mode (driver, UM_LOCAL); + gis_driver_set_user_permissions (driver, user, NULL); + } + return NULL; + } + return g_object_new (GIS_TYPE_ACCOUNT_PAGE, "driver", driver, NULL); diff --git a/gnome-initial-setup/pages/install/gis-install-page.c b/gnome-initial-setup/pages/install/gis-install-page.c new file mode 100644 index 00000000..36ed7539 --- /dev/null +++ b/gnome-initial-setup/pages/install/gis-install-page.c @@ -0,0 +1,382 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * Copyright (C) 2023 Red Hat + * + * 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 of the + * License, 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, see . + */ + +/* Install page {{{1 */ + +#define PAGE_ID "install" + +#include "config.h" +#include "cc-common-language.h" +#include "gis-install-page.h" +#include "gis-pkexec.h" + +#include +#include +#include +#include +#include +#include + +#include + +#define SERVICE_NAME "gdm-password" +#define VENDOR_INSTALLER_GROUP "install" +#define VENDOR_APPLICATION_KEY "application" + +struct _GisInstallPagePrivate { + GtkWidget *try_button; + GtkWidget *install_button; + AdwStatusPage *status_page; + GDesktopAppInfo *installer; + + ActUser *user_account; + const gchar *user_password; +}; +typedef struct _GisInstallPagePrivate GisInstallPagePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (GisInstallPage, gis_install_page, GIS_TYPE_PAGE); + +static void +request_info_query (GisInstallPage *page, + GdmUserVerifier *user_verifier, + const char *question, + gboolean is_secret) +{ + /* TODO: pop up modal dialog */ + g_debug ("user verifier asks%s question: %s", + is_secret ? " secret" : "", + question); +} + +static void +on_info (GdmUserVerifier *user_verifier, + const char *service_name, + const char *info, + GisInstallPage *page) +{ + g_debug ("PAM module info: %s", info); +} + +static void +on_problem (GdmUserVerifier *user_verifier, + const char *service_name, + const char *problem, + GisInstallPage *page) +{ + g_warning ("PAM module error: %s", problem); +} + +static void +on_info_query (GdmUserVerifier *user_verifier, + const char *service_name, + const char *question, + GisInstallPage *page) +{ + request_info_query (page, user_verifier, question, FALSE); +} + +static void +on_secret_info_query (GdmUserVerifier *user_verifier, + const char *service_name, + const char *question, + GisInstallPage *page) +{ + GisInstallPagePrivate *priv = gis_install_page_get_instance_private (page); + gboolean should_send_password = priv->user_password != NULL; + + g_debug ("PAM module secret info query: %s", question); + if (should_send_password) { + g_debug ("sending password\n"); + gdm_user_verifier_call_answer_query (user_verifier, + service_name, + priv->user_password, + NULL, NULL, NULL); + priv->user_password = NULL; + } else { + request_info_query (page, user_verifier, question, TRUE); + } +} + +static void +on_session_opened (GdmGreeter *greeter, + const char *service_name, + GisInstallPage *page) +{ + gdm_greeter_call_start_session_when_ready_sync (greeter, service_name, + TRUE, NULL, NULL); +} + +static void +log_user_in (GisInstallPage *page) +{ + GisInstallPagePrivate *priv = gis_install_page_get_instance_private (page); + g_autoptr(GError) error = NULL; + GdmGreeter *greeter = NULL; + GdmUserVerifier *user_verifier = NULL; + + if (!gis_driver_get_gdm_objects (GIS_PAGE (page)->driver, + &greeter, &user_verifier)) { + g_warning ("No GDM connection; not initiating login"); + return; + } + + g_signal_connect (user_verifier, "info", + G_CALLBACK (on_info), page); + g_signal_connect (user_verifier, "problem", + G_CALLBACK (on_problem), page); + g_signal_connect (user_verifier, "info-query", + G_CALLBACK (on_info_query), page); + g_signal_connect (user_verifier, "secret-info-query", + G_CALLBACK (on_secret_info_query), page); + + g_signal_connect (greeter, "session-opened", + G_CALLBACK (on_session_opened), page); + + gdm_user_verifier_call_begin_verification_for_user_sync (user_verifier, + SERVICE_NAME, + act_user_get_user_name (priv->user_account), + NULL, &error); + + if (error != NULL) + g_warning ("Could not begin verification: %s", error->message); +} + +static void +on_try_button_clicked (GtkButton *button, + GisInstallPage *page) +{ + + g_autoptr (GError) error = NULL; + + if (!gis_driver_save_data (GIS_PAGE (page)->driver, &error)) + g_warning ("Error saving data: %s", error->message); + + gis_ensure_stamp_files (GIS_PAGE (page)->driver); + + gis_driver_hide_window (GIS_PAGE (page)->driver); + log_user_in (page); +} + +static void +on_installer_exited (GPid pid, + int exit_status, + gpointer user_data) +{ + g_autoptr (GError) error = NULL; + g_autoptr(GSubprocessLauncher) launcher = NULL; + g_autoptr(GSubprocess) subprocess = NULL; + gboolean started_to_reboot; + + launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_SEARCH_PATH_FROM_ENVP); + + g_subprocess_launcher_unsetenv (launcher, "SHELL"); + + subprocess = g_subprocess_launcher_spawn (launcher, &error, "systemctl", "reboot", NULL); + + if (subprocess == NULL) { + g_warning ("Failed to initiate reboot: %s\n", error->message); + return; + } + + started_to_reboot = g_subprocess_wait (subprocess, NULL, &error); + + if (!started_to_reboot) { + g_warning ("Failed to reboot: %s\n", error->message); + return; + } +} + +static void +on_installer_started (GDesktopAppInfo *appinfo, + GPid pid, + gpointer user_data) +{ + g_child_watch_add (pid, on_installer_exited, user_data); +} + +static void +run_installer (GisInstallPage *page) +{ + g_autoptr (GError) error = NULL; + GisInstallPagePrivate *priv = gis_install_page_get_instance_private (page); + gboolean installer_launched; + g_autoptr (GAppLaunchContext) launch_context = NULL; + g_autofree char *language = NULL; + + if (!gis_driver_save_data (GIS_PAGE (page)->driver, &error)) + g_warning ("Error saving data: %s", error->message); + + gis_ensure_stamp_files (GIS_PAGE (page)->driver); + + launch_context = g_app_launch_context_new (); + + g_app_launch_context_unsetenv (launch_context, "SHELL"); + + language = cc_common_language_get_current_language (); + + if (language != NULL) + g_app_launch_context_setenv (launch_context, "LANG", language); + + installer_launched = g_desktop_app_info_launch_uris_as_manager (priv->installer, + NULL, + launch_context, + G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_CHILD_INHERITS_STDERR | G_SPAWN_CHILD_INHERITS_STDOUT | G_SPAWN_SEARCH_PATH, + NULL, + NULL, + on_installer_started, + page, + &error); + + if (!installer_launched) + g_warning ("Could not launch installer: %s", error->message); +} + +static void +on_install_button_clicked (GtkButton *button, + GisInstallPage *page) +{ + gis_driver_hide_window (GIS_PAGE (page)->driver); + run_installer (page); +} + +static void +gis_install_page_shown (GisPage *page) +{ + GisInstallPage *install = GIS_INSTALL_PAGE (page); + GisInstallPagePrivate *priv = gis_install_page_get_instance_private (install); + g_autoptr(GError) local_error = NULL; + + gis_driver_get_user_permissions (GIS_PAGE (page)->driver, + &priv->user_account, + &priv->user_password); + + gtk_widget_grab_focus (priv->install_button); +} + +static void +update_distro_name (GisInstallPage *page) +{ + GisInstallPagePrivate *priv = gis_install_page_get_instance_private (page); + g_autofree char *text = NULL; + + text = g_strdup (adw_status_page_get_description (priv->status_page)); + gis_substitute_variables_in_text (&text, (GisVariableLookupFunc) g_get_os_info, NULL); + adw_status_page_set_description (priv->status_page, text); + g_clear_pointer (&text, g_free); + + text = g_strdup (gtk_button_get_label (GTK_BUTTON (priv->try_button))); + gis_substitute_variables_in_text (&text, (GisVariableLookupFunc) g_get_os_info, NULL); + gtk_button_set_label (GTK_BUTTON (priv->try_button), text); + g_clear_pointer (&text, g_free); +} + + +static void +apply_stylesheet (GisInstallPage *page) +{ + GisInstallPagePrivate *priv = gis_install_page_get_instance_private (page); + g_autoptr (GtkCssProvider) css_provider = gtk_css_provider_new(); + + gtk_widget_add_css_class (GTK_WIDGET (priv->status_page), "override-icon-size"); + gtk_widget_add_css_class (GTK_WIDGET (priv->status_page), "override-button-line-height"); + + gtk_css_provider_load_from_resource (css_provider, "/org/gnome/initial-setup/gis-install-page.css"); + + gtk_style_context_add_provider_for_display (gtk_widget_get_display (GTK_WIDGET (priv->status_page)), + GTK_STYLE_PROVIDER (css_provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); +} + +static gboolean +find_installer (GisInstallPage *page) +{ + GisInstallPagePrivate *priv = gis_install_page_get_instance_private (page); + g_autofree char *desktop_file = NULL; + + desktop_file = gis_driver_conf_get_string (GIS_PAGE (page)->driver, + VENDOR_INSTALLER_GROUP, + VENDOR_APPLICATION_KEY); + + if (!desktop_file) + return FALSE; + + priv->installer = g_desktop_app_info_new (desktop_file); + + return priv->installer != NULL; +} + +static void +gis_install_page_constructed (GObject *object) +{ + GisInstallPage *page = GIS_INSTALL_PAGE (object); + GisInstallPagePrivate *priv = gis_install_page_get_instance_private (page); + + G_OBJECT_CLASS (gis_install_page_parent_class)->constructed (object); + + if (!find_installer (page)) + gtk_widget_set_sensitive (priv->install_button, FALSE); + + apply_stylesheet (page); + update_distro_name (page); + g_signal_connect (priv->try_button, "clicked", G_CALLBACK (on_try_button_clicked), page); + g_signal_connect (priv->install_button, "clicked", G_CALLBACK (on_install_button_clicked), page); + + gis_page_set_complete (GIS_PAGE (page), TRUE); + + gtk_widget_set_visible (GTK_WIDGET (page), TRUE); +} + +static void +gis_install_page_locale_changed (GisPage *page) +{ + g_autofree char *title = g_strdup (_("Install ${PRETTY_NAME}")); + gis_substitute_variables_in_text (&title, (GisVariableLookupFunc) g_get_os_info, NULL); + gis_page_set_title (page, title); +} + +static void +gis_install_page_class_init (GisInstallPageClass *klass) +{ + GisPageClass *page_class = GIS_PAGE_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-install-page.ui"); + + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisInstallPage, try_button); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisInstallPage, install_button); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisInstallPage, status_page); + + page_class->page_id = PAGE_ID; + page_class->locale_changed = gis_install_page_locale_changed; + page_class->shown = gis_install_page_shown; + object_class->constructed = gis_install_page_constructed; +} + +static void +gis_install_page_init (GisInstallPage *page) +{ + gtk_widget_init_template (GTK_WIDGET (page)); +} + +GisPage * +gis_prepare_install_page (GisDriver *driver) +{ + return g_object_new (GIS_TYPE_INSTALL_PAGE, + "driver", driver, + NULL); +} diff --git a/gnome-initial-setup/pages/install/gis-install-page.css b/gnome-initial-setup/pages/install/gis-install-page.css new file mode 100644 index 00000000..f3583b33 --- /dev/null +++ b/gnome-initial-setup/pages/install/gis-install-page.css @@ -0,0 +1,11 @@ +.override-icon-size image { + -gtk-icon-size: 70px; +} + +.override-button-line-height button { + line-height: 1.75; +} + +.description { + line-height: 1.25; +} diff --git a/gnome-initial-setup/pages/install/gis-install-page.h b/gnome-initial-setup/pages/install/gis-install-page.h new file mode 100644 index 00000000..292427d8 --- /dev/null +++ b/gnome-initial-setup/pages/install/gis-install-page.h @@ -0,0 +1,52 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * Copyright (C) 2023 Red Hat + * + * 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 of the + * License, 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, see . + */ + +#ifndef __GIS_INSTALL_PAGE_H__ +#define __GIS_INSTALL_PAGE_H__ + +#include "gnome-initial-setup.h" + +G_BEGIN_DECLS + +#define GIS_TYPE_INSTALL_PAGE (gis_install_page_get_type ()) +#define GIS_INSTALL_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_INSTALL_PAGE, GisInstallPage)) +#define GIS_INSTALL_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_INSTALL_PAGE, GisInstallPageClass)) +#define GIS_IS_INSTALL_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_INSTALL_PAGE)) +#define GIS_IS_INSTALL_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_INSTALL_PAGE)) +#define GIS_INSTALL_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_INSTALL_PAGE, GisInstallPageClass)) + +typedef struct _GisInstallPage GisInstallPage; +typedef struct _GisInstallPageClass GisInstallPageClass; + +struct _GisInstallPage +{ + GisPage parent; +}; + +struct _GisInstallPageClass +{ + GisPageClass parent_class; +}; + +GType gis_install_page_get_type (void); + +GisPage *gis_prepare_install_page (GisDriver *driver); + +G_END_DECLS + +#endif /* __GIS_INSTALL_PAGE_H__ */ diff --git a/gnome-initial-setup/pages/install/gis-install-page.ui b/gnome-initial-setup/pages/install/gis-install-page.ui new file mode 100644 index 00000000..c9ed5c88 --- /dev/null +++ b/gnome-initial-setup/pages/install/gis-install-page.ui @@ -0,0 +1,51 @@ + + + + diff --git a/gnome-initial-setup/pages/install/install.gresource.xml b/gnome-initial-setup/pages/install/install.gresource.xml new file mode 100644 index 00000000..15391108 --- /dev/null +++ b/gnome-initial-setup/pages/install/install.gresource.xml @@ -0,0 +1,8 @@ + + + + gis-install-page.ui + gis-install-page.css + + + diff --git a/gnome-initial-setup/pages/install/meson.build b/gnome-initial-setup/pages/install/meson.build new file mode 100644 index 00000000..e5084e5e --- /dev/null +++ b/gnome-initial-setup/pages/install/meson.build @@ -0,0 +1,9 @@ +sources += gnome.compile_resources( + 'install-resources', + files('install.gresource.xml'), + c_name: 'install' +) +sources += files( + 'gis-install-page.c', + 'gis-install-page.h' +) diff --git a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c index ea9b9f35..debcd146 100644 --- a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c +++ b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c @@ -187,7 +187,7 @@ set_input_settings (GisKeyboardPage *self, g_variant_builder_init (&input_options_builder, G_VARIANT_TYPE ("as")); - is_system_mode = gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER; + is_system_mode = gis_driver_get_mode (GIS_PAGE (self)->driver) & GIS_DRIVER_MODE_SYSTEM; layouts_array = g_ptr_array_new (); variants_array = g_ptr_array_new (); @@ -301,7 +301,7 @@ update_input (GisKeyboardPage *self) set_input_settings (self, type, id); - if (gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER) { + if (gis_driver_get_mode (GIS_PAGE (self)->driver) & GIS_DRIVER_MODE_SYSTEM) { if (g_permission_get_allowed (priv->permission)) { set_localed_input (self); } else if (g_permission_get_can_acquire (priv->permission)) { @@ -363,7 +363,7 @@ preselect_input_source (GisKeyboardPage *self) got_input_source = gnome_get_input_source_from_locale (language, &type, &id); if (got_input_source) { - gboolean is_system_mode = gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER; + gboolean is_system_mode = gis_driver_get_mode (GIS_PAGE (self)->driver) & GIS_DRIVER_MODE_SYSTEM; if (is_system_mode || g_str_equal (type, "ibus")) { cc_input_chooser_set_input (CC_INPUT_CHOOSER (priv->input_chooser), id, @@ -384,7 +384,7 @@ update_page_complete (GisKeyboardPage *self) GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); gboolean complete; - if (gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER) { + if (gis_driver_get_mode (GIS_PAGE (self)->driver) & GIS_DRIVER_MODE_SYSTEM) { complete = (priv->localed != NULL && cc_input_chooser_get_input_id (CC_INPUT_CHOOSER (priv->input_chooser)) != NULL); } else { @@ -496,7 +496,7 @@ gis_keyboard_page_constructed (GObject *object) gnome_get_default_input_sources (priv->cancellable, on_got_default_sources, self); /* If we're in new user mode then we're manipulating system settings */ - if (gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER) + if (gis_driver_get_mode (GIS_PAGE (self)->driver) & GIS_DRIVER_MODE_SYSTEM) priv->permission = polkit_permission_new_sync ("org.freedesktop.locale1.set-keyboard", NULL, NULL, NULL); update_page_complete (self); diff --git a/gnome-initial-setup/pages/language/gis-language-page.c b/gnome-initial-setup/pages/language/gis-language-page.c index f8f82097..9c2004c0 100644 --- a/gnome-initial-setup/pages/language/gis-language-page.c +++ b/gnome-initial-setup/pages/language/gis-language-page.c @@ -144,7 +144,7 @@ language_changed (CcLanguageChooser *chooser, gis_driver_set_user_language (driver, priv->new_locale_id, TRUE); - if (gis_driver_get_mode (driver) == GIS_DRIVER_MODE_NEW_USER) { + if (gis_driver_get_mode (driver) & GIS_DRIVER_MODE_SYSTEM) { gis_page_set_complete (GIS_PAGE (page), FALSE); @@ -256,8 +256,7 @@ gis_language_page_constructed (GObject *object) g_signal_connect (priv->language_chooser, "confirm", G_CALLBACK (language_confirmed), page); - /* If we're in new user mode then we're manipulating system settings */ - if (gis_driver_get_mode (GIS_PAGE (page)->driver) == GIS_DRIVER_MODE_NEW_USER) + if (gis_driver_get_mode (GIS_PAGE (page)->driver) & GIS_DRIVER_MODE_SYSTEM) { priv->permission = polkit_permission_new_sync ("org.freedesktop.locale1.set-locale", NULL, NULL, NULL); diff --git a/gnome-initial-setup/pages/meson.build b/gnome-initial-setup/pages/meson.build index 8d327f69..ff8406ba 100644 --- a/gnome-initial-setup/pages/meson.build +++ b/gnome-initial-setup/pages/meson.build @@ -1,5 +1,6 @@ pages = [ 'account', + 'install', 'language', 'keyboard', 'network', diff --git a/gnome-initial-setup/pages/password/gis-password-page.c b/gnome-initial-setup/pages/password/gis-password-page.c index 9a41b50f..a466316d 100644 --- a/gnome-initial-setup/pages/password/gis-password-page.c +++ b/gnome-initial-setup/pages/password/gis-password-page.c @@ -507,6 +507,12 @@ gis_password_page_init (GisPasswordPage *page) GisPage * gis_prepare_password_page (GisDriver *driver) { + GisDriverMode driver_mode; + + driver_mode = gis_driver_get_mode (driver); + if (driver_mode == GIS_DRIVER_MODE_LIVE_USER && !gis_kernel_command_line_has_argument ((const char *[]) { "rd.live.overlay", NULL })) + return NULL; + return g_object_new (GIS_TYPE_PASSWORD_PAGE, "driver", driver, NULL); -- 2.44.0 From f700a87a96fe21a20da88df5b775d524f9a8ab85 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Wed, 16 Aug 2023 10:47:13 -0400 Subject: [PATCH 07/16] initial-setup: Don't show duplicated pages between modes It's possible a user just got asked questions in live mode before install that they'll then get asked again on first boot when the initial user is created. This commit tracks that information so it doesn't get reasked. --- data/meson.build | 6 +++ gnome-initial-setup/gis-driver.c | 3 ++ gnome-initial-setup/gnome-initial-setup.c | 65 +++++++++++++++++++++++ meson.build | 4 ++ 4 files changed, 78 insertions(+) diff --git a/data/meson.build b/data/meson.build index dfa064c5..8203e29e 100644 --- a/data/meson.build +++ b/data/meson.build @@ -130,3 +130,9 @@ configure_file( mode_dir = join_paths(data_dir, 'gnome-shell', 'modes') install_data('initial-setup.json', install_dir: mode_dir) + +install_subdir( + 'gnome-initial-setup', + install_dir : working_dir, + strip_directory : true +) diff --git a/gnome-initial-setup/gis-driver.c b/gnome-initial-setup/gis-driver.c index 1c22ba86..0934d2c5 100644 --- a/gnome-initial-setup/gis-driver.c +++ b/gnome-initial-setup/gis-driver.c @@ -106,6 +106,8 @@ struct _GisDriver { const gchar *vendor_conf_file_path; GKeyFile *vendor_conf_file; + + GKeyFile *state_file; }; G_DEFINE_TYPE (GisDriver, gis_driver, ADW_TYPE_APPLICATION) @@ -136,6 +138,7 @@ gis_driver_finalize (GObject *object) g_clear_object (&driver->user_account); g_clear_pointer (&driver->vendor_conf_file, g_key_file_free); + g_clear_pointer (&driver->state_file, g_key_file_free); g_clear_object (&driver->parent_account); g_free (driver->parent_password); diff --git a/gnome-initial-setup/gnome-initial-setup.c b/gnome-initial-setup/gnome-initial-setup.c index 02ca2808..4e2aa5af 100644 --- a/gnome-initial-setup/gnome-initial-setup.c +++ b/gnome-initial-setup/gnome-initial-setup.c @@ -48,6 +48,8 @@ #define VENDOR_EXISTING_USER_ONLY_KEY "existing_user_only" #define VENDOR_LIVE_USER_ONLY_KEY "live_user_only" +#define STATE_FILE GIS_WORKING_DIR "/state" + static gboolean force_existing_user_mode; static gboolean force_live_user_mode; @@ -119,6 +121,7 @@ pages_to_skip_from_file (GisDriver *driver) g_autofree char *mode_group = NULL; g_autoptr (GFlagsClass) driver_mode_flags_class = NULL; const GFlagsValue *driver_mode_flags = NULL; + g_autoptr (GError) error = NULL; /* This code will read the keyfile containing vendor customization options and * look for options under the "pages" group, and supports the following keys: @@ -187,6 +190,26 @@ pages_to_skip_from_file (GisDriver *driver) } } + /* Also, if this is a system mode, we check if the user already answered questions earlier in + * a different system mode, and skip those pages too. + */ + if (driver_mode & GIS_DRIVER_MODE_NEW_USER) { + g_autoptr(GKeyFile) state = NULL; + gboolean state_loaded; + + state = g_key_file_new (); + state_loaded = g_key_file_load_from_file (state, STATE_FILE, G_KEY_FILE_NONE, &error); + + if (state_loaded) { + skip_pages = g_key_file_get_string_list (state, VENDOR_PAGES_GROUP, VENDOR_SKIP_KEY, NULL, NULL); + + if (skip_pages != NULL) { + g_strv_builder_addv (builder, (const char **) skip_pages); + g_clear_pointer (&skip_pages, g_strfreev); + } + } + } + return g_strv_builder_end (builder); } @@ -396,6 +419,46 @@ main (int argc, char *argv[]) return status; } +static void +write_state (GisDriver *driver) +{ + g_autoptr(GKeyFile) state = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GStrvBuilder) builder = NULL; + g_auto(GStrv) visited_pages = NULL; + GisAssistant *assistant; + GList *pages, *node; + + assistant = gis_driver_get_assistant (driver); + + if (assistant == NULL) + return; + + state = g_key_file_new (); + + builder = g_strv_builder_new (); + + pages = gis_assistant_get_all_pages (assistant); + for (node = pages; node != NULL; node = node->next) { + GisPage *page = node->data; + g_strv_builder_add (builder, GIS_PAGE_GET_CLASS (page)->page_id); + } + + visited_pages = g_strv_builder_end (builder); + + g_key_file_set_string_list (state, + VENDOR_PAGES_GROUP, + VENDOR_SKIP_KEY, + (const char * const *) + visited_pages, + g_strv_length (visited_pages)); + + if (!g_key_file_save_to_file (state, STATE_FILE, &error)) { + g_warning ("Unable to save state to %s: %s", STATE_FILE, error->message); + return; + } +} + void gis_ensure_stamp_files (GisDriver *driver) { @@ -407,6 +470,8 @@ gis_ensure_stamp_files (GisDriver *driver) g_warning ("Unable to create %s: %s", done_file, error->message); g_clear_error (&error); } + + write_state (driver); } /** diff --git a/meson.build b/meson.build index 0f434482..5fdeced7 100644 --- a/meson.build +++ b/meson.build @@ -14,19 +14,23 @@ po_dir = join_paths(meson.current_source_dir(), 'po') bin_dir = join_paths(prefix, get_option('bindir')) data_dir = join_paths(prefix, get_option('datadir')) locale_dir = join_paths(prefix, get_option('localedir')) +localstate_dir = join_paths(prefix, get_option('localstatedir')) libexec_dir = join_paths(prefix, get_option('libexecdir')) sysconf_dir = join_paths(prefix, get_option('sysconfdir')) pkgdata_dir = join_paths(data_dir, meson.project_name()) pkgsysconf_dir = join_paths(sysconf_dir, meson.project_name()) +working_dir = join_paths(localstate_dir, 'lib', 'gnome-initial-setup') conf = configuration_data() conf.set_quoted('GETTEXT_PACKAGE', meson.project_name()) conf.set_quoted('GNOMELOCALEDIR', locale_dir) conf.set_quoted('PKGDATADIR', pkgdata_dir) conf.set_quoted('DATADIR', data_dir) +conf.set_quoted('LOCALSTATEDIR', localstate_dir) conf.set_quoted('PKGSYSCONFDIR', pkgsysconf_dir) conf.set_quoted('SYSCONFDIR', sysconf_dir) conf.set_quoted('LIBEXECDIR', libexec_dir) +conf.set_quoted('GIS_WORKING_DIR', working_dir) conf.set('SECRET_API_SUBJECT_TO_CHANGE', true) conf.set_quoted('G_LOG_DOMAIN', 'InitialSetup') conf.set('G_LOG_USE_STRUCTURED', true) -- 2.44.0 From 4bd3eb002d1dcded6efe647c108f76ccd0acd673 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Sun, 13 Aug 2023 16:33:49 -0400 Subject: [PATCH 08/16] polkit: Add fedora specfic rules We should probably add some way to check vendor.conf for the policy updates instead of a hardcoded list. --- data/20-gnome-initial-setup.rules.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/20-gnome-initial-setup.rules.in b/data/20-gnome-initial-setup.rules.in index 881efde9..f5b7d981 100644 --- a/data/20-gnome-initial-setup.rules.in +++ b/data/20-gnome-initial-setup.rules.in @@ -17,7 +17,8 @@ polkit.addRule(function(action, subject) { action.id.indexOf('org.freedesktop.realmd.') === 0 || action.id.indexOf('com.endlessm.ParentalControls.') === 0 || action.id.indexOf('org.fedoraproject.thirdparty.') === 0 || - action.id.indexOf('org.freedesktop.login1.reboot') === 0); + action.id.indexOf('org.freedesktop.login1.reboot') === 0 || + action.id.indexOf('org.fedoraproject.pkexec.liveinst') === 0); if (actionMatches) { if (subject.local) -- 2.44.0 From cae816eb42216cf1c2370fbc88aaba1b70579931 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Tue, 22 Aug 2023 13:51:40 -0400 Subject: [PATCH 09/16] gnome-initial-setup: Read /etc/sysconfig/anaconda Just as /var/lib/gnome-initial-setup/state may show pages the user has already answered, on Fedora, /etc/sysconfig/anaconda shows pages the user has already answered via Anaconda. This commit skips those from the --new-user mode as well. --- gnome-initial-setup/gnome-initial-setup.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/gnome-initial-setup/gnome-initial-setup.c b/gnome-initial-setup/gnome-initial-setup.c index 4e2aa5af..2de3ecab 100644 --- a/gnome-initial-setup/gnome-initial-setup.c +++ b/gnome-initial-setup/gnome-initial-setup.c @@ -196,6 +196,8 @@ pages_to_skip_from_file (GisDriver *driver) if (driver_mode & GIS_DRIVER_MODE_NEW_USER) { g_autoptr(GKeyFile) state = NULL; gboolean state_loaded; + g_autoptr(GKeyFile) anaconda = NULL; + gboolean anaconda_loaded; state = g_key_file_new (); state_loaded = g_key_file_load_from_file (state, STATE_FILE, G_KEY_FILE_NONE, &error); @@ -208,6 +210,27 @@ pages_to_skip_from_file (GisDriver *driver) g_clear_pointer (&skip_pages, g_strfreev); } } + + anaconda = g_key_file_new (); + anaconda_loaded = g_key_file_load_from_file (anaconda, "/etc/sysconfig/anaconda", G_KEY_FILE_NONE, NULL); + + if (anaconda_loaded) { + struct { + const char *spoke_name; + const char *page_name; + } spoke_page_map[] = { + { "WelcomeLanguageSpoke", "language" }, + { "DatetimeSpoke", "timezone" }, + { "KeyboardSpoke", "keyboard" }, + { NULL, NULL } + }; + size_t i; + + for (i = 0; spoke_page_map[i].spoke_name != NULL; i++) { + if (g_key_file_get_boolean (anaconda, spoke_page_map[i].spoke_name, "visited", NULL)) + g_strv_builder_add (builder, spoke_page_map[i].page_name); + } + } } return g_strv_builder_end (builder); -- 2.44.0 From da764a5d6b27d3a0a90597a55dc544b6af0deb87 Mon Sep 17 00:00:00 2001 From: Michael Catanzaro Date: Wed, 17 Jan 2024 12:29:54 -0600 Subject: [PATCH 10/16] Fix criticals in set_localed_input() If the default input sources do not match the current input sources, these won't be set in set_input_settings() and we shouldn't try to use them. Fixes: (gnome-initial-setup:41149): GLib-CRITICAL **: 10:09:25.599: g_strjoinv: assertion 'str_array != NULL' failed --- gnome-initial-setup/pages/keyboard/gis-keyboard-page.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c index debcd146..fa4c0ea9 100644 --- a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c +++ b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c @@ -253,12 +253,12 @@ set_localed_input (GisKeyboardPage *self) g_autofree char *variants = NULL; g_autofree char *options = NULL; - if (!priv->localed) + if (!priv->localed || !priv->system_layouts || !priv->system_variants) return; layouts = g_strjoinv (",", priv->system_layouts); variants = g_strjoinv (",", priv->system_variants); - options = g_strjoinv (",", priv->system_options); + options = priv->system_options ? g_strjoinv (",", priv->system_options) : g_strdup (""); g_dbus_proxy_call (priv->localed, "SetX11Keyboard", -- 2.44.0 From c96fcb4a66a80d988417f1c01231b1c3c26219fc Mon Sep 17 00:00:00 2001 From: Michael Catanzaro Date: Fri, 19 Jan 2024 15:49:15 -0600 Subject: [PATCH 11/16] assistant: assert next page exists when switching to next page If there is no next page, then we should crash nicely on this assert rather than not so nicely. --- gnome-initial-setup/gis-assistant.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gnome-initial-setup/gis-assistant.c b/gnome-initial-setup/gis-assistant.c index 8a7fc52b..c1af2943 100644 --- a/gnome-initial-setup/gis-assistant.c +++ b/gnome-initial-setup/gis-assistant.c @@ -111,7 +111,9 @@ find_next_page (GisAssistant *self, static void switch_to_next_page (GisAssistant *assistant) { - switch_to (assistant, find_next_page (assistant, assistant->current_page)); + GisPage *next = find_next_page (assistant, assistant->current_page); + g_assert (next != NULL); + switch_to (assistant, next); } static void -- 2.44.0 From 1c593f6324d5f81348d0c2c2888d393bbeadd3b4 Mon Sep 17 00:00:00 2001 From: Michael Catanzaro Date: Fri, 19 Jan 2024 15:50:33 -0600 Subject: [PATCH 12/16] summary: don't crash if there is no user account to create --- gnome-initial-setup/pages/summary/gis-summary-page.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gnome-initial-setup/pages/summary/gis-summary-page.c b/gnome-initial-setup/pages/summary/gis-summary-page.c index 9eaf90dd..f359673f 100644 --- a/gnome-initial-setup/pages/summary/gis-summary-page.c +++ b/gnome-initial-setup/pages/summary/gis-summary-page.c @@ -151,6 +151,11 @@ log_user_in (GisSummaryPage *page) return; } + if (!priv->user_account) { + g_info ("No new user account (was the account page skipped?); not initiating login"); + return; + } + g_signal_connect (user_verifier, "info", G_CALLBACK (on_info), page); g_signal_connect (user_verifier, "problem", -- 2.44.0 From d7eb5b4b446412ecd06148908242b6ccc0826ae2 Mon Sep 17 00:00:00 2001 From: Michael Catanzaro Date: Fri, 19 Jan 2024 15:52:03 -0600 Subject: [PATCH 13/16] Don't show warnings when failing to connect to gdm This is an expected condition. We need to log it since it might be needed when debugging, but a warning is overkill. --- gnome-initial-setup/gis-driver.c | 3 ++- gnome-initial-setup/pages/install/gis-install-page.c | 2 +- gnome-initial-setup/pages/summary/gis-summary-page.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/gnome-initial-setup/gis-driver.c b/gnome-initial-setup/gis-driver.c index 0934d2c5..c3a25c6f 100644 --- a/gnome-initial-setup/gis-driver.c +++ b/gnome-initial-setup/gis-driver.c @@ -846,7 +846,8 @@ connect_to_gdm (GisDriver *driver) driver->user_verifier = gdm_client_get_user_verifier_sync (driver->client, NULL, &error); if (error != NULL) { - g_warning ("Failed to open connection to GDM: %s", error->message); + /* Not a warning because this is expected if running in a user session */ + g_message ("Failed to open connection to GDM: %s", error->message); g_clear_object (&driver->user_verifier); g_clear_object (&driver->greeter); g_clear_object (&driver->client); diff --git a/gnome-initial-setup/pages/install/gis-install-page.c b/gnome-initial-setup/pages/install/gis-install-page.c index 36ed7539..850c8241 100644 --- a/gnome-initial-setup/pages/install/gis-install-page.c +++ b/gnome-initial-setup/pages/install/gis-install-page.c @@ -131,7 +131,7 @@ log_user_in (GisInstallPage *page) if (!gis_driver_get_gdm_objects (GIS_PAGE (page)->driver, &greeter, &user_verifier)) { - g_warning ("No GDM connection; not initiating login"); + g_info ("No GDM connection; not initiating login"); return; } diff --git a/gnome-initial-setup/pages/summary/gis-summary-page.c b/gnome-initial-setup/pages/summary/gis-summary-page.c index f359673f..367c5285 100644 --- a/gnome-initial-setup/pages/summary/gis-summary-page.c +++ b/gnome-initial-setup/pages/summary/gis-summary-page.c @@ -147,7 +147,7 @@ log_user_in (GisSummaryPage *page) if (!gis_driver_get_gdm_objects (GIS_PAGE (page)->driver, &greeter, &user_verifier)) { - g_warning ("No GDM connection; not initiating login"); + g_info ("No GDM connection; not initiating login"); return; } -- 2.44.0 From 96ac17aece664173830b467fd2007b421b141004 Mon Sep 17 00:00:00 2001 From: Michael Catanzaro Date: Fri, 19 Jan 2024 15:58:09 -0600 Subject: [PATCH 14/16] Never skip the summary page if available This avoids a crash when finishing the welcome page if there are no other pages available. --- gnome-initial-setup/gnome-initial-setup.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gnome-initial-setup/gnome-initial-setup.c b/gnome-initial-setup/gnome-initial-setup.c index 2de3ecab..54bca86f 100644 --- a/gnome-initial-setup/gnome-initial-setup.c +++ b/gnome-initial-setup/gnome-initial-setup.c @@ -99,6 +99,16 @@ should_skip_page (const gchar *page_id, if (strcmp (page_id, "welcome") == 0) return !should_skip_page ("language", skip_pages); + /* We have to make sure the welcome page is not the last page because it + * unconditionally attempts to load the next page. So, always show the + * summary page. + * + * This doesn't work in existing user mode, but that's OK because we don't + * skip arbitrary previously-visited pages when in existing user mode. + */ + if (strcmp (page_id, "summary") == 0) + return FALSE; + /* check through our skip pages list for pages we don't want */ if (skip_pages) { while (skip_pages[i]) { -- 2.44.0 From ec202e90d00738e15a9cd5826545994fc20dcd3d Mon Sep 17 00:00:00 2001 From: Michael Catanzaro Date: Mon, 22 Jan 2024 14:07:15 -0600 Subject: [PATCH 15/16] Fix failure to log into user account Currently gis_driver_get_user_permissions() is guaranteed to fail because we call it after gis_driver_save_data(), which creates the user account. We need to call it later at the right point. Also, let's own the user_password that we store on the summary and install page. Even if it's not expected to change, it doesn't seem very safe to rely on the GisDriver not deleting it. --- .../pages/install/gis-install-page.c | 41 ++++++++++++++---- .../pages/summary/gis-summary-page.c | 42 ++++++++++++------- 2 files changed, 59 insertions(+), 24 deletions(-) diff --git a/gnome-initial-setup/pages/install/gis-install-page.c b/gnome-initial-setup/pages/install/gis-install-page.c index 850c8241..be4d6968 100644 --- a/gnome-initial-setup/pages/install/gis-install-page.c +++ b/gnome-initial-setup/pages/install/gis-install-page.c @@ -43,9 +43,7 @@ struct _GisInstallPagePrivate { GtkWidget *install_button; AdwStatusPage *status_page; GDesktopAppInfo *installer; - - ActUser *user_account; - const gchar *user_password; + char *user_password; }; typedef struct _GisInstallPagePrivate GisInstallPagePrivate; @@ -128,6 +126,24 @@ log_user_in (GisInstallPage *page) g_autoptr(GError) error = NULL; GdmGreeter *greeter = NULL; GdmUserVerifier *user_verifier = NULL; + ActUser *user_account = NULL; + const char *user_password = NULL; + + gis_driver_get_user_permissions (GIS_PAGE (page)->driver, + &user_account, + &user_password); + if (user_account == NULL) { + g_info ("No new user account (was the account page skipped?); not initiating login"); + return; + } + g_assert (priv->user_password == NULL); + priv->user_password = g_strdup (user_password); + + if (!gis_driver_get_gdm_objects (GIS_PAGE (page)->driver, + &greeter, &user_verifier)) { + g_info ("No GDM connection; not initiating login"); + return; + } if (!gis_driver_get_gdm_objects (GIS_PAGE (page)->driver, &greeter, &user_verifier)) { @@ -149,7 +165,7 @@ log_user_in (GisInstallPage *page) gdm_user_verifier_call_begin_verification_for_user_sync (user_verifier, SERVICE_NAME, - act_user_get_user_name (priv->user_account), + act_user_get_user_name (user_account), NULL, &error); if (error != NULL) @@ -259,11 +275,6 @@ gis_install_page_shown (GisPage *page) { GisInstallPage *install = GIS_INSTALL_PAGE (page); GisInstallPagePrivate *priv = gis_install_page_get_instance_private (install); - g_autoptr(GError) local_error = NULL; - - gis_driver_get_user_permissions (GIS_PAGE (page)->driver, - &priv->user_account, - &priv->user_password); gtk_widget_grab_focus (priv->install_button); } @@ -341,6 +352,17 @@ gis_install_page_constructed (GObject *object) gtk_widget_set_visible (GTK_WIDGET (page), TRUE); } +static void +gis_install_page_finalize (GObject *object) +{ + GisInstallPage *page = GIS_INSTALL_PAGE (object); + GisInstallPagePrivate *priv = gis_install_page_get_instance_private (page); + + g_clear_pointer (&priv->user_password, g_free); + + G_OBJECT_CLASS (gis_install_page_parent_class)->finalize (object); +} + static void gis_install_page_locale_changed (GisPage *page) { @@ -365,6 +387,7 @@ gis_install_page_class_init (GisInstallPageClass *klass) page_class->locale_changed = gis_install_page_locale_changed; page_class->shown = gis_install_page_shown; object_class->constructed = gis_install_page_constructed; + object_class->finalize = gis_install_page_finalize; } static void diff --git a/gnome-initial-setup/pages/summary/gis-summary-page.c b/gnome-initial-setup/pages/summary/gis-summary-page.c index 367c5285..37881442 100644 --- a/gnome-initial-setup/pages/summary/gis-summary-page.c +++ b/gnome-initial-setup/pages/summary/gis-summary-page.c @@ -40,9 +40,7 @@ struct _GisSummaryPagePrivate { GtkWidget *start_button; AdwStatusPage *status_page; - - ActUser *user_account; - const gchar *user_password; + char *user_password; }; typedef struct _GisSummaryPagePrivate GisSummaryPagePrivate; @@ -144,15 +142,22 @@ log_user_in (GisSummaryPage *page) g_autoptr(GError) error = NULL; GdmGreeter *greeter = NULL; GdmUserVerifier *user_verifier = NULL; + ActUser *user_account = NULL; + const char *user_password = NULL; - if (!gis_driver_get_gdm_objects (GIS_PAGE (page)->driver, - &greeter, &user_verifier)) { - g_info ("No GDM connection; not initiating login"); + gis_driver_get_user_permissions (GIS_PAGE (page)->driver, + &user_account, + &user_password); + if (user_account == NULL) { + g_info ("No new user account (was the account page skipped?); not initiating login"); return; } + g_assert (priv->user_password == NULL); + priv->user_password = g_strdup (user_password); - if (!priv->user_account) { - g_info ("No new user account (was the account page skipped?); not initiating login"); + if (!gis_driver_get_gdm_objects (GIS_PAGE (page)->driver, + &greeter, &user_verifier)) { + g_info ("No GDM connection; not initiating login"); return; } @@ -171,11 +176,11 @@ log_user_in (GisSummaryPage *page) /* We are in NEW_USER mode and we want to make it possible for third * parties to find out which user ID we created. */ - add_uid_file (act_user_get_uid (priv->user_account)); + add_uid_file (act_user_get_uid (user_account)); gdm_user_verifier_call_begin_verification_for_user_sync (user_verifier, SERVICE_NAME, - act_user_get_user_name (priv->user_account), + act_user_get_user_name (user_account), NULL, &error); if (error != NULL) @@ -214,11 +219,6 @@ gis_summary_page_shown (GisPage *page) { GisSummaryPage *summary = GIS_SUMMARY_PAGE (page); GisSummaryPagePrivate *priv = gis_summary_page_get_instance_private (summary); - g_autoptr(GError) local_error = NULL; - - gis_driver_get_user_permissions (GIS_PAGE (page)->driver, - &priv->user_account, - &priv->user_password); gtk_widget_grab_focus (priv->start_button); } @@ -264,6 +264,17 @@ gis_summary_page_constructed (GObject *object) gtk_widget_set_visible (GTK_WIDGET (page), TRUE); } +static void +gis_summary_page_finalize (GObject *object) +{ + GisSummaryPage *page = GIS_SUMMARY_PAGE (object); + GisSummaryPagePrivate *priv = gis_summary_page_get_instance_private (page); + + g_clear_pointer (&priv->user_password, g_free); + + G_OBJECT_CLASS (gis_summary_page_parent_class)->finalize (object); +} + static void gis_summary_page_locale_changed (GisPage *page) { @@ -286,6 +297,7 @@ gis_summary_page_class_init (GisSummaryPageClass *klass) page_class->locale_changed = gis_summary_page_locale_changed; page_class->shown = gis_summary_page_shown; object_class->constructed = gis_summary_page_constructed; + object_class->finalize = gis_summary_page_finalize; } static void -- 2.44.0 From be7626a013d8838f393ff20d760c4076a164c233 Mon Sep 17 00:00:00 2001 From: Michael Catanzaro Date: Thu, 25 Jan 2024 17:09:29 -0600 Subject: [PATCH 16/16] Update POTFILES.in Related: #209 --- po/POTFILES.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/po/POTFILES.in b/po/POTFILES.in index 10536d14..c235b379 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -16,6 +16,8 @@ gnome-initial-setup/pages/account/gis-account-page.ui gnome-initial-setup/pages/account/um-photo-dialog.c gnome-initial-setup/pages/account/um-realm-manager.c gnome-initial-setup/pages/account/um-utils.c +gnome-initial-setup/pages/install/gis-install-page.c +gnome-initial-setup/pages/install/gis-install-page.ui gnome-initial-setup/pages/keyboard/cc-input-chooser.c gnome-initial-setup/pages/keyboard/gis-keyboard-page.c gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui -- 2.44.0