diff options
author | Mike Gabriel <mike.gabriel@das-netzwerkteam.de> | 2023-02-06 10:50:34 +0100 |
---|---|---|
committer | Mike Gabriel <mike.gabriel@das-netzwerkteam.de> | 2023-02-06 10:50:34 +0100 |
commit | 642cbe7d3830c7dd27230ea84e86277243ccd7b7 (patch) | |
tree | 6d4f269e0389b8f12d267cb715dc663382763c82 | |
parent | b2fac08944adadd4b278a58a5fe363ef0df1e9c2 (diff) | |
parent | bf00dadc0584d911bf4c23cc4ab25e8a09c611ce (diff) | |
download | arctica-greeter-642cbe7d3830c7dd27230ea84e86277243ccd7b7.tar.gz arctica-greeter-642cbe7d3830c7dd27230ea84e86277243ccd7b7.tar.bz2 arctica-greeter-642cbe7d3830c7dd27230ea84e86277243ccd7b7.zip |
Merge remote-tracking branch 'gh-Ionic/feature/high-contrast-big-font'
Attributes GH PR #40: https://github.com/ArcticaProject/arctica-greeter/pull/40
-rw-r--r-- | configure.ac | 10 | ||||
-rw-r--r-- | data/org.ArcticaProject.arctica-greeter.gschema.xml | 12 | ||||
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/arctica-greeter.vala | 150 | ||||
-rw-r--r-- | src/background.vala | 3 | ||||
-rw-r--r-- | src/dash-box.vala | 22 | ||||
-rw-r--r-- | src/dash-entry.vala | 5 | ||||
-rw-r--r-- | src/greeter-list.vala | 37 | ||||
-rw-r--r-- | src/main-window.vala | 13 | ||||
-rw-r--r-- | src/menubar.vala | 198 | ||||
-rw-r--r-- | src/prompt-box.vala | 188 | ||||
-rw-r--r-- | src/session-list.vala | 3 | ||||
-rw-r--r-- | src/settings.vala | 71 | ||||
-rw-r--r-- | src/toggle-box.vala | 43 | ||||
-rw-r--r-- | src/user-list.vala | 95 | ||||
-rw-r--r-- | src/util.vala | 28 | ||||
-rw-r--r-- | tests/arctica-greeter.vala | 10 | ||||
-rw-r--r-- | tests/menubar.vala | 1 | ||||
-rw-r--r-- | tests/test.vala | 70 |
19 files changed, 823 insertions, 139 deletions
diff --git a/configure.ac b/configure.ac index 222556a..7619254 100644 --- a/configure.ac +++ b/configure.ac @@ -91,6 +91,16 @@ if test x$gtk_check_pass = xyes ; then AC_SUBST([AM_VALAFLAGS]) fi +dnl ########################################################################### +dnl Check for GTK version - 4.0 +dnl ########################################################################### + +PKG_CHECK_MODULES(GTK_4_0, gtk4 >= 4.0.0 , gtk4_check_pass=yes, gtk4_check_pass=no) +if test x$gtk4_check_pass = xyes ; then + AM_VALAFLAGS="$AM_VALAFLAGS -D HAVE_GTK_4_0" + AC_SUBST([AM_VALAFLAGS]) +fi + dnl ########################################################################## dnl Remote Logon Dependencies dnl ########################################################################## diff --git a/data/org.ArcticaProject.arctica-greeter.gschema.xml b/data/org.ArcticaProject.arctica-greeter.gschema.xml index 9efc200..b0069c2 100644 --- a/data/org.ArcticaProject.arctica-greeter.gschema.xml +++ b/data/org.ArcticaProject.arctica-greeter.gschema.xml @@ -37,6 +37,10 @@ <default>'Blue-Submarine'</default> <summary>GTK+ theme to use</summary> </key> + <key name="high-contrast-theme-name" type="s"> + <default>'HighContrastInverse'</default> + <summary>GTK+ theme to use in high contrast mode</summary> + </key> <key name="icon-theme-name" type="s"> <default>'Adwaita'</default> <summary>Icon theme to use</summary> @@ -82,6 +86,10 @@ <default>false</default> <summary>Whether to use a high contrast theme</summary> </key> + <key name="big-font" type="b"> + <default>false</default> + <summary>Whether to use big fonts throughout the application</summary> + </key> <key name="screen-reader" type="b"> <default>false</default> <summary>Whether to enable the screen reader</summary> @@ -115,6 +123,10 @@ <default>'auto'</default> <summary>Whether to enable HiDPI support</summary> </key> + <key name="menubar-alpha" type="d"> + <default>0.5</default> + <summary>Alpha value for menubar, multiplied with the theme-provided transparency value. Not used in high contrast mode.</summary> + </key> <key name="remote-service-configure-uri" type="s"> <default>''</default> <summary>Default FQDN for host offering Remote Logon Service</summary> diff --git a/src/Makefile.am b/src/Makefile.am index 5c0c78f..4c10950 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -37,7 +37,8 @@ arctica_greeter_SOURCES = \ toggle-box.vala \ arctica-greeter.vala \ user-list.vala \ - user-prompt-box.vala + user-prompt-box.vala \ + util.vala logo_generator_SOURCES = logo-generator.vala diff --git a/src/arctica-greeter.vala b/src/arctica-greeter.vala index 96a98ce..6d31a9b 100644 --- a/src/arctica-greeter.vala +++ b/src/arctica-greeter.vala @@ -21,16 +21,15 @@ public const int grid_size = 40; -public class ArcticaGreeter +[SingleInstance] +public class ArcticaGreeter : Object { - public static ArcticaGreeter singleton; - public signal void show_message (string text, LightDM.MessageType type); public signal void show_prompt (string text, LightDM.PromptType type); public signal void authentication_complete (); public signal void starting_session (); - public bool test_mode = false; + public bool test_mode { get; construct; default = false; } private string state_file; private KeyFile state; @@ -53,11 +52,8 @@ public class ArcticaGreeter public signal void xsettings_ready (); public signal void greeter_ready (); - private ArcticaGreeter (bool test_mode_) + construct { - singleton = this; - test_mode = test_mode_; - greeter = new LightDM.Greeter (); greeter.show_message.connect ((text, type) => { show_message (text, type); }); greeter.show_prompt.connect ((text, type) => { show_prompt (text, type); }); @@ -140,6 +136,22 @@ public class ArcticaGreeter xsettings_ready_cb (); } + /* + * Note that we need a way to specify a parameter for the initial instance + * creation of the singleton, but also a constructor that takes no + * parameters for later usage. + * + * Making the parameter optional is a good compromise. + * + * This this parameter is construct-only, initializing it by passing it to + * the GObject constructor is both the correct way to do it, and it will + * additionally avoid changing it in later calls of our constructor. + */ + public ArcticaGreeter (bool test_mode_ = false) + { + Object (test_mode: test_mode_); + } + public string? get_state (string key) { try @@ -294,6 +306,47 @@ public class ArcticaGreeter Canberra.PROP_EVENT_ID, "system-ready"); + /* Synchronize properties in AGSettings once. */ + var agsettings = new AGSettings (); + agsettings.high_contrast = !(!(agsettings.high_contrast)); + agsettings.big_font = !(!(agsettings.big_font)); + + /* + * Add timeouts to process the full node hierarchy to handle a11y + * changes. + * + * That's the easiest way to handle a changing node hierarchy. + * + * Alternatives would involve connecting a function for every a11y + * change to the GtkWidget::parent-set event to *every widget* we + * create, but that would make the code incredibly messy. + * + * The value has been determined by a fair dice roll and should make + * sure that changes are visible almost instantaneously to users. + */ + Timeout.add_full (GLib.Priority.HIGH_IDLE, 302, () => { + var agsettings_intimer = new AGSettings (); + /* + if (0 == GLib.Random.int_range (0, 10)) { + debug ("Syncing up high contrast value via timer: %s", agsettings_intimer.high_contrast.to_string ()); + } + */ + switch_contrast (agsettings_intimer.high_contrast); + + return true; + }); + Timeout.add_full (GLib.Priority.HIGH_IDLE, 302, () => { + var agsettings_intimer = new AGSettings (); + /* + if (0 == GLib.Random.int_range (0, 10)) { + debug ("Syncing up big font value via timer: %s", agsettings_intimer.big_font.to_string ()); + } + */ + switch_font (agsettings_intimer.big_font); + + return true; + }); + return false; } @@ -340,7 +393,7 @@ public class ArcticaGreeter { try { - ArcticaGreeter.singleton.greeter.authenticate_remote (session, userid); + greeter.authenticate_remote (session, userid); } catch (Error e) { @@ -407,6 +460,75 @@ public class ArcticaGreeter return greeter.has_guest_account_hint; } + private delegate void SwitchClassType (Gtk.Widget widget, string classname, bool enable); + + private delegate void IterateChildrenType (Gtk.Widget widget); + + private void switch_generic (Gtk.Widget widget, string classname, bool enable) + { + var style_ctx = widget.get_style_context (); + if (enable) + { + style_ctx.add_class (classname); + } + else + { + style_ctx.remove_class (classname); + } + } + + private void iterate_children_generic (Gtk.Widget widget, SwitchClassType switch_func, string classname, bool enable) + { + /* + * GTK 4 changed its API quite dramatically, got rid of GtkContainer + * and made each GtkWidget accept children, while also defining a new + * way to access those. + */ + IterateChildrenType rec_func = null; + rec_func = (widget) => { +#if HAVE_GTK_4_0 + Gtk.Widget child = widget.get_first_child (); + while (null != child) + { + rec_func (child); + child = child.get_next_sibling (); + } +#else + if (gtk_is_container (widget)) + { + ((Gtk.Container)(widget)).@foreach (rec_func); + } +#endif + + /* Common code to add or remove the CSS class. */ + switch_func (widget, classname, enable); + }; + + /* + * Actually recursively iterate through this item and all of its + * children. + */ + rec_func (widget); + } + + public void switch_contrast (bool high) + { + var time_pre = GLib.get_monotonic_time (); + iterate_children_generic (main_window, switch_generic, "high_contrast", high); + var time_post = GLib.get_monotonic_time (); + var time_diff = time_post - time_pre; + assert (0 <= time_diff); + var time_diff_sec = time_diff / 1000000; + var time_diff_msec = time_diff / 1000; + var time_diff_usec = time_diff % 1000000; + // debug ("Time passed: %" + int64.FORMAT + " s, %" + int64.FORMAT + " ms, %" + int64.FORMAT + " us", time_diff_sec, time_diff_msec, time_diff_usec); + } + + public void switch_font (bool big) + { + iterate_children_generic (main_window, switch_generic, "big_font", big); + } + private Gdk.FilterReturn focus_upon_map (Gdk.XEvent gxevent, Gdk.Event event) { var xevent = (X.Event*)gxevent; @@ -807,6 +929,16 @@ public class ArcticaGreeter if (value != "") settings.set ("gtk-xft-rgba", value, null); + /* + * Keep a reference to an AGSettings instance for the whole program + * run, so that the SingleInstance property is working the way we'd + * like it to work. + * + * We want to do this before creating the actual greeter, since the + * latter is using AGSettings quite extensively. + */ + var agsettings = new AGSettings (); + debug ("Creating Arctica Greeter"); var greeter = new ArcticaGreeter (do_test_mode); diff --git a/src/background.vala b/src/background.vala index 9a77047..18bf169 100644 --- a/src/background.vala +++ b/src/background.vala @@ -778,7 +778,8 @@ public class Background : Gtk.Fixed { notify_property ("average-color"); - if (!ArcticaGreeter.singleton.test_mode) + var greeter = new ArcticaGreeter (); + if (!greeter.test_mode) { var rgba = current.average_color.to_string (); var root = get_screen ().get_root_window (); diff --git a/src/dash-box.vala b/src/dash-box.vala index cc1d715..1073aa5 100644 --- a/src/dash-box.vala +++ b/src/dash-box.vala @@ -54,7 +54,8 @@ public class DashBox : Gtk.Box /* Does not actually add w to this widget, as doing so would potentially mess with w's placement. */ public void set_base (Gtk.Widget? w) { - if (!ArcticaGreeter.singleton.test_mode) { + var greeter = new ArcticaGreeter (); + if (!greeter.test_mode) { return_if_fail (pushed == null); return_if_fail (mode == Mode.NORMAL); } @@ -220,10 +221,25 @@ public class DashBox : Gtk.Box CairoUtils.rounded_rectangle (c, 0, box_y, box_w, box_h, box_r); - c.set_source_rgba (0.1, 0.1, 0.1, 0.4); + var agsettings = new AGSettings (); + if (agsettings.high_contrast) + { + c.set_source_rgba (1.0, 1.0, 1.0, 1.0); + } + else + { + c.set_source_rgba (0.1, 0.1, 0.1, 0.4); + } c.fill_preserve (); - c.set_source_rgba (0.4, 0.4, 0.4, 0.4); + if (agsettings.high_contrast) + { + c.set_source_rgba (0.0, 0.0, 0.0, 1.0); + } + else + { + c.set_source_rgba (0.4, 0.4, 0.4, 0.4); + } c.set_line_width (1); c.stroke (); diff --git a/src/dash-entry.vala b/src/dash-entry.vala index 526b631..4d1146d 100644 --- a/src/dash-entry.vala +++ b/src/dash-entry.vala @@ -301,10 +301,11 @@ public class DashEntry : Gtk.Entry, Fadable // This is a workaround for bug https://launchpad.net/bugs/944159 // The problem is that orca seems to not notice that it's in a password // field on startup. We just need to kick orca in the pants. - if (ArcticaGreeter.singleton.orca_needs_kick) + var greeter = new ArcticaGreeter (); + if (greeter.orca_needs_kick) { Signal.emit_by_name (get_accessible (), "focus-event", true); - ArcticaGreeter.singleton.orca_needs_kick = false; + greeter.orca_needs_kick = false; } return base.key_press_event (event); diff --git a/src/greeter-list.vala b/src/greeter-list.vala index 0cc3a1f..98c8b48 100644 --- a/src/greeter-list.vala +++ b/src/greeter-list.vala @@ -229,7 +229,8 @@ public abstract class GreeterList : FadableBox public void cancel_authentication () { - ArcticaGreeter.singleton.cancel_authentication (); + var greeter = new ArcticaGreeter (); + greeter.cancel_authentication (); entry_selected (selected_entry.id); } @@ -483,7 +484,8 @@ public abstract class GreeterList : FadableBox entry.destroy (); /* Show a manual login if no users and no remote login entry */ - if (!have_entries () && !ArcticaGreeter.singleton.show_remote_login_hint ()) + var greeter = new ArcticaGreeter (); + if (!have_entries () && !greeter.show_remote_login_hint ()) add_manual_entry (); queue_draw (); @@ -793,9 +795,10 @@ public abstract class GreeterList : FadableBox protected void connect_to_lightdm () { - ArcticaGreeter.singleton.show_message.connect (show_message_cb); - ArcticaGreeter.singleton.show_prompt.connect (show_prompt_cb); - ArcticaGreeter.singleton.authentication_complete.connect (authentication_complete_cb); + var greeter = new ArcticaGreeter (); + greeter.show_message.connect (show_message_cb); + greeter.show_prompt.connect (show_prompt_cb); + greeter.authentication_complete.connect (authentication_complete_cb); } protected void show_message_cb (string text, LightDM.MessageType type) @@ -809,10 +812,11 @@ public abstract class GreeterList : FadableBox /* Notify the greeter on what user has been logged */ if (get_selected_id () == "*other" && manual_name == null) { - if (ArcticaGreeter.singleton.test_mode) + var greeter = new ArcticaGreeter (); + if (greeter.test_mode) manual_name = test_username; else - manual_name = ArcticaGreeter.singleton.authentication_user(); + manual_name = greeter.authentication_user(); } prompted = true; @@ -841,10 +845,11 @@ public abstract class GreeterList : FadableBox return; bool is_authenticated; - if (ArcticaGreeter.singleton.test_mode) + var greeter = new ArcticaGreeter (); + if (greeter.test_mode) is_authenticated = test_is_authenticated; else - is_authenticated = ArcticaGreeter.singleton.is_authenticated(); + is_authenticated = greeter.is_authenticated(); if (is_authenticated) { @@ -852,7 +857,7 @@ public abstract class GreeterList : FadableBox if (prompted && !unacknowledged_messages) { login_complete (); - if (ArcticaGreeter.singleton.test_mode) + if (greeter.test_mode) start_session (); else { @@ -905,16 +910,17 @@ public abstract class GreeterList : FadableBox greeter_authenticating_user = get_selected_id (); - if (ArcticaGreeter.singleton.test_mode) + var greeter = new ArcticaGreeter (); + if (greeter.test_mode) test_start_authentication (); else { if (get_selected_id () == "*other") - ArcticaGreeter.singleton.authenticate (); + greeter.authenticate (); else if (get_selected_id () == "*guest") - ArcticaGreeter.singleton.authenticate_as_guest (); + greeter.authenticate_as_guest (); else - ArcticaGreeter.singleton.authenticate (get_selected_id ()); + greeter.authenticate (get_selected_id ()); } } @@ -929,7 +935,8 @@ public abstract class GreeterList : FadableBox private void start_session () { - if (!ArcticaGreeter.singleton.start_session (get_lightdm_session (), background)) + var greeter = new ArcticaGreeter (); + if (!greeter.start_session (get_lightdm_session (), background)) { show_message (_("Failed to start session"), true); start_authentication (); diff --git a/src/main-window.vala b/src/main-window.vala index 993e43e..d7fe6f0 100644 --- a/src/main-window.vala +++ b/src/main-window.vala @@ -40,7 +40,7 @@ public class MainWindow : Gtk.Window public ListStack stack; // Menubar is smaller, but with shadow, we reserve more space - public const int MENUBAR_HEIGHT = 32; + public const int MENUBAR_HEIGHT = 40; construct { @@ -75,6 +75,8 @@ public class MainWindow : Gtk.Window shadow_style = "background-image: url('%s'); background-repeat: repeat;".printf(shadow_path); } + /* Disable the shadow image, we will use CSS instead. */ + /* try { var style = new Gtk.CssProvider (); @@ -89,6 +91,7 @@ public class MainWindow : Gtk.Window { debug ("Internal error loading menubox style: %s", e.message); } + */ menubox.set_size_request (-1, MENUBAR_HEIGHT); menubox.show (); menualign.show (); @@ -159,7 +162,8 @@ public class MainWindow : Gtk.Window only_on_monitor = AGSettings.get_string(AGSettings.KEY_ONLY_ON_MONITOR); monitor_setting_ok = only_on_monitor == "auto"; - if (ArcticaGreeter.singleton.test_mode) + var greeter = new ArcticaGreeter (); + if (greeter.test_mode) { /* Simulate an 800x600 monitor to the left of a 640x480 monitor */ monitors = new List<Monitor> (); @@ -375,6 +379,7 @@ public class MainWindow : Gtk.Window } } + var greeter = new ArcticaGreeter (); switch (event.keyval) { case Gdk.Key.Escape: @@ -434,14 +439,14 @@ public class MainWindow : Gtk.Window } return true; case Gdk.Key.z: - if (ArcticaGreeter.singleton.test_mode && (event.state & Gdk.ModifierType.MOD1_MASK) != 0) + if (greeter.test_mode && (event.state & Gdk.ModifierType.MOD1_MASK) != 0) { show_shutdown_dialog (ShutdownDialogType.SHUTDOWN); return true; } break; case Gdk.Key.Z: - if (ArcticaGreeter.singleton.test_mode && (event.state & Gdk.ModifierType.MOD1_MASK) != 0) + if (greeter.test_mode && (event.state & Gdk.ModifierType.MOD1_MASK) != 0) { show_shutdown_dialog (ShutdownDialogType.RESTART); return true; diff --git a/src/menubar.vala b/src/menubar.vala index a2b6b99..a417a03 100644 --- a/src/menubar.vala +++ b/src/menubar.vala @@ -68,7 +68,6 @@ private class IndicatorMenuItem : Gtk.MenuItem public class MenuBar : Gtk.MenuBar { public Background? background { get; construct; default = null; } - public bool high_contrast { get; private set; default = false; } public Gtk.Window? keyboard_window { get; private set; default = null; } public Gtk.AccelGroup? accel_group { get; construct; } @@ -83,16 +82,57 @@ public class MenuBar : Gtk.MenuBar { if (background != null) { + /* Disable background drawing to see how it changes the visuals. */ + /* int x, y; background.translate_coordinates (this, 0, 0, out x, out y); c.save (); c.translate (x, y); background.draw_full (c, Background.DrawFlags.NONE); c.restore (); + */ } - c.set_source_rgb (0.1, 0.1, 0.1); - c.paint_with_alpha (0.4); + /* Get the style and dimensions. */ + var style_ctx = this.get_style_context (); + + var w = this.get_allocated_width (); + var h = this.get_allocated_height (); + + /* Add a group. */ + c.push_group (); + + /* Draw the background normally. */ + style_ctx.render_background (c, 0, 0, w, h); + + /* Draw the frame normally. */ + style_ctx.render_frame (c, 0, 0, w, h); + + /* Go back to the original widget. */ + c.pop_group_to_source (); + + var agsettings = new AGSettings (); + if (agsettings.high_contrast) { + /* + * In case the high contrast mode is enabled, do not add any + * transparency. While the GTK theme might define one (even though + * it better should not, given that we are also switching to a + * high contrast theme), we certainly do not want to make the look + * fuzzy. + */ + c.paint (); + } + else { + /* + * And finally repaint it with additional transparency. + * Note that most GTK styles already define a transparency for OSD + * menus. We want to have something more transparent, but also + * make sure that it is not too transparent, so do not choose a + * value that is too low here - certainly not your desired final + * alpha value. + */ + c.paint_with_alpha (AGSettings.get_double (AGSettings.KEY_MENUBAR_ALPHA)); + } foreach (var child in get_children ()) { @@ -102,43 +142,131 @@ public class MenuBar : Gtk.MenuBar return false; } + public static void add_style_class (Gtk.Widget widget) + { + /* + * Add style context class osd, which makes the widget respect the GTK + * style definitions for this type of elements. + */ + var ctx = widget.get_style_context (); + ctx.add_class ("osd"); + } + /* Due to LP #973922 the keyboard has to be loaded after the main window * is shown and given focus. Therefore we don't enable the active state * until now. */ public void set_keyboard_state () { - if (!ArcticaGreeter.singleton.test_mode) + var greeter = new ArcticaGreeter (); + if (!greeter.test_mode) onscreen_keyboard_item.set_active (AGSettings.get_boolean (AGSettings.KEY_ONSCREEN_KEYBOARD)); } - private string default_theme_name; private List<Indicator.Object> indicator_objects; private Gtk.CheckMenuItem high_contrast_item; + private Gtk.CheckMenuItem big_font_item; private Pid keyboard_pid = 0; private Pid reader_pid = 0; private Gtk.CheckMenuItem onscreen_keyboard_item; construct { - Gtk.Settings.get_default ().get ("gtk-theme-name", out default_theme_name); + add_style_class (this); + + /* Add shadow. */ + var shadow_style = new Gtk.CssProvider (); + shadow_style.load_from_data ("* { box-shadow: 0px 0px 5px 5px #000000; }", -1); + this.get_style_context ().add_provider (shadow_style, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); pack_direction = Gtk.PackDirection.RTL; if (AGSettings.get_boolean (AGSettings.KEY_SHOW_HOSTNAME)) { - var label = new Gtk.Label (Posix.utsname ().nodename); - label.show (); - var hostname_item = new Gtk.MenuItem (); - hostname_item.add (label); - hostname_item.sensitive = false; - hostname_item.right_justified = true; - hostname_item.show (); + var hostname_item = new Gtk.MenuItem.with_label (Posix.utsname ().nodename); append (hostname_item); + hostname_item.show (); + + /* + * Even though this (menu) item is insensitive, we want its label + * text to have the sensitive color as to not look out of place + * and difficult to read. + * + * There's a really weird bug that leads to always fetch the + * sensitive color after the widget (menuitem in this case) has + * been set to insensitive once - at least in this constructor. + * + * I haven't found a way to fix that, or, for that matter, what is + * actually causing the issue. Even waiting on the main event loop + * until all events are processed didn't help. + * + * We'll work around this issue by fetching the color before + * setting the widget to insensitive and call it proper. + */ + var insensitive_override_style = new Gtk.CssProvider (); + + /* + * First, fetch the associated GtkStyleContext and save the state, + * we'll override the state later on. + */ + var hostname_item_ctx = hostname_item.get_style_context (); + hostname_item_ctx.save (); + + try { + /* Get the actual color. */ + var sensitive_color = hostname_item_ctx.get_color (Gtk.StateFlags.NORMAL); + debug ("Directly fetched sensitive color: %s", sensitive_color.to_string ()); + + insensitive_override_style.load_from_data ("*:disabled { color: %s; }".printf(sensitive_color.to_string ()), -1); + } + catch (Error e) + { + debug ("Internal error loading hostname menu item text color: %s", e.message); + } + finally { + /* + * Restore the context, which we might have changed through the + * previous get_color () call. + */ + hostname_item_ctx.restore (); + } + + try { + /* And finally override the insensitive color. */ + hostname_item_ctx.add_provider (insensitive_override_style, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); + + /* + * Just overriding the color for the Gtk.MenuItem widget + * doesn't help, we'll also apply it to the children. + * + * In theory, we could just use the get_child () method to + * fetch the only child we should ever have on that widget, + * namely a GtkAccelLabel, but that isn't future-proof enough, + * especially if that is ever extended into having a submenu. + * + * Thus, iterate over all children and override the style for + * all of them. + */ + if (gtk_is_container (hostname_item)) { + var children = hostname_item.get_children (); + foreach (Gtk.Widget element in children) { + var child_ctx = element.get_style_context (); + debug ("Adding override style provider to child widget %s", element.name); + child_ctx.add_provider (insensitive_override_style, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); + } + } + } + catch (Error e) + { + debug ("Internal error overriding hostname menu item text color: %s", e.message); + } - /* Hack to get a label showing on the menubar */ - var fg = label.get_style_context ().get_color (Gtk.StateFlags.NORMAL); - label.override_color (Gtk.StateFlags.INSENSITIVE, fg); + hostname_item.set_sensitive (false); + + hostname_item.set_right_justified (true); } /* Prevent dragging the window by the menubar */ @@ -155,7 +283,8 @@ public class MenuBar : Gtk.MenuBar setup_indicators (); - ArcticaGreeter.singleton.starting_session.connect (cleanup); + var greeter = new ArcticaGreeter (); + greeter.starting_session.connect (cleanup); } private void close_pid (ref Pid pid) @@ -233,7 +362,19 @@ public class MenuBar : Gtk.MenuBar high_contrast_item.add_accelerator ("activate", accel_group, Gdk.Key.h, Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE); high_contrast_item.show (); submenu.append (high_contrast_item); - high_contrast_item.set_active (AGSettings.get_boolean (AGSettings.KEY_HIGH_CONTRAST)); + var agsettings = new AGSettings (); + debug ("Initializing high contrast menu item to state %s", agsettings.high_contrast.to_string ()); + high_contrast_item.set_active (agsettings.high_contrast); + +/* Hide the Big Font feature until it's available. + big_font_item = new Gtk.CheckMenuItem.with_label (_("Big Font")); + big_font_item.toggled.connect (big_font_toggled_cb); + big_font_item.add_accelerator ("activate", accel_group, Gdk.Key.b, Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE); + big_font_item.show (); + submenu.append (big_font_item); +*/ + + big_font_item.set_active (agsettings.big_font); var item = new Gtk.CheckMenuItem.with_label (_("Screen Reader")); item.toggled.connect (screen_reader_toggled_cb); item.add_accelerator ("activate", accel_group, Gdk.Key.s, Gdk.ModifierType.SUPER_MASK | Gdk.ModifierType.MOD1_MASK, Gtk.AccelFlags.VISIBLE); @@ -293,7 +434,8 @@ public class MenuBar : Gtk.MenuBar private void load_indicator (string indicator_name) { - if (!ArcticaGreeter.singleton.test_mode) + var greeter = new ArcticaGreeter (); + if (!greeter.test_mode) { if (indicator_name == "ug-accessibility") { @@ -440,13 +582,14 @@ public class MenuBar : Gtk.MenuBar private void high_contrast_toggled_cb (Gtk.CheckMenuItem item) { - var settings = Gtk.Settings.get_default (); - if (item.active) - settings.set ("gtk-theme-name", "HighContrastInverse"); - else - settings.set ("gtk-theme-name", default_theme_name); - high_contrast = item.active; - AGSettings.set_boolean (AGSettings.KEY_HIGH_CONTRAST, high_contrast); + var agsettings = new AGSettings (); + agsettings.high_contrast = item.active; + } + + private void big_font_toggled_cb (Gtk.CheckMenuItem item) + { + var agsettings = new AGSettings (); + agsettings.big_font = item.active; } private void screen_reader_toggled_cb (Gtk.CheckMenuItem item) @@ -484,7 +627,8 @@ public class MenuBar : Gtk.MenuBar // this is not racy with orca startup, it is racy with whether // orca will read the first character or not out loud. Hence // why we do both. Ideally this would be fixed in orca itself. - ArcticaGreeter.singleton.orca_needs_kick = true; + var greeter = new ArcticaGreeter (); + greeter.orca_needs_kick = true; Timeout.add_seconds (1, () => { Signal.emit_by_name ((get_toplevel () as Gtk.Window).get_focus ().get_accessible (), "focus-event", true); return false; diff --git a/src/prompt-box.vala b/src/prompt-box.vala index 1a0007f..2180c70 100644 --- a/src/prompt-box.vala +++ b/src/prompt-box.vala @@ -208,7 +208,21 @@ public class PromptBox : FadableBox debug ("Internal error loading font style (%s, %dpt): %s", font_family, font_size+2, e.message); } - name_label.override_color (Gtk.StateFlags.NORMAL, { 1.0f, 1.0f, 1.0f, 1.0f }); + var agsettings = new AGSettings (); + try + { + var color_provider = new Gtk.CssProvider (); + var css = "* { color: rgba(255, 255, 255, 1.0); }\n" + + ".high_contrast { color: rgba (0, 0, 0, 1.0); }"; + color_provider.load_from_data (css, -1); + style_ctx.add_provider (color_provider, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); + } + catch (Error e) + { + debug ("Internal error setting color on name label: %s", e.message); + } + name_label.valign = Gtk.Align.START; name_label.vexpand = true; name_label.yalign = 0.5f; @@ -236,7 +250,40 @@ public class PromptBox : FadableBox name_grid.attach (align, COL_NAME_MESSAGE, ROW_NAME, 1, 1); option_button = new FlatButton (); - option_button.get_style_context ().add_class ("option-button"); + var option_button_ctx = option_button.get_style_context (); + option_button_ctx.add_class ("option-button"); + + try { + /* + * Override background for both high-contrast and normal modes. + * Note that we have to use CSS selectors here, since this code + * is only executed once. + */ + var background_style = new Gtk.CssProvider (); + background_style.load_from_data ("button.flat.option-button.high_contrast {\n" + + " background-color: %s;\n".printf("rgba(0,0,0,1.0)") + + " background-image: none;\n" + + "}\n" + + "button.flat.option-button:hover:not(.high_contrast), " + + "button.flat.option-button:active:not(.high_contrast), " + + "button.flat.option-button:hover:active:not(.high_contrast) {\n"+ + " background-color: %s;\n".printf("rgba(255,255,255,0.5)")+ + " background-image: none;"+ + "}\n" + + "button.flat.option-button:hover.high_contrast," + + "button.flat.option-button:active.high_contrast," + + "button.flat.option-button:hover:active.high_contrast {\n" + + " background-color:%s;\n".printf("rgba(70, 70, 70, 1.0)") + + " background-image: none;" + + "}\n", -1); + option_button_ctx.add_provider (background_style, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); + } + catch (Error e) + { + debug ("Internal error loading option button background style: %s", e.message); + } + option_button.hexpand = true; option_button.halign = Gtk.Align.END; option_button.valign = Gtk.Align.START; @@ -388,6 +435,7 @@ public class PromptBox : FadableBox public void clear () { prompt_visibility = PromptVisibility.HIDDEN; + active_indicator.expanded = false; /* Hold a ref while removing the prompt widgets - * if we just do w.destroy() we get this warning: @@ -452,6 +500,7 @@ public class PromptBox : FadableBox { case PromptVisibility.HIDDEN: w.hide (); + active_indicator.expanded = false; break; case PromptVisibility.FADING: var f = w as Fadable; @@ -460,10 +509,12 @@ public class PromptBox : FadableBox f.fade_in (); else w.show (); + active_indicator.expanded = true; break; case PromptVisibility.SHOWN: w.show (); w.sensitive = true; + active_indicator.expanded = true; break; } } @@ -473,6 +524,7 @@ public class PromptBox : FadableBox prompt_visibility = PromptVisibility.FADING; show (); foreach_prompt_widget ((w) => { update_prompt_visibility (w); }); + active_indicator.expanded = true; } public void show_prompts () @@ -480,6 +532,7 @@ public class PromptBox : FadableBox prompt_visibility = PromptVisibility.SHOWN; show (); foreach_prompt_widget ((w) => { update_prompt_visibility (w); }); + active_indicator.expanded = true; } protected void attach_item (Gtk.Widget w, bool add_style_class = true) @@ -713,6 +766,7 @@ public class PromptBox : FadableBox private class ActiveIndicator : Gtk.Image { public bool active { get; set; } + public bool expanded { get; set; } public const int WIDTH = 8; public const int HEIGHT = 7; @@ -722,12 +776,16 @@ private class ActiveIndicator : Gtk.Image try { pixbuf = new Gdk.Pixbuf.from_file (filename); + pixbuf_changed_helper (); } catch (Error e) { debug ("Could not load active image: %s", e.message); } notify["active"].connect (() => { queue_draw (); }); + notify["expanded"].connect (() => { notify_expanded (); }); + notify["pixbuf"].connect ((s, p) => { notify_pixbuf_changed (s, p); }); + this.style_updated.connect ((w) => { handle_style_updated (w); }); xalign = 0.0f; } @@ -747,6 +805,132 @@ private class ActiveIndicator : Gtk.Image { if (!active) return false; + return base.draw (c); } + + private void notify_pixbuf_changed (Object src, ParamSpec prop) { + assert ("pixbuf" == prop.name); + + if (!(this.swapping_)) { + pixbuf_changed_helper (); + } + } + + private void pixbuf_changed_helper () { + /* Copy the new pixbuf. */ + this.pixbuf_orig_ = this.pixbuf.copy (); + this.pixbuf.copy_options (this.pixbuf_orig_); + this.pixbuf_inverted_ = this.pixbuf.copy (); + this.pixbuf.copy_options (this.pixbuf_inverted_); + + /* Invert the new pixbuf. */ + invert_pixbuf (); + + if (this.inverted_) { + this.swapping_ = true; + this.pixbuf = this.pixbuf_inverted_; + this.swapping_ = false; + } + } + + private void handle_style_updated (Gtk.Widget widget) { + var style_ctx = this.get_style_context (); + if (style_ctx.has_class ("high_contrast")) { + if ((!(this.inverted_)) && (this.expanded)) { + this.swapping_ = true; + this.pixbuf = this.pixbuf_inverted_; + this.inverted_ = true; + this.swapping_ = false; + } + } + else { + if (this.inverted_) { + this.swapping_ = true; + this.pixbuf = this.pixbuf_orig_; + this.inverted_ = false; + this.swapping_ = false; + } + } + } + + private void invert_pixbuf () { + assert (Gdk.Colorspace.RGB == this.pixbuf_inverted_.get_colorspace ()); + var sample_size = this.pixbuf_inverted_.get_bits_per_sample (); + var alpha = this.pixbuf_inverted_.get_has_alpha (); + var channels = this.pixbuf_inverted_.get_n_channels (); + + assert (((4 == channels) && (alpha)) || + (3 == channels)); + + /* + * Fun fact: we don't need to decompose the actual colors. Since + * the inversion is just a simple XOR operation, we can just + * invert all the bits minus the alpha value. + */ + assert (1 <= sample_size); + + var rowstride = this.pixbuf_inverted_.get_rowstride (); + var col_bytes = (((channels * sample_size) + 7) / 8); + var row_bytes = (this.pixbuf_inverted_.width * col_bytes); + unowned var pixels = this.pixbuf_inverted_.get_pixels (); + assert (pixels != null); + size_t color_bits = channels; + if (alpha) { + --color_bits; + } + color_bits *= sample_size; + for (size_t i = 0; i < this.pixbuf_inverted_.height; (++i)) { + for (size_t y = 0; y < this.pixbuf_inverted_.width; (++y)) { + /* Invert full bytes first. */ + for (size_t x = 0; x < (color_bits / 8); (++(x))) { + pixels[(i * rowstride) + (y * col_bytes) + (x)] ^= (~(0)); + } + + /* + * And now, invert the last actual color bits minus alpha + * and padding. + */ + uint8 rest = (uint8) (color_bits % 8); + if (0 < rest) { + uint8 mask = (~(0)); + mask <<= (8 - rest); + pixels[(i * rowstride) + (y * col_bytes) + (color_bits / 8)] ^= mask; + } + } + } + } + + private void notify_expanded () { + if (!(this.expanded)) { + /* + * In non-expanded form, we always want to have the original + * look. + */ + if (this.inverted_) { + this.swapping_ = true; + this.pixbuf = this.pixbuf_orig_; + this.inverted_ = false; + this.swapping_ = false; + } + } + else { + /* + * In expanded form, we want to restore the inverted form iff the + * high contrast mode is enabled. + */ + var agsettings = new AGSettings (); + if ((!(this.inverted_)) && (agsettings.high_contrast)) { + this.swapping_ = true; + this.pixbuf = this.pixbuf_inverted_; + this.inverted_ = true; + this.swapping_ = false; + } + } + } + + private bool inverted_ = false; + private bool swapping_ = false; + private Gdk.Pixbuf? pixbuf_orig_ = null; + private Gdk.Pixbuf? pixbuf_inverted_ = null; } diff --git a/src/session-list.vala b/src/session-list.vala index 820b4c6..290a7af 100644 --- a/src/session-list.vala +++ b/src/session-list.vala @@ -44,7 +44,8 @@ public class SessionPrompt : PromptBox box = new ToggleBox (default_session, session); - if (ArcticaGreeter.singleton.test_mode) + var greeter = new ArcticaGreeter (); + if (greeter.test_mode) { box.add_item ("gnome", "GNOME", SessionList.get_badge ("gnome")); box.add_item ("kde", "KDE", SessionList.get_badge ("kde")); diff --git a/src/settings.vala b/src/settings.vala index 701d5a1..af753ce 100644 --- a/src/settings.vala +++ b/src/settings.vala @@ -2,6 +2,7 @@ * * Copyright (C) 2011,2012 Canonical Ltd * Copyright (C) 2015,2017 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> + * Copyright (C) 2022 Mihai Moldovan <ionic@ionic.de> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as @@ -20,7 +21,8 @@ * Mike Gabriel <mike.gabriel@das-netzwerkteam.de> */ -public class AGSettings +[SingleInstance] +public class AGSettings : Object { public const string KEY_BACKGROUND = "background"; public const string KEY_BACKGROUND_COLOR = "background-color"; @@ -29,6 +31,7 @@ public class AGSettings public const string KEY_SHOW_HOSTNAME = "show-hostname"; public const string KEY_LOGO = "logo"; public const string KEY_THEME_NAME = "theme-name"; + public const string KEY_HIGH_CONTRAST_THEME_NAME = "high-contrast-theme-name"; public const string KEY_ICON_THEME_NAME = "icon-theme-name"; public const string KEY_FONT_NAME = "font-name"; public const string KEY_XFT_ANTIALIAS = "xft-antialias"; @@ -37,6 +40,7 @@ public class AGSettings public const string KEY_XFT_RGBA = "xft-rgba"; public const string KEY_ONSCREEN_KEYBOARD = "onscreen-keyboard"; public const string KEY_HIGH_CONTRAST = "high-contrast"; + public const string KEY_BIG_FONT = "big-font"; public const string KEY_SCREEN_READER = "screen-reader"; public const string KEY_PLAY_READY_SOUND = "play-ready-sound"; public const string KEY_INDICATORS = "indicators"; @@ -49,6 +53,8 @@ public class AGSettings public const string KEY_TOGGLEBOX_FONT_FGCOLOR = "togglebox-font-fgcolor"; public const string KEY_TOGGLEBOX_BUTTON_BGCOLOR = "togglebox-button-bgcolor"; public const string KEY_ENABLE_HIDPI = "enable-hidpi"; + public const string KEY_MENUBAR_ALPHA = "menubar-alpha"; + public static bool get_boolean (string key) { @@ -105,5 +111,68 @@ public class AGSettings return gsettings.set_strv (key, value); } + public AGSettings () + { + } + + construct { + Gtk.Settings.get_default ().get ("gtk-theme-name", out this.default_theme_name_); + /* + debug ("Fetched default theme name in construct: %s", this.default_theme_name_); + */ + } + + public bool high_contrast { + get { + return this.high_contrast_; + } + + set { + debug ("Called high contrast setter with value %s", value.to_string ()); + this.high_contrast_ = value; + + /* Also sync back to dconf, so that this state is persistent. */ + set_boolean (AGSettings.KEY_HIGH_CONTRAST, value); + + var greeter = new ArcticaGreeter (); + greeter.switch_contrast (value); + + var settings = Gtk.Settings.get_default (); + if (value) + { + /* + debug ("Switching GTK Theme to high contrast theme \"%s\"", AGSettings.get_string (AGSettings.KEY_HIGH_CONTRAST_THEME_NAME)); + */ + settings.set ("gtk-theme-name", AGSettings.get_string (AGSettings.KEY_HIGH_CONTRAST_THEME_NAME)); + } + else + { + /* + debug ("Switching GTK Theme to default theme \"%s\"", this.default_theme_name_); + */ + settings.set ("gtk-theme-name", this.default_theme_name_); + } + } + } + + public bool big_font { + get { + return this.big_font_; + } + + set { + this.big_font_ = value; + + /* Also sync back to dconf, so that this state is persistent. */ + set_boolean (AGSettings.KEY_BIG_FONT, value); + + var greeter = new ArcticaGreeter (); + greeter.switch_font (value); + } + } + private const string SCHEMA = "org.ArcticaProject.arctica-greeter"; + private bool high_contrast_ = AGSettings.get_boolean (AGSettings.KEY_HIGH_CONTRAST); + private bool big_font_ = AGSettings.get_boolean (AGSettings.KEY_BIG_FONT); + private string default_theme_name_; } diff --git a/src/toggle-box.vala b/src/toggle-box.vala index 5502c1d..518275f 100644 --- a/src/toggle-box.vala +++ b/src/toggle-box.vala @@ -65,13 +65,26 @@ public class ToggleBox : Gtk.Box /* Tighten padding on buttons to not be so large, default color scheme for buttons */ var style = new Gtk.CssProvider (); style.load_from_data ("* {padding: 8px;}\n"+ - "GtkButton {\n"+ + "GtkButton, button {\n"+ " background-color: %s;\n".printf("rgba(0,0,0,0)")+ " background-image: none;"+ "}\n"+ - ".button:hover,\n"+ - ".button:hover:active {\n"+ + "button:hover,\n"+ + "button:active,\n" + + "button:hover:active,\n" + + "button.selected {\n"+ " background-color: %s;\n".printf(AGSettings.get_string (AGSettings.KEY_TOGGLEBOX_BUTTON_BGCOLOR))+ + "}\n" + + "button.high_contrast {\n" + + " background-color: %s;\n".printf ("rgba(70, 70, 70, 1.0)") + + " background-image: none;\n" + + " border-color: %s\n;".printf ("rgba(0, 0, 0, 1.0)") + + "}\n" + + "button.high_contrast:hover,\n" + + "button.high_contrast:active,\n" + + "button.high_contrast:hover:active,\n" + + "button.high_contrast.selected {\n" + + " background-color: %s;\n".printf ("rgba(0, 0, 0, 1.0)") + "}\n", -1); button.get_style_context ().add_provider (style, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); } @@ -113,9 +126,8 @@ public class ToggleBox : Gtk.Box selected_button = button; selected_key = selected_button.get_data<string> ("toggle-list-key"); - var bg_color = Gdk.RGBA (); - bg_color.parse (AGSettings.get_string (AGSettings.KEY_TOGGLEBOX_BUTTON_BGCOLOR)); - selected_button.override_background_color(Gtk.StateFlags.NORMAL, bg_color); + /* Handle color via CSS. */ + selected_button.get_style_context ().add_class ("selected"); } private Gtk.Button make_button (string key, string name_in, Gdk.Pixbuf? icon) @@ -140,7 +152,24 @@ public class ToggleBox : Gtk.Box } var label = new Gtk.Label (null); - label.set_markup ("<span font=\"%s %d\" fgcolor=\"%s\">%s</span>".printf (font_family, font_size+2, AGSettings.get_string (AGSettings.KEY_TOGGLEBOX_FONT_FGCOLOR), name)); + /* Font and other properties are being handled via CSS. */ + label.set_text (name); + try { + var style = new Gtk.CssProvider (); + style.load_from_data ("label {\n" + + " font-family: \"%s\", sans-serif;\n".printf (font_family) + + " font-size: %d;\n".printf (font_size + 2) + + " color: %s;\n".printf (AGSettings.get_string (AGSettings.KEY_TOGGLEBOX_FONT_FGCOLOR)) + + "}\n" + + "label.high_contrast {\n" + + " color: %s;\n".printf ("rgba(255, 255, 255, 1.0)") + + "}\n", -1); + label.get_style_context ().add_provider (style, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); + } + catch (Error e) + { + debug ("Internal error loading session chooser label style: %s", e.message); + } label.halign = Gtk.Align.START; hbox.pack_start (label, true, true, 0); diff --git a/src/user-list.vala b/src/user-list.vala index 0991703..9f4b265 100644 --- a/src/user-list.vala +++ b/src/user-list.vala @@ -73,7 +73,8 @@ public class UserList : GreeterList { show_hidden_users_ = value; - if (ArcticaGreeter.singleton.test_mode) + var greeter = new ArcticaGreeter (); + if (greeter.test_mode) { if (value) add_user ("hidden", "Hidden User", null, false, false, null); @@ -147,7 +148,8 @@ public class UserList : GreeterList construct { - menubar.notify["high-contrast"].connect (() => { change_background (); }); + var agsettings = new AGSettings (); + agsettings.notify["high-contrast"].connect (() => { change_background (); }); entry_displayed_start.connect (() => { change_background (); }); entry_displayed_done.connect (() => { change_background (); }); @@ -166,8 +168,9 @@ public class UserList : GreeterList connect_to_lightdm (); - if (!ArcticaGreeter.singleton.test_mode && - ArcticaGreeter.singleton.show_remote_login_hint ()) + var greeter = new ArcticaGreeter (); + if (!greeter.test_mode && + greeter.show_remote_login_hint ()) remote_logon_service_watch = Bus.watch_name (BusType.SESSION, "org.ArcticaProject.RemoteLogon", BusNameWatcherFlags.AUTO_START, @@ -390,7 +393,8 @@ public class UserList : GreeterList remote_logon_service = null; /* provide a fallback manual login option */ - if (ArcticaGreeter.singleton.hide_users_hint ()) { + var greeter = new ArcticaGreeter (); + if (greeter.hide_users_hint ()) { add_manual_entry(); set_active_entry ("*other"); } @@ -443,10 +447,11 @@ public class UserList : GreeterList else { var login_success = false; + var greeter = new ArcticaGreeter (); try { var url = url_from_remote_loding_server_list_name (selected_entry.id); - if (ArcticaGreeter.singleton.test_mode) + if (greeter.test_mode) { if (password_field.text == "password") { @@ -528,7 +533,8 @@ public class UserList : GreeterList sensitive = false; will_clear = true; greeter_authenticating_user = selected_entry.id; - if (ArcticaGreeter.singleton.test_mode) + var greeter = new ArcticaGreeter (); + if (greeter.test_mode) { Gtk.Entry field = current_remote_fields.get ("password") as Gtk.Entry; test_is_authenticated = field.text == "password"; @@ -539,7 +545,7 @@ public class UserList : GreeterList } else { - ArcticaGreeter.singleton.authenticate_remote (get_lightdm_session (), null); + greeter.authenticate_remote (get_lightdm_session (), null); remote_logon_service.set_last_used_server.begin (currently_browsing_server_url, url_from_remote_loding_server_list_name (selected_entry.id)); } } @@ -576,7 +582,8 @@ public class UserList : GreeterList if (is_supported_remote_session (config_session)) { greeter_authenticating_user = selected_entry.id; - ArcticaGreeter.singleton.authenticate_remote (config_session, null); + var greeter = new ArcticaGreeter (); + greeter.authenticate_remote (config_session, null); } } dialog.destroy (); @@ -587,7 +594,8 @@ public class UserList : GreeterList private bool change_background_timeout_cb () { string? new_background_file = null; - if (menubar.high_contrast || !AGSettings.get_boolean (AGSettings.KEY_DRAW_USER_BACKGROUNDS)) + var agsettings = new AGSettings (); + if (agsettings.high_contrast || !AGSettings.get_boolean (AGSettings.KEY_DRAW_USER_BACKGROUNDS)) new_background_file = null; else if (selected_entry is UserPromptBox) new_background_file = (selected_entry as UserPromptBox).background; @@ -635,7 +643,8 @@ public class UserList : GreeterList private void entry_selected_cb (string? username) { - ArcticaGreeter.singleton.set_state ("last-user", username); + var greeter = new ArcticaGreeter (); + greeter.set_state ("last-user", username); if (selected_entry is UserPromptBox) session = (selected_entry as UserPromptBox).session; else @@ -777,9 +786,10 @@ public class UserList : GreeterList else if (field.type == "email") { string[] email_domains; + var greeter = new ArcticaGreeter (); try { - if (ArcticaGreeter.singleton.test_mode) + if (greeter.test_mode) email_domains = { "canonical.com", "ubuntu.org", "candy.com", "urban.net" }; else yield remote_logon_service.get_cached_domains_for_server (url, out email_domains); @@ -889,12 +899,13 @@ public class UserList : GreeterList will_clear = true; unacknowledged_messages = false; + var greeter = new ArcticaGreeter (); foreach (var response in responses) { - if (ArcticaGreeter.singleton.test_mode) + if (greeter.test_mode) test_respond (response); else - ArcticaGreeter.singleton.respond (response); + greeter.respond (response); } } @@ -904,10 +915,11 @@ public class UserList : GreeterList unacknowledged_messages = false; var is_authenticated = false; - if (ArcticaGreeter.singleton.test_mode) + var greeter = new ArcticaGreeter (); + if (greeter.test_mode) is_authenticated = test_is_authenticated; else - is_authenticated = ArcticaGreeter.singleton.is_authenticated(); + is_authenticated = greeter.is_authenticated(); /* Finish authentication (again) or restart it */ if (is_authenticated) @@ -923,18 +935,21 @@ public class UserList : GreeterList { var session_chooser = new SessionList (background, menubar, session, default_session); session_chooser.session_clicked.connect (session_clicked_cb); - ArcticaGreeter.singleton.push_list (session_chooser); + var greeter = new ArcticaGreeter (); + greeter.push_list (session_chooser); } private void session_clicked_cb (string session) { this.session = session; - ArcticaGreeter.singleton.pop_list (); + var greeter = new ArcticaGreeter (); + greeter.pop_list (); } private bool should_show_session_badge () { - if (ArcticaGreeter.singleton.test_mode) + var greeter = new ArcticaGreeter (); + if (greeter.test_mode) return get_selected_id () != "no-badge"; else return LightDM.get_sessions ().length () > 1; @@ -962,7 +977,8 @@ public class UserList : GreeterList private bool is_supported_remote_session (string session_internal_name) { - if (ArcticaGreeter.singleton.test_mode) + var greeter = new ArcticaGreeter (); + if (greeter.test_mode) return session_internal_name == "rdp"; var found = false; @@ -1004,13 +1020,14 @@ public class UserList : GreeterList private void fill_list () { - if (ArcticaGreeter.singleton.test_mode) + var greeter = new ArcticaGreeter (); + if (greeter.test_mode) test_fill_list (); else { - default_session = ArcticaGreeter.singleton.default_session_hint (); - always_show_manual = ArcticaGreeter.singleton.show_manual_login_hint (); - if (!ArcticaGreeter.singleton.hide_users_hint ()) + default_session = greeter.default_session_hint (); + always_show_manual = greeter.show_manual_login_hint (); + if (!greeter.hide_users_hint ()) { var users = LightDM.UserList.get_instance (); users.user_added.connect (user_added_cb); @@ -1020,7 +1037,7 @@ public class UserList : GreeterList user_added_cb (user); } - if (ArcticaGreeter.singleton.has_guest_account_hint ()) + if (greeter.has_guest_account_hint ()) { debug ("Adding guest account entry"); offer_guest = true; @@ -1030,9 +1047,9 @@ public class UserList : GreeterList if (!have_entries ()) add_manual_entry (); - var last_user = ArcticaGreeter.singleton.get_state ("last-user"); - if (ArcticaGreeter.singleton.select_user_hint () != null) - set_active_entry (ArcticaGreeter.singleton.select_user_hint ()); + var last_user = greeter.get_state ("last-user"); + if (greeter.select_user_hint () != null) + set_active_entry (greeter.select_user_hint ()); else if (last_user != null) set_active_entry (last_user); } @@ -1098,39 +1115,40 @@ public class UserList : GreeterList { if (selected_entry.id.has_prefix ("*remote_login")) { + var greeter = new ArcticaGreeter (); if ((text == pam_x2go.PROMPT_USER) || (text == pam_freerdp2.PROMPT_USER)) { Gtk.Entry field = current_remote_fields.get ("username") as Gtk.Entry; var answer = field != null ? field.text : ""; debug ("remote_login prompt parsing: username -> %s", answer); - ArcticaGreeter.singleton.respond (answer); + greeter.respond (answer); } else if ((text == pam_x2go.PROMPT_PASSWORD) || (text == pam_freerdp2.PROMPT_PASSWORD)) { Gtk.Entry field = current_remote_fields.get ("password") as Gtk.Entry; var answer = field != null ? field.text : ""; debug ("remote_login prompt parsing: password -> <hidden>"); - ArcticaGreeter.singleton.respond (answer); + greeter.respond (answer); } else if ((text == pam_x2go.PROMPT_HOST) || (text == pam_freerdp2.PROMPT_HOST)) { var answer = url_from_remote_loding_server_list_name (selected_entry.id); debug ("remote_login prompt parsing: host -> %s", answer); - ArcticaGreeter.singleton.respond (answer); + greeter.respond (answer); } else if (text == pam_freerdp2.PROMPT_DOMAIN) { Gtk.Entry field = current_remote_fields.get ("domain") as Gtk.Entry; var answer = field != null ? field.text : ""; debug ("remote_login prompt parsing: domain -> %s", answer); - ArcticaGreeter.singleton.respond (answer); + greeter.respond (answer); } else if (text == pam_x2go.PROMPT_COMMAND) { Gtk.Entry field = current_remote_fields.get ("command") as Gtk.Entry; var answer = field != null ? field.text : ""; debug ("remote_login prompt parsing: command -> %s", answer); - ArcticaGreeter.singleton.respond (answer); + greeter.respond (answer); } } else @@ -1210,7 +1228,8 @@ public class UserList : GreeterList { } - if (!ArcticaGreeter.singleton.hide_users_hint()) + var greeter = new ArcticaGreeter (); + if (!greeter.hide_users_hint()) while (add_test_entry ()); /* add a manual entry if the list of entries is empty initially */ @@ -1221,12 +1240,12 @@ public class UserList : GreeterList n_test_entries++; } - offer_guest = ArcticaGreeter.singleton.has_guest_account_hint(); - always_show_manual = ArcticaGreeter.singleton.show_manual_login_hint(); + offer_guest = greeter.has_guest_account_hint(); + always_show_manual = greeter.show_manual_login_hint(); key_press_event.connect (test_key_press_cb); - if (ArcticaGreeter.singleton.show_remote_login_hint()) + if (greeter.show_remote_login_hint()) Timeout.add (1000, () => { RemoteServer[] test_server_list = {}; @@ -1248,7 +1267,7 @@ public class UserList : GreeterList return false; }); - var last_user = ArcticaGreeter.singleton.get_state ("last-user"); + var last_user = greeter.get_state ("last-user"); if (last_user != null) set_active_entry (last_user); diff --git a/src/util.vala b/src/util.vala new file mode 100644 index 0000000..6b82f50 --- /dev/null +++ b/src/util.vala @@ -0,0 +1,28 @@ +/* -*- Mode: Vala; indent-tabs-mode: nil; tab-width: 4 -*- + * + * Copyright (C) 2011,2012 Canonical Ltd + * Copyright (C) 2015-2017 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> + * Copyright (C) 2022 Mihai Moldovan <ionic@ionic.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * 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 <http://www.gnu.org/licenses/>. + * + * Authors: Robert Ancell <robert.ancell@canonical.com> + * Michael Terry <michael.terry@canonical.com> + * Mike Gabriel <mike.gabriel@das-netzwerkteam.de> + * Mihai Moldovan <ionic@ionic.de> + */ + +#if !HAVE_GTK_4_0 +[CCode(cname = "GTK_IS_CONTAINER", cheader_filename="gtk/gtk.h", simple_generics = true, has_target = false)] +static extern bool gtk_is_container<T> (T widget); +#endif diff --git a/tests/arctica-greeter.vala b/tests/arctica-greeter.vala index 2a6581b..3bcfeb8 100644 --- a/tests/arctica-greeter.vala +++ b/tests/arctica-greeter.vala @@ -19,7 +19,8 @@ public const int grid_size = 40; -public class ArcticaGreeter +[SingleInstance] +public class ArcticaGreeter : Object { public static ArcticaGreeter singleton; @@ -27,11 +28,16 @@ public class ArcticaGreeter public signal void show_prompt (string text, LightDM.PromptType type); public signal void authentication_complete (); - public bool test_mode = false; + public bool test_mode { get; construct; default = false; } public bool session_started = false; public string last_respond_response; public bool orca_needs_kick; + public ArcticaGreeter (bool test_mode_ = false) + { + Object (test_mode: test_mode_); + } + public bool is_authenticated () { return false; diff --git a/tests/menubar.vala b/tests/menubar.vala index f222957..4162dc0 100644 --- a/tests/menubar.vala +++ b/tests/menubar.vala @@ -19,7 +19,6 @@ public class MenuBar : Gtk.MenuBar { public const int HEIGHT = 32; - public bool high_contrast { get; private set; default = false; } public MenuBar (Background bg, Gtk.AccelGroup ag) { diff --git a/tests/test.vala b/tests/test.vala index b129bfd..9348b82 100644 --- a/tests/test.vala +++ b/tests/test.vala @@ -137,6 +137,21 @@ public class Test } } + public static void greeter_test_mode () + { + var greeter = new ArcticaGreeter (); + + /* + * Test that fetching the greeter singleton worked, even though we use + * the default value for test mode (false). + */ + GLib.assert (true == greeter.test_mode); + + // And explicitly try to override it, too. + greeter = new ArcticaGreeter (false); + GLib.assert (true == greeter.test_mode); + } + public static void simple_navigation () { MainWindow mw = setup (); @@ -486,11 +501,12 @@ public class Test GLib.assert (list.selected_entry.id == "*remote_login*http://rdpdefaultusername2.com*lwola"); wait_for_scrolling_end (list); - ArcticaGreeter.singleton.session_started = false; + var greeter = new ArcticaGreeter (); + greeter.session_started = false; pwd = remote_login_entry_password_field (list); pwd.text = "password"; list.selected_entry.respond ({}); - GLib.assert (ArcticaGreeter.singleton.session_started); + GLib.assert (greeter.session_started); mw.hide (); } @@ -516,7 +532,8 @@ public class Test GLib.assert (list.selected_entry.id == "*remote_login*http://rdpdefaultusername2.com*lwola"); wait_for_scrolling_end (list); - ArcticaGreeter.singleton.session_started = false; + var greeter = new ArcticaGreeter (); + greeter.session_started = false; pwd = remote_login_entry_password_field (list); pwd.text = "delay"; pwd.activate (); @@ -650,14 +667,15 @@ public class Test username.text = "bar"; pwd.text = "foobar"; - ArcticaGreeter.singleton.show_prompt("remote login:", LightDM.PromptType.QUESTION); - GLib.assert (ArcticaGreeter.singleton.last_respond_response == username.text); - ArcticaGreeter.singleton.show_prompt("remote host:", LightDM.PromptType.QUESTION); - GLib.assert (ArcticaGreeter.singleton.last_respond_response == "http://coolrdpserver.com"); - ArcticaGreeter.singleton.show_prompt("domain:", LightDM.PromptType.QUESTION); - GLib.assert (ArcticaGreeter.singleton.last_respond_response == domain.text); - ArcticaGreeter.singleton.show_prompt("password:", LightDM.PromptType.SECRET); - GLib.assert (ArcticaGreeter.singleton.last_respond_response == pwd.text); + var greeter = new ArcticaGreeter (); + greeter.show_prompt("remote login:", LightDM.PromptType.QUESTION); + GLib.assert (greeter.last_respond_response == username.text); + greeter.show_prompt("remote host:", LightDM.PromptType.QUESTION); + GLib.assert (greeter.last_respond_response == "http://coolrdpserver.com"); + greeter.show_prompt("domain:", LightDM.PromptType.QUESTION); + GLib.assert (greeter.last_respond_response == domain.text); + greeter.show_prompt("password:", LightDM.PromptType.SECRET); + GLib.assert (greeter.last_respond_response == pwd.text); mw.hide (); } @@ -690,15 +708,16 @@ public class Test public static void remote_login_only () { - ArcticaGreeter.singleton.test_mode = true; - ArcticaGreeter.singleton.session_started = false; + var greeter = new ArcticaGreeter (); + greeter.test_mode = true; + greeter.session_started = false; /* this configuration should result in the list containing only the remote login entry, without any fallback manual entry */ - ArcticaGreeter.singleton._hide_users_hint = true; - ArcticaGreeter.singleton._show_remote_login_hint = true; - ArcticaGreeter.singleton._has_guest_account_hint = false; - ArcticaGreeter.singleton._show_manual_login_hint = false; + greeter._hide_users_hint = true; + greeter._show_remote_login_hint = true; + greeter._has_guest_account_hint = false; + greeter._show_manual_login_hint = false; MainWindow mw = setup (); TestList list = mw.stack.top () as TestList; @@ -725,14 +744,15 @@ public class Test public static void manual_login_fallback () { - ArcticaGreeter.singleton.test_mode = true; - ArcticaGreeter.singleton.session_started = false; + var greeter = new ArcticaGreeter (); + greeter.test_mode = true; + greeter.session_started = false; /* this configuration should result in the list containing at least a manual entry */ - ArcticaGreeter.singleton._hide_users_hint = true; - ArcticaGreeter.singleton._show_remote_login_hint = false; - ArcticaGreeter.singleton._has_guest_account_hint = false; - ArcticaGreeter.singleton._show_manual_login_hint = true; + greeter._hide_users_hint = true; + greeter._show_remote_login_hint = false; + greeter._has_guest_account_hint = false; + greeter._show_manual_login_hint = true; MainWindow mw = setup (); TestList list = mw.stack.top () as TestList; @@ -782,9 +802,9 @@ public class Test setup_gsettings (); - ArcticaGreeter.singleton = new ArcticaGreeter(); - ArcticaGreeter.singleton.test_mode = true; + var greeter = new ArcticaGreeter (true); + GLib.Test.add_func ("/Greeter Test Mode", greeter_test_mode); GLib.Test.add_func ("/Simple Navigation", simple_navigation); GLib.Test.add_func ("/Remote Login", remote_login); GLib.Test.add_func ("/Remote Login duplicate entries", remote_login_duplicate_entries); |