[gnome-initial-setup/wip/pwithnall/misc-fixes: 28/70] factory test mode: implement the factory dialog




commit 4d7b47c4abc3ab3e23f4987bd1ea4a01b8d7ef97
Author: Mario Sanchez Prada <mario endlessm com>
Date:   Fri Aug 18 13:06:11 2017 +0200

    factory test mode: implement the factory dialog
    
    This adds a factory test dialog which is accessed by pressing Ctrl+F
    from the language page (which is the first page shown in the FBE.)
    The dialog also shows the image name stamp, which includes the
    personality name at the end.
    
    (Rebase 3.38: Fix minor rebase conflicts and drop barcode support and
    use of zint as per https://phabricator.endlessm.com/T20124. Refactor
    `get_software_version()` to use `g_get_os_info()` for simplicity.)
    
    https://phabricator.endlessm.com/T15834
    https://phabricator.endlessm.com/T17082
    https://phabricator.endlessm.com/T20124
    https://phabricator.endlessm.com/T23690

 gnome-initial-setup/gis-page-util.c                | 284 +++++++++++++++++++++
 gnome-initial-setup/gis-page-util.gresource.xml    |   6 +
 gnome-initial-setup/gis-page-util.h                |   4 +
 gnome-initial-setup/gis-page-util.ui               | 174 +++++++++++++
 gnome-initial-setup/meson.build                    |   6 +
 .../pages/language/gis-language-page.c             |  20 ++
 meson.build                                        |   3 +
 po/POTFILES.in                                     |   2 +
 8 files changed, 499 insertions(+)
---
diff --git a/gnome-initial-setup/gis-page-util.c b/gnome-initial-setup/gis-page-util.c
index 18db3eb9..2246b056 100644
--- a/gnome-initial-setup/gis-page-util.c
+++ b/gnome-initial-setup/gis-page-util.c
@@ -28,10 +28,107 @@
 #include <errno.h>
 #include <gio/gio.h>
 #include <glib.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include <polkit/polkit.h>
 #include <sys/types.h>
 #include <sys/xattr.h>
 
 #define EOS_IMAGE_VERSION_XATTR "user.eos-image-version"
+#define SERIAL_VERSION_FILE "/sys/devices/virtual/dmi/id/product_uuid"
+#define DT_COMPATIBLE_FILE  "/proc/device-tree/compatible"
+#define SYSROOT_MOUNT       "/sysroot"
+#define SD_CARD_MOUNT       LOCALSTATEDIR "/endless-extra"
+
+static GtkBuilder *
+get_modals_builder (void)
+{
+  GtkBuilder *builder = NULL;
+  const gchar *resource_path = "/org/gnome/initial-setup/gis-page-util.ui";
+  g_autoptr(GError) error = NULL;
+
+  builder = gtk_builder_new ();
+  gtk_builder_add_from_resource (builder, resource_path, &error);
+
+  if (error != NULL) {
+    g_warning ("Error while loading %s: %s", resource_path, error->message);
+    g_object_unref (builder);
+    return NULL;
+  }
+
+  return builder;
+}
+
+static gboolean
+get_serial_version (gchar **display_serial)
+{
+  GError *error = NULL;
+  gchar *serial = NULL;
+  gchar **split;
+  GString *display;
+  gchar *display_str;
+  gint idx;
+
+  g_file_get_contents (SERIAL_VERSION_FILE, &serial, NULL, &error);
+
+  if (error) {
+    g_warning ("Error when reading %s: %s", SERIAL_VERSION_FILE, error->message);
+    g_error_free (error);
+    return FALSE;
+  }
+
+  /* Drop hyphens */
+  split = g_strsplit (serial, "-", -1);
+  g_free (serial);
+  serial = g_strstrip (g_strjoinv (NULL, split));
+  g_strfreev (split);
+
+  display = g_string_new (NULL);
+
+  /* Each byte is encoded here as two characters; valid bytes are
+   * followed by markers (same length), and we need to get the first 6
+   * valid bytes only.
+   * So, 6 * 4 = 24 below...
+   */
+  for (idx = 0; idx < MIN (strlen (serial), 24); idx++) {
+    /* Discard markers */
+    if (idx % 4 > 1)
+      continue;
+
+    g_string_append_c (display, serial[idx]);
+
+    /* Space out valid bytes in the display version of the string */
+    if (idx % 4 == 1)
+      g_string_append_c (display, ' ');
+  }
+
+  g_free (serial);
+
+  display_str = g_strstrip (g_string_free (display, FALSE));
+
+  if (display_serial)
+    *display_serial = display_str;
+  else
+    g_free (display_str);
+
+  return TRUE;
+}
+
+static gboolean
+get_have_sdcard (void)
+{
+  GDir *dir;
+  gboolean has_bundles;
+
+  dir = g_dir_open (SD_CARD_MOUNT, 0, NULL);
+  if (!dir)
+    return FALSE;
+
+  has_bundles = (g_dir_read_name (dir) != NULL);
+  g_dir_close (dir);
+
+  return has_bundles;
+}
 
 gchar *
 gis_page_util_get_image_version (const gchar *path,
@@ -65,3 +162,190 @@ gis_page_util_get_image_version (const gchar *path,
       return NULL;
     }
 }
+
+static gchar *
+get_software_version (void)
+{
+  g_autoptr(GString) software_version = NULL;
+  g_autofree gchar *name = NULL;
+  g_autofree gchar *version = NULL;
+
+  software_version = g_string_new ("");
+
+  name = g_get_os_info (G_OS_INFO_KEY_NAME);
+  version = g_get_os_info (G_OS_INFO_KEY_VERSION);
+
+  if (name)
+    g_string_append (software_version, name);
+  if (name && version)
+    g_string_append_c (software_version, ' ');
+  if (version)
+    g_string_append (software_version, version);
+
+  return g_string_free (g_steal_pointer (&software_version), FALSE);
+}
+
+static gchar *
+get_product_id (void)
+{
+  GError *error = NULL;
+  gchar *compatible = NULL;
+
+  g_file_get_contents (DT_COMPATIBLE_FILE, &compatible, NULL, &error);
+
+  if (error) {
+    g_error_free (error);
+    return NULL;
+  }
+
+  return compatible;
+}
+
+static void
+system_poweroff (gpointer data)
+{
+  GDBusConnection *bus;
+  GError *error = NULL;
+  GPermission *permission;
+
+  permission = polkit_permission_new_sync ("org.freedesktop.login1.power-off", NULL, NULL, &error);
+  if (error) {
+    g_warning ("Failed getting permission to power off: %s", error->message);
+    g_error_free (error);
+    return;
+  }
+
+  if (!g_permission_get_allowed (permission)) {
+    g_warning ("Not allowed to power off");
+    g_object_unref (permission);
+    return;
+  }
+
+  g_object_unref (permission);
+
+  bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+  if (error) {
+    g_warning ("Failed to get system bus: %s", error->message);
+    g_error_free (error);
+    return;
+  }
+
+  g_dbus_connection_call (bus,
+                          "org.freedesktop.login1",
+                          "/org/freedesktop/login1",
+                          "org.freedesktop.login1.Manager",
+                          "PowerOff",
+                          g_variant_new ("(b)", FALSE),
+                          NULL, 0, G_MAXINT, NULL, NULL, NULL);
+
+  g_object_unref (bus);
+}
+
+static void
+system_testmode (GtkButton *button, gpointer data)
+{
+  GtkWindow *factory_dialog = GTK_WINDOW (data);
+  GError *error = NULL;
+
+  /* Test mode can only be initialized once */
+  gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);
+
+  if (!gis_pkexec (LIBEXECDIR "/eos-test-mode", NULL, NULL, &error)) {
+    GtkWidget *dialog;
+
+    g_warning ("%s", error->message);
+    dialog = gtk_message_dialog_new (factory_dialog,
+                                     GTK_DIALOG_DESTROY_WITH_PARENT,
+                                     GTK_MESSAGE_ERROR,
+                                     GTK_BUTTONS_CLOSE,
+                                     "%s",
+                                     error->message);
+    gtk_dialog_run (GTK_DIALOG (dialog));
+    gtk_widget_destroy (dialog);
+    g_error_free (error);
+  }
+
+  gtk_window_close (factory_dialog);
+}
+
+void
+gis_page_util_show_factory_dialog (GisPage *page)
+{
+  g_autoptr(GtkBuilder) builder = NULL;
+  GtkButton *poweroff_button;
+  GtkButton *testmode_button;
+  GtkDialog *factory_dialog;
+  GtkLabel *image_version_label;
+  GtkLabel *sdcard_label;
+  GtkLabel *serial_label;
+  GtkLabel *product_id_label;
+  GtkLabel *version_label;
+  gboolean have_serial;
+  g_autofree gchar *display_serial = NULL;
+  g_autofree gchar *version = NULL;
+  g_autofree gchar *image_version = NULL;
+  g_autofree gchar *sd_version = NULL;
+  g_autofree gchar *image_version_text = NULL;
+  g_autofree gchar *sd_text = NULL;
+  g_autofree gchar *product_id_text = NULL;
+
+  builder = get_modals_builder ();
+  if (builder == NULL) {
+    g_warning ("Can't get private builder object for factory mode!");
+    return;
+  }
+
+  factory_dialog = (GtkDialog *)gtk_builder_get_object (builder, "factory-dialog");
+  version_label = (GtkLabel *)gtk_builder_get_object (builder, "software-version");
+  product_id_label = (GtkLabel *)gtk_builder_get_object (builder, "product-id");
+  image_version_label = (GtkLabel *)gtk_builder_get_object (builder, "image-version");
+  sdcard_label = (GtkLabel *)gtk_builder_get_object (builder, "sd-card");
+  serial_label = (GtkLabel *)gtk_builder_get_object (builder, "serial-text");
+  poweroff_button = (GtkButton *)gtk_builder_get_object (builder, "poweroff-button");
+  testmode_button = (GtkButton *)gtk_builder_get_object (builder, "testmode-button");
+
+  version = get_software_version ();
+  gtk_label_set_text (version_label, version);
+
+  product_id_text = get_product_id ();
+  if (product_id_text) {
+    gtk_label_set_text (product_id_label, product_id_text);
+    gtk_widget_set_visible (GTK_WIDGET (product_id_label), TRUE);
+  }
+
+  have_serial = get_serial_version (&display_serial);
+
+  if (have_serial) {
+    gtk_label_set_text (serial_label, display_serial);
+  } else {
+    gtk_widget_set_visible (GTK_WIDGET (serial_label), FALSE);
+  }
+
+  image_version = gis_page_util_get_image_version (SYSROOT_MOUNT, NULL);
+  if (!image_version)
+    image_version = g_strdup (_("Unknown"));
+
+  image_version_text = g_strdup_printf (_("Image: %s"), image_version);
+  gtk_label_set_text (image_version_label, image_version_text);
+
+  if (get_have_sdcard ())
+    sd_version = gis_page_util_get_image_version (SD_CARD_MOUNT, NULL);
+
+  if (!sd_version)
+    sd_version = g_strdup (_("Disabled"));
+
+  sd_text = g_strdup_printf (_("SD Card: %s"), sd_version);
+  gtk_label_set_text (sdcard_label, sd_text);
+
+  g_signal_connect_swapped (poweroff_button, "clicked",
+                            G_CALLBACK (system_poweroff), NULL);
+  g_signal_connect (testmode_button, "clicked",
+                    G_CALLBACK (system_testmode), factory_dialog);
+
+  gtk_window_set_transient_for (GTK_WINDOW (factory_dialog),
+                                GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (page))));
+  gtk_window_set_modal (GTK_WINDOW (factory_dialog), TRUE);
+  gtk_window_present (GTK_WINDOW (factory_dialog));
+  g_signal_connect (factory_dialog, "delete-event",
+                    G_CALLBACK (gtk_widget_hide_on_delete), NULL);
+}
diff --git a/gnome-initial-setup/gis-page-util.gresource.xml b/gnome-initial-setup/gis-page-util.gresource.xml
new file mode 100644
index 00000000..5f5d2ee0
--- /dev/null
+++ b/gnome-initial-setup/gis-page-util.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/initial-setup">
+    <file preprocess="xml-stripblanks">gis-page-util.ui</file>
+  </gresource>
+</gresources>
diff --git a/gnome-initial-setup/gis-page-util.h b/gnome-initial-setup/gis-page-util.h
index a7d30dfc..48b28b18 100644
--- a/gnome-initial-setup/gis-page-util.h
+++ b/gnome-initial-setup/gis-page-util.h
@@ -26,8 +26,12 @@
 
 #include <glib.h>
 
+#include "gis-page.h"
+
 G_BEGIN_DECLS
 
+void gis_page_util_show_factory_dialog (GisPage *page);
+
 gchar *gis_page_util_get_image_version (const gchar *path,
                                         GError     **error);
 
diff --git a/gnome-initial-setup/gis-page-util.ui b/gnome-initial-setup/gis-page-util.ui
new file mode 100644
index 00000000..9dd0ad25
--- /dev/null
+++ b/gnome-initial-setup/gis-page-util.ui
@@ -0,0 +1,174 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.16.1 -->
+<interface>
+  <requires lib="gtk+" version="3.0"/>
+  <object class="GtkDialog" id="factory-dialog">
+    <property name="can_focus">False</property>
+    <property name="type_hint">dialog</property>
+    <property name="use_header_bar">1</property>
+    <property name="title"></property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox1">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child>
+          <object class="GtkButtonBox" id="dialog-action_area1">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkButton" id="testmode-button">
+                <property name="visible">True</property>
+                <property name="can_default">True</property>
+                <property name="can_focus">True</property>
+                <child>
+                  <object class="GtkLabel" id="testmode-label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Test Mode</property>
+                    <attributes>
+                      <attribute name="font-desc" value="default 16"/>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="poweroff-button">
+                <property name="visible">True</property>
+                <property name="can_default">True</property>
+                <property name="can_focus">True</property>
+                <property name="has_focus">True</property>
+                <property name="has_default">True</property>
+                <property name="receives_default">True</property>
+                <child>
+                  <object class="GtkLabel" id="label1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Power Off</property>
+                    <attributes>
+                      <attribute name="font-desc" value="default 16"/>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox" id="box1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="margin_left">16</property>
+            <property name="margin_right">16</property>
+            <property name="margin_top">16</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">10</property>
+            <child>
+              <object class="GtkLabel" id="software-version">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label">Endless 1.x.y</property>
+                <attributes>
+                  <attribute name="font-desc" value="default 32"/>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="product-id">
+                <property name="visible">False</property>
+                <property name="can_focus">False</property>
+                <attributes>
+                  <attribute name="font-desc" value="default 16"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="image-version">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label">image version</property>
+                <attributes>
+                  <attribute name="font-desc" value="default 16"/>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="sd-card">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label">sd card</property>
+                <attributes>
+                  <attribute name="font-desc" value="default 16"/>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="serial-text">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="no">label</property>
+                <attributes>
+                  <attribute name="font-desc" value="default 26"/>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">4</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">4</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/gnome-initial-setup/meson.build b/gnome-initial-setup/meson.build
index 8d31a936..9cb86948 100644
--- a/gnome-initial-setup/meson.build
+++ b/gnome-initial-setup/meson.build
@@ -8,6 +8,12 @@ resources = gnome.compile_resources(
     c_name: 'gis_assistant'
 )
 
+resources += gnome.compile_resources(
+    'gis-page-util-resources',
+    files('gis-page-util.gresource.xml'),
+    c_name: 'gis_page_util'
+)
+
 sources += [
     resources,
     'cc-common-language.c',
diff --git a/gnome-initial-setup/pages/language/gis-language-page.c 
b/gnome-initial-setup/pages/language/gis-language-page.c
index f62b2ad4..9f2beb73 100644
--- a/gnome-initial-setup/pages/language/gis-language-page.c
+++ b/gnome-initial-setup/pages/language/gis-language-page.c
@@ -33,6 +33,7 @@
 #include "language-resources.h"
 #include "gis-welcome-widget.h"
 #include "cc-language-chooser.h"
+#include "gis-page-util.h"
 #include "gis-language-page.h"
 
 #include <act/act-user-manager.h>
@@ -51,6 +52,8 @@ struct _GisLanguagePagePrivate
   const gchar *new_locale_id;
 
   GCancellable *cancellable;
+
+  GtkAccelGroup *accel_group;
 };
 typedef struct _GisLanguagePagePrivate GisLanguagePagePrivate;
 
@@ -222,6 +225,7 @@ gis_language_page_constructed (GObject *object)
   GisLanguagePage *page = GIS_LANGUAGE_PAGE (object);
   GisLanguagePagePrivate *priv = gis_language_page_get_instance_private (page);
   GDBusConnection *bus;
+  GClosure *closure;
 
   g_type_ensure (CC_TYPE_LANGUAGE_CHOOSER);
 
@@ -252,10 +256,24 @@ gis_language_page_constructed (GObject *object)
       g_object_unref (bus);
     }
 
+  /* Use ctrl+f to show factory dialog */
+  priv->accel_group = gtk_accel_group_new ();
+  closure = g_cclosure_new_swap (G_CALLBACK (gis_page_util_show_factory_dialog), page, NULL);
+  gtk_accel_group_connect (priv->accel_group, GDK_KEY_f, GDK_CONTROL_MASK, 0, closure);
+
   gis_page_set_complete (GIS_PAGE (page), TRUE);
   gtk_widget_show (GTK_WIDGET (page));
 }
 
+static GtkAccelGroup *
+gis_language_page_get_accel_group (GisPage *page)
+{
+  GisLanguagePage *self = GIS_LANGUAGE_PAGE (page);
+  GisLanguagePagePrivate *priv = gis_language_page_get_instance_private (self);
+
+  return priv->accel_group;
+}
+
 static void
 gis_language_page_locale_changed (GisPage *page)
 {
@@ -271,6 +289,7 @@ gis_language_page_dispose (GObject *object)
   g_clear_object (&priv->permission);
   g_clear_object (&priv->localed);
   g_clear_object (&priv->cancellable);
+  g_clear_object (&priv->accel_group);
 
   G_OBJECT_CLASS (gis_language_page_parent_class)->dispose (object);
 }
@@ -289,6 +308,7 @@ gis_language_page_class_init (GisLanguagePageClass *klass)
 
   page_class->page_id = PAGE_ID;
   page_class->locale_changed = gis_language_page_locale_changed;
+  page_class->get_accel_group = gis_language_page_get_accel_group;
   object_class->constructed = gis_language_page_constructed;
   object_class->dispose = gis_language_page_dispose;
 }
diff --git a/meson.build b/meson.build
index 6d1c56f3..d6927b2f 100644
--- a/meson.build
+++ b/meson.build
@@ -13,6 +13,7 @@ prefix = get_option('prefix')
 po_dir = join_paths(meson.source_root(), 'po')
 data_dir = join_paths(prefix, get_option('datadir'))
 locale_dir = join_paths(prefix, get_option('localedir'))
+local_state_dir = join_paths(prefix, get_option('localstatedir'))
 libexec_dir = join_paths(prefix, get_option('libexecdir'))
 sysconf_dir = join_paths(prefix, get_option('sysconfdir'))
 source_root = join_paths(meson.source_root(), 'gnome-initial-setup')
@@ -22,6 +23,8 @@ pkgsysconf_dir = join_paths(sysconf_dir, meson.project_name())
 conf = configuration_data()
 conf.set_quoted('GETTEXT_PACKAGE', meson.project_name())
 conf.set_quoted('GNOMELOCALEDIR', locale_dir)
+conf.set_quoted('LIBEXECDIR', libexec_dir)
+conf.set_quoted('LOCALSTATEDIR', local_state_dir)
 conf.set_quoted('PKGDATADIR', pkgdata_dir)
 conf.set_quoted('DATADIR', data_dir)
 conf.set_quoted('PKGSYSCONFDIR', pkgsysconf_dir)
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 65ee030a..e96f9b40 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -2,6 +2,8 @@ data/gnome-initial-setup-first-login.desktop.in.in
 data/gnome-initial-setup.desktop.in.in
 gnome-initial-setup/cc-common-language.c
 gnome-initial-setup/gis-assistant.c
+gnome-initial-setup/gis-page-util.c
+gnome-initial-setup/gis-page-util.ui
 gnome-initial-setup/gnome-initial-setup.c
 gnome-initial-setup/pages/account/gis-account-avatar-chooser.ui
 gnome-initial-setup/pages/account/gis-account-page-enterprise.c


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]