aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Gabriel <mike.gabriel@das-netzwerkteam.de>2023-02-06 10:50:34 +0100
committerMike Gabriel <mike.gabriel@das-netzwerkteam.de>2023-02-06 10:50:34 +0100
commit642cbe7d3830c7dd27230ea84e86277243ccd7b7 (patch)
tree6d4f269e0389b8f12d267cb715dc663382763c82
parentb2fac08944adadd4b278a58a5fe363ef0df1e9c2 (diff)
parentbf00dadc0584d911bf4c23cc4ab25e8a09c611ce (diff)
downloadarctica-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.ac10
-rw-r--r--data/org.ArcticaProject.arctica-greeter.gschema.xml12
-rw-r--r--src/Makefile.am3
-rw-r--r--src/arctica-greeter.vala150
-rw-r--r--src/background.vala3
-rw-r--r--src/dash-box.vala22
-rw-r--r--src/dash-entry.vala5
-rw-r--r--src/greeter-list.vala37
-rw-r--r--src/main-window.vala13
-rw-r--r--src/menubar.vala198
-rw-r--r--src/prompt-box.vala188
-rw-r--r--src/session-list.vala3
-rw-r--r--src/settings.vala71
-rw-r--r--src/toggle-box.vala43
-rw-r--r--src/user-list.vala95
-rw-r--r--src/util.vala28
-rw-r--r--tests/arctica-greeter.vala10
-rw-r--r--tests/menubar.vala1
-rw-r--r--tests/test.vala70
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);