aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/animate-timer.vala4
-rw-r--r--src/arctica-greeter.vala598
-rw-r--r--src/background.vala69
-rw-r--r--src/cached-image.vala35
-rw-r--r--src/dash-box.vala23
-rw-r--r--src/greeter-list.vala55
-rw-r--r--src/list-stack.vala6
-rw-r--r--src/main-window.vala314
-rw-r--r--src/menubar.vala273
-rw-r--r--src/prompt-box.vala192
-rw-r--r--src/session-list.vala184
-rw-r--r--src/settings-daemon.vala69
-rw-r--r--src/settings.vala35
-rw-r--r--src/shutdown-dialog.vala10
-rw-r--r--src/user-list.vala44
16 files changed, 1358 insertions, 555 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index bb91f59..7ddcf57 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -91,7 +91,5 @@ arctica_greeter_LDADD = \
logo_generator_LDADD = $(arctica_greeter_LDADD)
-arctica_greeter_vala.stamp: $(top_srcdir)/config.h
-
DISTCLEANFILES = \
Makefile.in
diff --git a/src/animate-timer.vala b/src/animate-timer.vala
index 9f92448..4879cb0 100644
--- a/src/animate-timer.vala
+++ b/src/animate-timer.vala
@@ -2,6 +2,7 @@
*
* Copyright (C) 2011,2012 Canonical Ltd
* Copyright (C) 2015 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
+ * Copyright (C) 2025 Robert Tari
*
* 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
@@ -18,6 +19,7 @@
* Authors: Robert Ancell <robert.ancell@canonical.com>
* Michael Terry <michael.terry@canonical.com>
* Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
+ * Robert Tari <robert@tari.in>
*/
private class AnimateTimer : Object
@@ -32,7 +34,7 @@ private class AnimateTimer : Object
public const int SLOW = 1000; /* Good for animations that convey information that is only presented in the animation */
/* speed is in milliseconds */
- public unowned EasingFunc easing_func { get; private set; }
+ public unowned EasingFunc easing_func;
public int speed { get; set; }
public bool is_running { get { return timeout != 0; } }
public double progress { get; private set; }
diff --git a/src/arctica-greeter.vala b/src/arctica-greeter.vala
index a2aa59e..5eb8a59 100644
--- a/src/arctica-greeter.vala
+++ b/src/arctica-greeter.vala
@@ -2,7 +2,7 @@
*
* Copyright (C) 2011 Canonical Ltd
* Copyright (C) 2015-2017 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
- * Copyright (C) 2023-2024 Robert Tari
+ * Copyright (C) 2023-2025 Robert Tari
*
* 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
@@ -21,7 +21,6 @@
* Robert Tari <robert@tari.in>
*/
-public const int grid_size = 40;
[SingleInstance]
public class ArcticaGreeter : Object
@@ -35,6 +34,12 @@ public class ArcticaGreeter : Object
public Gtk.Window? pMagnifierWindow { get; set; default = null; }
public bool test_mode { get; construct; default = false; }
public bool test_highcontrast { get; construct; default = false; }
+
+ // Menubar is smaller, but with shadow, we reserve more space
+ public int menubar_height { get; set; default = 32 + 8; }
+ public int grid_size { get; set; default = 40; }
+ public double scaling_factor_widgets { get; set; default = 1; }
+
private string state_file;
private KeyFile state;
private DBusServer pServer;
@@ -54,6 +59,10 @@ public class ArcticaGreeter : Object
public signal void xsettings_ready ();
public signal void greeter_ready ();
+ public List<Pid> indicator_service_pids;
+ Pid notificationdaemon_pid = 0;
+ Pid windowmanager_pid = 0;
+
construct
{
Bus.own_name (BusType.SESSION, "org.ayatana.greeter", BusNameOwnerFlags.NONE, onBusAcquired);
@@ -102,14 +111,19 @@ public class ArcticaGreeter : Object
state_file = Path.build_filename (state_dir, state_file_name);
state = new KeyFile ();
- try
- {
- state.load_from_file (state_file, KeyFileFlags.NONE);
+ if (FileUtils.test (state_file, FileTest.EXISTS)) {
+ try
+ {
+ state.load_from_file (state_file, KeyFileFlags.NONE);
+ }
+ catch (Error e)
+ {
+ if (!(e is FileError.NOENT))
+ warning ("Failed to load state from %s: %s\n", state_file, e.message);
+ }
}
- catch (Error e)
- {
- if (!(e is FileError.NOENT))
- warning ("Failed to load state from %s: %s\n", state_file, e.message);
+ else {
+ debug ("State file does not (yet) exist: %s\n", state_file);
}
}
@@ -246,58 +260,75 @@ public class ArcticaGreeter : Object
public string? get_default_session ()
{
- var sessions = new List<string> ();
+ var available_sessions = new List<string> ();
var hide_default_xsession = AGSettings.get_boolean (AGSettings.KEY_HIDE_DEFAULT_XSESSION);
+
+ /* Debian/Ubuntu style of defining the default xsession.
+ */
if (!hide_default_xsession) {
- sessions.append ("lightdm-xsession");
+ var default_session_path_suse = Path.build_filename ("/usr/share/xsessions/default.desktop", null);
+ var default_session_path_deb = Path.build_filename ("/usr/share/xsessions/lightdm-xsession.desktop", null);
+ if (FileUtils.test (default_session_path_suse, FileTest.EXISTS)) {
+ /* openSUSE/SLED style of defining the default xsession.
+ */
+ available_sessions.append ("default");
+ }
+ else if (FileUtils.test (default_session_path_deb, FileTest.EXISTS)) {
+ /* Debian/Ubuntu style of defining the default xsession.
+ */
+ available_sessions.append ("lightdm-xsession");
+ }
}
var preferred_sessions = AGSettings.get_strv (AGSettings.KEY_PREFERRED_SESSIONS);
+ if (preferred_sessions.length > 0) {
+ foreach (var preferred_session in preferred_sessions) {
+ available_sessions.append (preferred_session);
+ }
- foreach (var preferred_session in preferred_sessions) {
- sessions.append (preferred_session);
- }
-
- var excluded_sessions = AGSettings.get_strv (AGSettings.KEY_EXCLUDED_SESSIONS);
- var includeonly_sessions = AGSettings.get_strv (AGSettings.KEY_INCLUDEONLY_SESSIONS);
+ var excluded_sessions = AGSettings.get_strv (AGSettings.KEY_EXCLUDED_SESSIONS);
+ var includeonly_sessions = AGSettings.get_strv (AGSettings.KEY_INCLUDEONLY_SESSIONS);
- if (!AGSettings.get_boolean (AGSettings.KEY_HIDE_WAYLAND_SESSIONS)) {
- foreach (string session in sessions) {
- if (includeonly_sessions.length > 0) {
- if (!(session in includeonly_sessions)) {
+ if (!AGSettings.get_boolean (AGSettings.KEY_HIDE_WAYLAND_SESSIONS)) {
+ foreach (string session in available_sessions) {
+ if (includeonly_sessions.length > 0) {
+ if (!(session in includeonly_sessions)) {
+ continue;
+ }
+ }
+ else if (session in excluded_sessions) {
continue;
}
- }
- else if (session in excluded_sessions) {
- continue;
- }
- var path = Path.build_filename ("/usr/share/wayland-sessions/", session.concat(".desktop"), null);
- if (FileUtils.test (path, FileTest.EXISTS)) {
- debug ("Using %s as default (Wayland) session.", session);
- return session;
+ var path = Path.build_filename ("/usr/share/wayland-sessions/", session.concat(".desktop"), null);
+ if (FileUtils.test (path, FileTest.EXISTS)) {
+ debug ("Using %s as default (Wayland) session.", session);
+ return session;
+ }
}
}
- }
- if (!AGSettings.get_boolean (AGSettings.KEY_HIDE_X11_SESSIONS)) {
- foreach (string session in sessions) {
- if (includeonly_sessions.length > 0) {
- if (!(session in includeonly_sessions)) {
+ if (!AGSettings.get_boolean (AGSettings.KEY_HIDE_X11_SESSIONS)) {
+ foreach (string session in available_sessions) {
+ if (includeonly_sessions.length > 0) {
+ if (!(session in includeonly_sessions)) {
+ continue;
+ }
+ }
+ else if (session in excluded_sessions) {
continue;
}
- }
- else if (session in excluded_sessions) {
- continue;
- }
- var path = Path.build_filename ("/usr/share/xsessions/", session.concat(".desktop"), null);
- if (FileUtils.test (path, FileTest.EXISTS)) {
- debug ("Using %s as default (X11) session.", session);
- return session;
+ var path = Path.build_filename ("/usr/share/xsessions/", session.concat(".desktop"), null);
+ if (FileUtils.test (path, FileTest.EXISTS)) {
+ debug ("Using %s as default (X11) session.", session);
+ return session;
+ }
}
}
+
+ warning ("Could not find a default session. Falling back to LightDM's system default.");
}
- warning ("Could not find a default session. Falling back to LightDM's system default.");
+ warning ("Using default session '%s' as configured as LightDM's system default.", greeter.default_session_hint);
return greeter.default_session_hint;
}
@@ -467,9 +498,11 @@ public class ArcticaGreeter : Object
debug ("Showing main window");
if (!test_mode)
main_window.set_decorated (false);
+ main_window.set_keep_below (true);
main_window.realize ();
main_window.setup_window ();
main_window.show ();
+ main_window.set_struts ();
main_window.get_window ().focus (Gdk.CURRENT_TIME);
try
@@ -668,7 +701,6 @@ public class ArcticaGreeter : Object
// Now check to see if this is the magnifier - no focus for it, either
X.Window nMagnifier = 0;
-
if (this.pMagnifierWindow != null)
{
Gdk.X11.Window pWindow = (Gdk.X11.Window) this.pMagnifierWindow.get_window ();
@@ -677,6 +709,7 @@ public class ArcticaGreeter : Object
if (xwin != keyboard_xid && xwin != nMagnifier && win.get_type_hint() != Gdk.WindowTypeHint.NOTIFICATION)
{
+ win.set_keep_below (true);
win.focus (Gdk.CURRENT_TIME);
/* Make sure to keep keyboard above */
@@ -803,7 +836,10 @@ public class ArcticaGreeter : Object
main_window = new MainWindow ();
- main_window.destroy.connect(() => { kill_fake_wm (); });
+ main_window.destroy.connect(() => {
+ stop_real_wm ();
+ kill_fake_wm ();
+ });
main_window.delete_event.connect(() =>
{
Gtk.main_quit();
@@ -846,6 +882,7 @@ public class ArcticaGreeter : Object
() => debug ("Failed to acquire name com.lomiri.Shell"));
}
+ start_real_wm ();
start_fake_wm ();
Gdk.threads_add_idle (ready_cb);
greeter_ready ();
@@ -932,6 +969,224 @@ public class ArcticaGreeter : Object
return ret;
}
+ public void start_indicators ()
+ {
+ if (!test_mode)
+ {
+ var indicator_list = AGSettings.get_strv(AGSettings.KEY_INDICATORS);
+
+ var update_indicator_list = false;
+ for (var i = 0; i < indicator_list.length; i++)
+ {
+ if (indicator_list[i] == "ug-keyboard")
+ {
+ indicator_list[i] = "org.ayatana.indicator.keyboard";
+ update_indicator_list = true;
+ }
+ }
+
+ if (update_indicator_list)
+ AGSettings.set_strv(AGSettings.KEY_INDICATORS, indicator_list);
+
+ var indicator_service = "";
+ foreach (unowned string indicator in indicator_list)
+ {
+ Pid indicator_service_pid = 0;
+
+ if ("ug-" in indicator && ! ("." in indicator))
+ continue;
+
+ if ("org.ayatana.indicator." in indicator)
+ indicator_service = "ayatana-indicator-%s".printf(indicator.split_set(".")[3]);
+ else if ("ayatana-" in indicator)
+ indicator_service = "ayatana-indicator-%s".printf(indicator.split_set("-")[1]);
+ else
+ indicator_service = indicator;
+
+ try {
+ /* Start the indicator service */
+ string[] argv = null;
+
+ /* FIXME: This path is rather hard-coded here.
+ * If it pops up, we need to handle this in
+ * some path detection fashion similar to
+ * how we find at-spi-bus-launcher on the file
+ * system.
+ */
+ if (FileUtils.test ("/usr/lib/%s/%s-service".printf(indicator_service, indicator_service), FileTest.EXISTS))
+ Shell.parse_argv ("/usr/lib/%s/%s-service".printf(indicator_service, indicator_service), out argv);
+ else if (FileUtils.test ("/usr/libexec/%s/%s-service".printf(indicator_service, indicator_service), FileTest.EXISTS))
+ Shell.parse_argv ("/usr/libexec/%s/%s-service".printf(indicator_service, indicator_service), out argv);
+ if (argv != null)
+ {
+ Process.spawn_async (null,
+ argv,
+ null,
+ SpawnFlags.SEARCH_PATH,
+ null,
+ out indicator_service_pid);
+ indicator_service_pids.append(indicator_service_pid);
+ debug ("Successfully started Ayatana Indicator Service '%s' [%d]", indicator_service, indicator_service_pid);
+ }
+ else
+ {
+ warning ("Could not find indicator service executable for Indicator Service '%s'", indicator_service);
+ }
+ }
+ catch (Error e)
+ {
+ warning ("Error starting Indicator Service '%s': %s", indicator_service, e.message);
+ }
+
+ }
+ }
+ }
+
+ public void stop_indicators ()
+ {
+ foreach (unowned Pid indicator_service_pid in indicator_service_pids)
+ {
+ if (indicator_service_pid != 0)
+ {
+#if VALA_0_40
+ Posix.kill (indicator_service_pid, Posix.Signal.TERM);
+#else
+ Posix.kill (indicator_service_pid, Posix.SIGTERM);
+#endif
+
+ int status;
+ Posix.waitpid (indicator_service_pid, out status, 0);
+ if (Process.if_exited (status))
+ debug ("Indicator Service process [%d] exited with return value %d", indicator_service_pid, Process.exit_status (status));
+ else
+ debug ("Indicator Service process [%d] terminated with signal %d", indicator_service_pid, Process.term_sig (status));
+ indicator_service_pid = 0;
+ }
+ }
+ }
+
+ public void start_notification_daemon ()
+ {
+ if (!test_mode)
+ {
+ try
+ {
+ string[] argv = null;
+
+ if (FileUtils.test ("/usr/lib/mate-notification-daemon/mate-notification-daemon", FileTest.EXISTS)) {
+ Shell.parse_argv ("/usr/lib/mate-notification-daemon/mate-notification-daemon --replace", out argv);
+ }
+ else if (FileUtils.test ("/usr/libexec/mate-notification-daemon/mate-notification-daemon", FileTest.EXISTS)) {
+ Shell.parse_argv ("/usr/libexec/mate-notification-daemon/mate-notification-daemon --replace", out argv);
+ }
+ if (argv != null)
+ Process.spawn_async (null,
+ argv,
+ null,
+ SpawnFlags.SEARCH_PATH,
+ null,
+ out notificationdaemon_pid);
+ debug ("Launched mate-notification-daemon. PID: %d", notificationdaemon_pid);
+ }
+ catch (Error e)
+ {
+ warning ("Error starting the mate-notification-daemon registry: %s", e.message);
+ }
+ }
+ }
+
+ public void stop_notification_daemon ()
+ {
+ if (notificationdaemon_pid != 0)
+ {
+#if VALA_0_40
+ Posix.kill (notificationdaemon_pid, Posix.Signal.KILL);
+#else
+ Posix.kill (notificationdaemon_pid, Posix.SIGKILL);
+#endif
+ int status;
+ Posix.waitpid (notificationdaemon_pid, out status, 0);
+ if (Process.if_exited (status))
+ debug ("mate-notification-daemon exited with return value %d", Process.exit_status (status));
+ else
+ debug ("mate-notification-daemon terminated with signal %d", Process.term_sig (status));
+ notificationdaemon_pid = 0;
+ }
+ }
+
+ public void start_real_wm ()
+ {
+ if (!test_mode)
+ {
+ string wm = AGSettings.get_string (AGSettings.KEY_WINDOW_MANAGER);
+ if ((wm == "metacity") || (wm == "marco"))
+ {
+ try
+ {
+ string[] argv;
+
+ Shell.parse_argv (wm, out argv);
+ Process.spawn_async (null,
+ argv,
+ null,
+ SpawnFlags.SEARCH_PATH,
+ null,
+ out windowmanager_pid);
+ debug ("Launched '%s' WM. PID: %d", wm, windowmanager_pid);
+ }
+ catch (Error e)
+ {
+ warning ("Error starting the '%s' Window Manager: %s", wm, e.message);
+ }
+
+ Timeout.add (50, () =>
+ {
+ try
+ {
+ string[] argv;
+
+ Shell.parse_argv ("%s-message disable-keybindings".printf(wm), out argv);
+
+ Process.spawn_sync (null,
+ argv,
+ null,
+ SpawnFlags.SEARCH_PATH,
+ null,
+ null,
+ null,
+ null);
+ debug ("Launched '%s-message disable-keybindings' command", wm);
+ return false;
+ }
+ catch (Error e)
+ {
+ warning ("Error during '%s-message disable-keybindings' command call: %s", wm, e.message);
+ return true;
+ }
+ });
+ }
+ }
+ }
+
+ public void stop_real_wm ()
+ {
+ if (windowmanager_pid != 0)
+ {
+#if VALA_0_40
+ Posix.kill (windowmanager_pid, Posix.Signal.TERM);
+#else
+ Posix.kill (windowmanager_pid, Posix.SIGTERM);
+#endif
+ int status;
+ Posix.waitpid (windowmanager_pid, out status, 0);
+ if (Process.if_exited (status))
+ debug ("Window Manager exited with return value %d", Process.exit_status (status));
+ else
+ debug ("Window Manager terminated with signal %d", Process.term_sig (status));
+ windowmanager_pid = 0;
+ }
+ }
+
public static int main (string[] args)
{
/* Protect memory from being paged to disk, as we deal with passwords
@@ -964,18 +1219,19 @@ public class ArcticaGreeter : Object
/* Initialize i18n */
Intl.setlocale (LocaleCategory.ALL, "");
+ Intl.setlocale(LocaleCategory.NUMERIC, "C.UTF-8");
Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8");
Intl.textdomain (Config.GETTEXT_PACKAGE);
/* Set up the accessibility stack, in case the user needs it for screen reading etc. */
- Environment.set_variable ("GTK_MODULES", "atk-bridge", false);
+ AGUtils.greeter_set_env ("GTK_MODULES", "atk-bridge");
/* Fix for https://bugs.launchpad.net/ubuntu/+source/unity-greeter/+bug/1024482
- Slick-greeter sets the mouse cursor on the root window.
+ arctica-greeter sets the mouse cursor on the root window.
Without GDK_CORE_DEVICE_EVENTS set, the DE is unable to apply its own cursor theme and size.
*/
- GLib.Environment.set_variable ("GDK_CORE_DEVICE_EVENTS", "1", true);
+ AGUtils.greeter_set_env ("GDK_CORE_DEVICE_EVENTS", "1");
log_timer = new Timer ();
Log.set_default_handler (log_cb);
@@ -997,7 +1253,7 @@ public class ArcticaGreeter : Object
}
/* Adjust GDK_SCALE to our configured scaling factor (via HiDPI settings). */
debug ("Setting GDK_SCALE to: %d (scaling all UI elements by this factor)", scaling_factor_hidpi);
- GLib.Environment.set_variable ("GDK_SCALE", "%d".printf (scaling_factor_hidpi), true);
+ AGUtils.greeter_set_env ("GDK_SCALE", "%d".printf (scaling_factor_hidpi));
/* Font scaling settings */
var scaling_factor_fonts = AGSettings.get_double (AGSettings.KEY_FONT_SCALING);
@@ -1005,10 +1261,31 @@ public class ArcticaGreeter : Object
/* Adjust GDK_SCALE / GDK_DPI_SCALE to our configured scaling factors. */
debug ("Setting GDK_DPI_SCALE to: %f (scaling fonts only by this factor)", scaling_factor_fonts);
- GLib.Environment.set_variable ("GDK_DPI_SCALE", "%f".printf (scaling_factor_fonts), true);
+ AGUtils.greeter_set_env ("GDK_DPI_SCALE", "%f".printf (scaling_factor_fonts));
/* Make nm-applet hide items the user does not have permissions to interact with */
- Environment.set_variable ("NM_APPLET_HIDE_POLICY_ITEMS", "1", true);
+ AGUtils.greeter_set_env ("NM_APPLET_HIDE_POLICY_ITEMS", "1");
+
+ /* Set indicators to run with reduced functionality */
+ AGUtils.greeter_set_env ("INDICATOR_GREETER_MODE", "1");
+
+ /* Don't allow virtual file systems? */
+ AGUtils.greeter_set_env ("GIO_USE_VFS", "local");
+ AGUtils.greeter_set_env ("GVFS_DISABLE_FUSE", "1");
+
+ /* Hint to have onboard running in greeter mode */
+ AGUtils.greeter_set_env ("RUNNING_UNDER_GDM", "1");
+
+ /* Let indicators know about our unique dbus name */
+ try
+ {
+ var conn = Bus.get_sync (BusType.SESSION);
+ AGUtils.greeter_set_env ("ARCTICA_GREETER_DBUS_NAME", conn.get_unique_name ());
+ }
+ catch (IOError e)
+ {
+ debug ("Could not set DBUS_NAME: %s", e.message);
+ }
bool do_show_version = false;
bool do_test_mode = false;
@@ -1039,6 +1316,7 @@ public class ArcticaGreeter : Object
* GLib.Environment.set_variable() calls won't take effect (for
* whatever unknown reason...) if they get issued after the c.parse()
* method call on our OptionContext object (see a few lines below).
+ * Same applies to AGUtils.set_greeter_env().
*
* To mitigate this (strange) behaviour, make sure that all env
* variable setups in main() are located above this comment.
@@ -1071,6 +1349,13 @@ public class ArcticaGreeter : Object
}
}
+ Pid atspi_pid = 0;
+ Pid nmapplet_pid = 0;
+ Pid geoclueagent_pid = 0;
+
+ var gsettings_mate_desktop_interface = new Settings ("org.mate.interface");
+ int wsf_orig = 0;
+
if (!do_test_mode) {
/* Set the keyboard layout */
set_keyboard_layout ();
@@ -1080,11 +1365,9 @@ public class ArcticaGreeter : Object
debug ("Activating numlock");
activate_numlock ();
}
- }
- Pid atspi_pid = 0;
- if (!do_test_mode)
- {
+ wsf_orig = gsettings_mate_desktop_interface.get_int ("window-scaling-factor");
+ gsettings_mate_desktop_interface.set_int ("window-scaling-factor", 1);
try
{
@@ -1109,39 +1392,39 @@ public class ArcticaGreeter : Object
{
warning ("Error starting the at-spi registry: %s", e.message);
}
- }
- Pid geoclueagent_pid = 0;
- if (AGSettings.get_boolean (AGSettings.KEY_GEOCLUE_AGENT) && (!do_test_mode))
- {
-
- try
+ if (AGSettings.get_boolean (AGSettings.KEY_GEOCLUE_AGENT))
{
- string[] argv = null;
- if (FileUtils.test ("/usr/lib/geoclue-2.0/demos/agent", FileTest.EXISTS)) {
- Shell.parse_argv ("/usr/lib/geoclue-2.0/demos/agent", out argv);
+ try
+ {
+ string[] argv = null;
+
+ if (FileUtils.test ("/usr/lib/geoclue-2.0/demos/agent", FileTest.EXISTS)) {
+ Shell.parse_argv ("/usr/lib/geoclue-2.0/demos/agent", out argv);
+ }
+ else if (FileUtils.test ("/usr/libexec/geoclue-2.0/demos/agent", FileTest.EXISTS)) {
+ Shell.parse_argv ("/usr/libexec/geoclue-2.0/demos/agent", out argv);
+ }
+ if (argv != null)
+ Process.spawn_async (null,
+ argv,
+ null,
+ SpawnFlags.SEARCH_PATH,
+ null,
+ out geoclueagent_pid);
+ debug ("Launched GeoClue-2.0 agent. PID: %d", geoclueagent_pid);
}
- else if (FileUtils.test ("/usr/libexec/geoclue-2.0/demos/agent", FileTest.EXISTS)) {
- Shell.parse_argv ("/usr/libexec/geoclue-2.0/demos/agent", out argv);
+ catch (Error e)
+ {
+ warning ("Error starting the GeoClue-2.0 agent: %s", e.message);
}
- if (argv != null)
- Process.spawn_async (null,
- argv,
- null,
- SpawnFlags.SEARCH_PATH,
- null,
- out geoclueagent_pid);
- debug ("Launched GeoClue-2.0 agent. PID: %d", geoclueagent_pid);
}
- catch (Error e)
- {
- warning ("Error starting the GeoClue-2.0 agent: %s", e.message);
- }
- }
- /* Enable touchpad tap-to-click */
- enable_tap_to_click ();
+ /* Enable touchpad tap-to-click */
+ enable_tap_to_click ();
+
+ }
Gtk.init (ref args);
Ido.init ();
@@ -1218,26 +1501,14 @@ public class ArcticaGreeter : Object
debug ("Creating Arctica Greeter");
var greeter = new ArcticaGreeter (do_test_mode, do_test_highcontrast);
- greeter.go();
-
- Pid nmapplet_pid = 0;
- var indicator_list = AGSettings.get_strv(AGSettings.KEY_INDICATORS);
+ /* Widget/UI scaling settings */
+ greeter.scaling_factor_widgets = AGSettings.get_double (AGSettings.KEY_WIDGET_SCALING);
+ debug ("Scaling factor for widgets / UI elements is: %f", greeter.scaling_factor_widgets);
+ greeter.menubar_height = (int)Math.round(greeter.menubar_height * greeter.scaling_factor_widgets);
+ greeter.grid_size = (int)Math.round(greeter.grid_size * greeter.scaling_factor_widgets);
- var update_indicator_list = false;
- for (var i = 0; i < indicator_list.length; i++)
- {
- if (indicator_list[i] == "ug-keyboard")
- {
- indicator_list[i] = "org.ayatana.indicator.keyboard";
- update_indicator_list = true;
- }
- }
-
- if (update_indicator_list)
- AGSettings.set_strv(AGSettings.KEY_INDICATORS, indicator_list);
-
- var launched_indicator_service_pids = new List<Pid>();
+ greeter.go();
if (!do_test_mode)
{
@@ -1249,58 +1520,6 @@ public class ArcticaGreeter : Object
greeter.show ();
});
- var indicator_service = "";
- foreach (unowned string indicator in indicator_list)
- {
- Pid indicator_service_pid = 0;
-
- if ("ug-" in indicator && ! ("." in indicator))
- continue;
-
- if ("org.ayatana.indicator." in indicator)
- indicator_service = "ayatana-indicator-%s".printf(indicator.split_set(".")[3]);
- else if ("ayatana-" in indicator)
- indicator_service = "ayatana-indicator-%s".printf(indicator.split_set("-")[1]);
- else
- indicator_service = indicator;
-
- try {
- /* Start the indicator service */
- string[] argv = null;
-
- /* FIXME: This path is rather hard-coded here.
- * If it pops up, we need to handle this in
- * some path detection fashion similar to
- * how we find at-spi-bus-launcher on the file
- * system.
- */
- if (FileUtils.test ("/usr/lib/%s/%s-service".printf(indicator_service, indicator_service), FileTest.EXISTS))
- Shell.parse_argv ("/usr/lib/%s/%s-service".printf(indicator_service, indicator_service), out argv);
- else if (FileUtils.test ("/usr/libexec/%s/%s-service".printf(indicator_service, indicator_service), FileTest.EXISTS))
- Shell.parse_argv ("/usr/libexec/%s/%s-service".printf(indicator_service, indicator_service), out argv);
- if (argv != null)
- {
- Process.spawn_async (null,
- argv,
- null,
- SpawnFlags.SEARCH_PATH,
- null,
- out indicator_service_pid);
- launched_indicator_service_pids.append(indicator_service_pid);
- debug ("Successfully started Ayatana Indicator Service '%s' [%d]", indicator_service, indicator_service_pid);
- }
- else
- {
- warning ("Could not find indicator service executable for Indicator Service '%s'", indicator_service);
- }
- }
- catch (Error e)
- {
- warning ("Error starting Indicator Service '%s': %s", indicator_service, e.message);
- }
-
- }
-
try
{
string[] argv;
@@ -1338,26 +1557,8 @@ public class ArcticaGreeter : Object
if (!do_test_mode)
{
- foreach (unowned Pid indicator_service_pid in launched_indicator_service_pids)
- {
- if (indicator_service_pid != 0)
- {
-#if VALA_0_40
- Posix.kill (indicator_service_pid, Posix.Signal.TERM);
-#else
- Posix.kill (indicator_service_pid, Posix.SIGTERM);
-#endif
-
- int status;
- Posix.waitpid (indicator_service_pid, out status, 0);
- if (Process.if_exited (status))
- debug ("Indicator Service process [%d] exited with return value %d", indicator_service_pid, Process.exit_status (status));
- else
- debug ("Indicator Service process [%d] terminated with signal %d", indicator_service_pid, Process.term_sig (status));
- indicator_service_pid = 0;
- }
- }
-
+ greeter.stop_indicators();
+ greeter.stop_notification_daemon();
greeter.settings_daemon.stop();
if (nmapplet_pid != 0)
@@ -1409,6 +1610,11 @@ public class ArcticaGreeter : Object
}
}
+ if (!do_test_mode)
+ {
+ gsettings_mate_desktop_interface.set_int ("window-scaling-factor", wsf_orig);
+ }
+
var screen = Gdk.Screen.get_default ();
Gdk.X11.Display pDisplay = (Gdk.X11.Display) screen.get_display ();
unowned X.Display xdisplay = pDisplay.get_xdisplay ();
@@ -1512,9 +1718,9 @@ public class DBusServer : Object
public string GetUser () throws GLib.DBusError, GLib.IOError
{
- string sUser = this.pGreeter.get_state ("last-user");
+ var sUser = this.pGreeter.get_state ("last-user");
- return sUser;
+ return (sUser != null) ? sUser : "*other";
}
public void SetLayout (string sLanguage, string sVariant) throws GLib.DBusError, GLib.IOError
@@ -1610,7 +1816,7 @@ public class DBusServer : Object
sThemeArgs = "--theme='%s'".printf (sThemePath);
}
- string sCommand = "onboard --xid %s %s".printf (sLayoutArgs, sThemeArgs);
+ string sCommand = "onboard --keep-aspect --launched-by=arctica-greeter --xid %s %s".printf (sLayoutArgs, sThemeArgs);
debug ("Launching OSK: '%s'", sCommand);
string[] lArgs;
@@ -1656,6 +1862,8 @@ public class DBusServer : Object
this.pGreeter.pKeyboardWindow.accept_focus = false;
this.pGreeter.pKeyboardWindow.focus_on_map = false;
this.pGreeter.pKeyboardWindow.set_title("OSK (theme: %s)".printf(sTheme));
+ this.pGreeter.pKeyboardWindow.set_decorated (false);
+ this.pGreeter.pKeyboardWindow.set_keep_above (true);
}
if ((this.pGreeter.pKeyboardWindow != null) && (pKeyboardSocket != null) && (nId != 0))
@@ -1666,7 +1874,10 @@ public class DBusServer : Object
debug ("Attaching new onboard process to OSK Gtk.Socket (+ Gtk.Window)");
pKeyboardSocket.add_id (nId);
+ }
+ if ((this.pGreeter.pKeyboardWindow != null) && (pKeyboardSocket != null) && bActive)
+ {
/* resize the keyboard window to cover the lower part of the screen */
debug ("Resizing OSK window.");
var pDisplay = this.pGreeter.main_window.get_display ();
@@ -1780,6 +1991,8 @@ public class DBusServer : Object
this.pGreeter.pMagnifierWindow.accept_focus = false;
this.pGreeter.pMagnifierWindow.focus_on_map = false;
this.pGreeter.pMagnifierWindow.set_title ("Magnifier");
+ this.pGreeter.pMagnifierWindow.set_decorated (false);
+ this.pGreeter.pMagnifierWindow.set_keep_above (true);
}
if ((this.pGreeter.pMagnifierWindow != null) && (pMagnifierSocket != null) && (nId != 0))
@@ -1791,6 +2004,43 @@ public class DBusServer : Object
pMagnifierSocket.add_id (nId);
}
+ if ((this.pGreeter.pMagnifierWindow != null) && (pMagnifierSocket != null) && bActive)
+ {
+ var greeter = new ArcticaGreeter();
+
+ /* resize and position the magnifier window */
+ debug ("Resizing and positioning Magnifier window.");
+ var pDisplay = this.pGreeter.main_window.get_display ();
+ var pMonitor = pDisplay.get_monitor_at_window (this.pGreeter.main_window.get_window ());
+ Gdk.Rectangle cRect = pMonitor.get_geometry ();
+ int magnifier_width = 2 * cRect.width / 5;
+ int magnifier_height = 2 * cRect.height / 5;
+ string sPosition = AGSettings.get_string (AGSettings.KEY_MAGNIFIER_POSITION);
+
+ if (sPosition == "top-left")
+ {
+ magnifier_width = (int) (magnifier_width * 0.75);
+ magnifier_height = (int) (magnifier_height * 0.75);
+ this.pGreeter.pMagnifierWindow.move (cRect.x + greeter.menubar_height, cRect.y + greeter.menubar_height * 2);
+ }
+ else if (sPosition == "top-right")
+ {
+ magnifier_width = (int) (magnifier_width * 0.75);
+ magnifier_height = (int) (magnifier_height * 0.75);
+ this.pGreeter.pMagnifierWindow.move (cRect.x + cRect.width - greeter.menubar_height - magnifier_width, cRect.y + greeter.menubar_height * 2);
+ }
+ else if (sPosition == "centre-left")
+ {
+ this.pGreeter.pMagnifierWindow.move (cRect.x + cRect.width / 10, cRect.y + cRect.height / 5 + cRect.height / 10);
+ }
+ else if (sPosition == "centre-right")
+ {
+ this.pGreeter.pMagnifierWindow.move (cRect.x + cRect.width - cRect.width / 10 - magnifier_width, cRect.y + cRect.height / 5 + cRect.height / 10);
+ }
+
+ this.pGreeter.pMagnifierWindow.resize (magnifier_width, magnifier_height);
+ }
+
this.pGreeter.pMagnifierWindow.visible = bActive;
}
}
diff --git a/src/background.vala b/src/background.vala
index 65a3365..07dce7e 100644
--- a/src/background.vala
+++ b/src/background.vala
@@ -2,6 +2,7 @@
*
* Copyright (C) 2011,2012 Canonical Ltd
* Copyright (C) 2015-2017 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
+ * Copyright (C) 2025 Robert Tari
*
* 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
@@ -18,6 +19,7 @@
* Authors: Robert Ancell <robert.ancell@canonical.com>
* Michael Terry <michael.terry@canonical.com>
* Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
+ * Robert Tari <robert@tari.in>
*/
class BackgroundLoader : Object
@@ -171,17 +173,43 @@ class BackgroundLoader : Object
var target_aspect = (double) width / height;
var aspect = (double) image.width / image.height;
double scale, offset_x = 0, offset_y = 0;
+ string sPosition = AGSettings.get_string (AGSettings.KEY_BACKGROUND_POSITION);
+
if (aspect > target_aspect)
{
/* Fit height and trim sides */
scale = (double) height / image.height;
- offset_x = (image.width * scale - width) / 2;
+
+ if (sPosition == "center")
+ {
+ offset_x = (image.width * scale - width) / 2;
+ }
+ else if (sPosition == "top-left" || sPosition == "bottom-left")
+ {
+ offset_x = 0;
+ }
+ else if (sPosition == "top-right" || sPosition == "bottom-right")
+ {
+ offset_x = (image.width * scale - width);
+ }
}
else
{
/* Fit width and trim top and bottom */
scale = (double) width / image.width;
- offset_y = (image.height * scale - height) / 2;
+
+ if (sPosition == "center")
+ {
+ offset_y = (image.height * scale - height) / 2;
+ }
+ else if (sPosition == "top-left" || sPosition == "top-right")
+ {
+ offset_y = 0;
+ }
+ else if (sPosition == "bottom-left" || sPosition == "bottom-right")
+ {
+ offset_y = (image.height * scale - height);
+ }
}
var scaled_image = new Gdk.Pixbuf (image.colorspace, image.has_alpha, image.bits_per_sample, width, height);
@@ -194,6 +222,7 @@ class BackgroundLoader : Object
{
var grid_x_offset = get_grid_offset (image.width);
var grid_y_offset = get_grid_offset (image.height);
+ var greeter = new ArcticaGreeter();
/* Create background */
var surface = new Cairo.Surface.similar (target_surface, Cairo.Content.COLOR, image.width, image.height);
@@ -206,8 +235,31 @@ class BackgroundLoader : Object
if (logo != null)
{
bc.save ();
- var x = (int) (grid_x_offset + 1.1 * grid_size);
- var y = (int) (image.height - 1.1 * grid_size - logo_height + grid_y_offset);
+ string sPosition = AGSettings.get_string (AGSettings.KEY_LOGO_POSITION);
+ int x = AGSettings.get_integer (AGSettings.KEY_LOGO_OFFSET_HORIZONTAL);
+ int y = AGSettings.get_integer (AGSettings.KEY_LOGO_OFFSET_VERTICAL);
+
+ if (sPosition == "top-left")
+ {
+ x = (int) (grid_x_offset + (x * greeter.grid_size));
+ y = (int) (grid_y_offset + ((y + 1) * greeter.grid_size));
+ }
+ else if (sPosition == "top-right")
+ {
+ x = (int) (image.width - (x * greeter.grid_size) - logo_width + grid_x_offset);
+ y = (int) (grid_y_offset + ((y + 1) * greeter.grid_size));
+ }
+ else if (sPosition == "bottom-left")
+ {
+ x = (int) (grid_x_offset + (x * greeter.grid_size));
+ y = (int) (image.height - (y * greeter.grid_size) - logo_height + grid_y_offset);
+ }
+ else if (sPosition == "bottom-right")
+ {
+ x = (int) (image.width - (x * greeter.grid_size) - logo_width + grid_x_offset);
+ y = (int) (image.height - (y * greeter.grid_size) - logo_height + grid_y_offset);
+ }
+
bc.translate (x, y);
bc.set_source_surface (logo, 0, 0);
bc.paint_with_alpha (AGSettings.get_double (AGSettings.KEY_LOGO_ALPHA));
@@ -835,14 +887,15 @@ public class Background : Gtk.Fixed
var height = get_allocated_height ();
var grid_x_offset = get_grid_offset (width);
var grid_y_offset = get_grid_offset (height);
+ var greeter = new ArcticaGreeter();
/* Overlay grid */
- var overlay_surface = new Cairo.Surface.similar (target_surface, Cairo.Content.COLOR_ALPHA, grid_size, grid_size);
+ var overlay_surface = new Cairo.Surface.similar (target_surface, Cairo.Content.COLOR_ALPHA, greeter.grid_size, greeter.grid_size);
var oc = new Cairo.Context (overlay_surface);
oc.rectangle (0, 0, 1, 1);
- oc.rectangle (grid_size - 1, 0, 1, 1);
- oc.rectangle (0, grid_size - 1, 1, 1);
- oc.rectangle (grid_size - 1, grid_size - 1, 1, 1);
+ oc.rectangle (greeter.grid_size - 1, 0, 1, 1);
+ oc.rectangle (0, greeter.grid_size - 1, 1, 1);
+ oc.rectangle (greeter.grid_size - 1, greeter.grid_size - 1, 1, 1);
oc.set_source_rgba (1.0, 1.0, 1.0, 0.25);
oc.fill ();
var overlay = new Cairo.Pattern.for_surface (overlay_surface);
diff --git a/src/cached-image.vala b/src/cached-image.vala
index 56157a3..3dfc5b8 100644
--- a/src/cached-image.vala
+++ b/src/cached-image.vala
@@ -21,7 +21,7 @@ public class CachedImage : Gtk.Image
{
private static HashTable<Gdk.Pixbuf, Cairo.Surface> surface_table;
- public static Cairo.Surface? get_cached_surface (Cairo.Context c, Gdk.Pixbuf pixbuf)
+ public static Cairo.Surface? get_cached_surface (Gdk.Pixbuf pixbuf)
{
if (surface_table == null)
surface_table = new HashTable<Gdk.Pixbuf, Cairo.Surface> (direct_hash, direct_equal);
@@ -29,31 +29,32 @@ public class CachedImage : Gtk.Image
var surface = surface_table.lookup (pixbuf);
if (surface == null)
{
- surface = new Cairo.Surface.similar (c.get_target (), Cairo.Content.COLOR_ALPHA, pixbuf.width, pixbuf.height);
- var new_c = new Cairo.Context (surface);
- Gdk.cairo_set_source_pixbuf (new_c, pixbuf, 0, 0);
- new_c.paint ();
+ surface = Gdk.cairo_surface_create_from_pixbuf (pixbuf, _scale_factor, null);
surface_table.insert (pixbuf, surface);
}
return surface;
}
+ private void update_image(Gdk.Pixbuf? pixbuf)
+ {
+ if (pixbuf != null)
+ {
+ surface = get_cached_surface (pixbuf);
+ }
+ else
+ {
+ surface = null;
+ pixbuf = null;
+ }
+ }
+
public CachedImage (Gdk.Pixbuf? pixbuf)
{
- Object (pixbuf: pixbuf);
+ update_image (pixbuf);
}
- public override bool draw (Cairo.Context c)
+ public void set_pixbuf(Gdk.Pixbuf? pixbuf)
{
- if (pixbuf != null)
- {
- var cached_surface = get_cached_surface (c, pixbuf);
- if (cached_surface != null)
- {
- c.set_source_surface (cached_surface, 0, 0);
- c.paint ();
- }
- }
- return false;
+ update_image (pixbuf);
}
}
diff --git a/src/dash-box.vala b/src/dash-box.vala
index 4d907db..52dd7d4 100644
--- a/src/dash-box.vala
+++ b/src/dash-box.vala
@@ -2,7 +2,7 @@
*
* Copyright (C) 2011,2012 Canonical Ltd
* Copyright (C) 2015 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
- * Copyright (C) 2023 Robert Tari
+ * Copyright (C) 2023-2025 Robert Tari
*
* 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
@@ -165,8 +165,10 @@ public class DashBox : Gtk.Box
}
else
{
- min = grid_size * GreeterList.DEFAULT_BOX_HEIGHT - GreeterList.BORDER * 2;
- nat = grid_size * GreeterList.DEFAULT_BOX_HEIGHT - GreeterList.BORDER * 2;
+ var greeter = new ArcticaGreeter();
+
+ min = greeter.grid_size * GreeterList.DEFAULT_BOX_HEIGHT - (int)(GreeterList.BORDER * greeter.scaling_factor_widgets * 2);
+ nat = greeter.grid_size * GreeterList.DEFAULT_BOX_HEIGHT - (int)(GreeterList.BORDER * greeter.scaling_factor_widgets * 2);
}
}
else
@@ -184,8 +186,9 @@ public class DashBox : Gtk.Box
public override void get_preferred_width (out int min, out int nat)
{
- min = grid_size * GreeterList.BOX_WIDTH - GreeterList.BORDER * 2;
- nat = grid_size * GreeterList.BOX_WIDTH - GreeterList.BORDER * 2;
+ var greeter = new ArcticaGreeter();
+ min = greeter.grid_size * GreeterList.BOX_WIDTH - (int)(GreeterList.BORDER * greeter.scaling_factor_widgets * 2);
+ nat = greeter.grid_size * GreeterList.BOX_WIDTH - (int)(GreeterList.BORDER * greeter.scaling_factor_widgets * 2);
}
public override bool draw (Cairo.Context c)
@@ -200,8 +203,10 @@ public class DashBox : Gtk.Box
c.restore ();
}
+ var greeter = new ArcticaGreeter();
+
/* Draw darker background with a rounded border */
- var box_r = 0.3 * grid_size;
+ var box_r = 0.3 * greeter.grid_size;
int box_y = 0;
int box_w;
int box_h;
@@ -232,7 +237,11 @@ public class DashBox : Gtk.Box
}
else
{
- c.set_source_rgba (0.1, 0.1, 0.1, 0.4);
+ string sBackGround = AGSettings.get_string (AGSettings.KEY_DASHBOX_BGCOLOR);
+ Gdk.RGBA cBackground = {1.0, 1.0, 1.0, 1.0};
+ cBackground.parse (sBackGround);
+ double fOpacity = AGSettings.get_double (AGSettings.KEY_DASHBOX_OPACITY);
+ c.set_source_rgba (cBackground.red, cBackground.green, cBackground.blue, fOpacity);
}
c.fill_preserve ();
diff --git a/src/greeter-list.vala b/src/greeter-list.vala
index 77a451f..da4f3e9 100644
--- a/src/greeter-list.vala
+++ b/src/greeter-list.vala
@@ -2,7 +2,7 @@
*
* Copyright (C) 2012 Canonical Ltd
* Copyright (C) 2015-2017 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
- * Copyright (C) 2023 Robert Tari
+ * Copyright (C) 2023-2025 Robert Tari
*
* 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
@@ -24,10 +24,13 @@
*/
private const int MAX_FIELD_SIZE = 200;
+public int _scale_factor = 1;
+
private int get_grid_offset (int size)
{
- return (int) (size % grid_size) / 2;
+ var greeter = new ArcticaGreeter();
+ return (int) (size % greeter.grid_size) / 2;
}
[DBus (name="com.lomiri.LomiriGreeter.List")]
@@ -102,7 +105,7 @@ public abstract class GreeterList : FadableBox
protected Mode mode = Mode.ENTRY;
public const int BORDER = 4;
- public const int BOX_WIDTH = 8; /* in grid_size blocks */
+ public const int BOX_WIDTH = 9; /* in grid_size blocks */
public const int DEFAULT_BOX_HEIGHT = 3; /* in grid_size blocks */
private uint n_above = 4;
@@ -117,12 +120,13 @@ public abstract class GreeterList : FadableBox
{
get
{
+ var greeter = new ArcticaGreeter();
/* First, get grid row number as if menubar weren't there */
- var row = (MainWindow.MENUBAR_HEIGHT + get_allocated_height ()) / grid_size;
+ var row = (greeter.menubar_height + get_allocated_height ()) / greeter.grid_size;
row = row - DEFAULT_BOX_HEIGHT; /* and no default dash box */
row = row / 2; /* and in the middle */
/* Now calculate y pixel spot keeping in mind menubar's allocation */
- return row * grid_size - MainWindow.MENUBAR_HEIGHT;
+ return row * greeter.grid_size - greeter.menubar_height;
}
}
@@ -173,6 +177,8 @@ public abstract class GreeterList : FadableBox
can_focus = false;
visible_window = false;
+ _scale_factor = get_scale_factor ();
+
fixed = new Gtk.Fixed ();
fixed.show ();
add (fixed);
@@ -223,8 +229,9 @@ public abstract class GreeterList : FadableBox
public override void get_preferred_width (out int min, out int nat)
{
- min = BOX_WIDTH * grid_size;
- nat = BOX_WIDTH * grid_size;
+ var greeter = new ArcticaGreeter();
+ min = BOX_WIDTH * greeter.grid_size;
+ nat = BOX_WIDTH * greeter.grid_size;
}
public override void get_preferred_height (out int min, out int nat)
@@ -398,8 +405,10 @@ public abstract class GreeterList : FadableBox
protected void add_entry (PromptBox entry)
{
+ var greeter = new ArcticaGreeter();
+
entry.expand = true;
- entry.set_size_request (grid_size * BOX_WIDTH - BORDER * 2, -1);
+ entry.set_size_request (greeter.grid_size * BOX_WIDTH - (int)(BORDER * greeter.scaling_factor_widgets * 2), -1);
add_with_class (entry);
insert_entry (entry);
@@ -510,33 +519,39 @@ public abstract class GreeterList : FadableBox
protected int get_greeter_box_height_grids ()
{
+ var greeter = new ArcticaGreeter();
+
int height = get_greeter_box_height ();
- return height / grid_size + 1; /* +1 because we'll be slightly under due to BORDER */
+ return height / greeter.grid_size + 1; /* +1 because we'll be slightly under due to BORDER */
}
protected int get_greeter_box_x ()
{
- return box_x + BORDER;
+ var greeter = new ArcticaGreeter();
+ return box_x + (int)(BORDER * greeter.scaling_factor_widgets);
}
protected int get_greeter_box_y ()
{
- return box_y + BORDER;
+ var greeter = new ArcticaGreeter();
+ return box_y + (int)(BORDER * greeter.scaling_factor_widgets);
}
protected virtual int get_position_y (double position)
{
+ var greeter = new ArcticaGreeter();
+
// Most position heights are just the grid height. Except for the
// greeter box itself.
- int box_height = get_greeter_box_height_grids () * grid_size;
+ int box_height = get_greeter_box_height_grids () * greeter.grid_size;
double offset;
if (position < 0)
- offset = position * grid_size;
+ offset = position * greeter.grid_size;
else if (position < 1)
offset = position * box_height;
else
- offset = (position - 1) * grid_size + box_height;
+ offset = (position - 1) * greeter.grid_size + box_height;
return box_y + (int)Math.round(offset);
}
@@ -556,8 +571,10 @@ public abstract class GreeterList : FadableBox
Gtk.Allocation allocation;
get_allocation (out allocation);
+ var greeter = new ArcticaGreeter();
+
var child_allocation = Gtk.Allocation ();
- child_allocation.width = grid_size * BOX_WIDTH - BORDER * 2;
+ child_allocation.width = greeter.grid_size * BOX_WIDTH - (int)(BORDER * greeter.scaling_factor_widgets * 2);
entry.get_preferred_height_for_width (child_allocation.width, null, out child_allocation.height);
child_allocation.x = allocation.x + get_greeter_box_x ();
child_allocation.y = allocation.y + get_position_y (position);
@@ -644,10 +661,7 @@ public abstract class GreeterList : FadableBox
focus_prompt ();
entry_displayed_done ();
mode = Mode.ENTRY;
-
-#if HAVE_GTK_3_20_0
queue_allocate ();
-#endif
}
protected void select_entry (PromptBox entry, double direction, bool do_scroll = true)
@@ -757,12 +771,14 @@ public abstract class GreeterList : FadableBox
fixed.propagate_draw (greeter_box, c); /* Always full alpha */
c.restore ();
+ var greeter = new ArcticaGreeter();
+
if (greeter_box.base_alpha != 0.0)
{
c.save ();
c.push_group ();
- c.rectangle (get_greeter_box_x (), get_greeter_box_y () - n_above * grid_size, grid_size * BOX_WIDTH - BORDER * 2, grid_size * (n_above + n_below + get_greeter_box_height_grids ()));
+ c.rectangle (get_greeter_box_x (), get_greeter_box_y () - n_above * greeter.grid_size, greeter.grid_size * BOX_WIDTH - (int)(BORDER * greeter.scaling_factor_widgets * 2), greeter.grid_size * (n_above + n_below + get_greeter_box_height_grids ()));
c.clip ();
foreach (var child in fixed.get_children ())
@@ -852,6 +868,7 @@ public abstract class GreeterList : FadableBox
/* Limit the number of characters in case a cat is sitting on the keyboard... */
entry.max_length = MAX_FIELD_SIZE;
+ queue_resize ();
}
protected virtual void authentication_complete_cb ()
diff --git a/src/list-stack.vala b/src/list-stack.vala
index 63bde28..4f74ea7 100644
--- a/src/list-stack.vala
+++ b/src/list-stack.vala
@@ -1,7 +1,7 @@
/* -*- Mode: Vala; indent-tabs-mode: nil; tab-width: 4 -*-
*
* Copyright (C) 2011,2012 Canonical Ltd
- * Copyright (C) 2023 Robert Tari
+ * Copyright (C) 2023-2025 Robert Tari
*
* 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
@@ -35,10 +35,10 @@ public class ListStack : Gtk.Fixed
construct
{
- // Hack to avoid gtk 3.20's new allocate logic, which messes us up.
resize_mode = Gtk.ResizeMode.QUEUE;
+ var greeter = new ArcticaGreeter();
- width = grid_size * GreeterList.BOX_WIDTH;
+ width = greeter.grid_size * GreeterList.BOX_WIDTH;
}
public GreeterList? top ()
diff --git a/src/main-window.vala b/src/main-window.vala
index 5332186..621b115 100644
--- a/src/main-window.vala
+++ b/src/main-window.vala
@@ -2,7 +2,7 @@
*
* Copyright (C) 2011,2012 Canonical Ltd
* Copyright (C) 2015-2017 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
- * Copyright (C) 2023 Robert Tari
+ * Copyright (C) 2023-2025 Robert Tari
*
* 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
@@ -34,14 +34,34 @@ public class MainWindow : Gtk.Window
private Background background;
private Gtk.Box login_box;
private Gtk.Box hbox;
+ private Gtk.Box content_box;
private Gtk.Button back_button;
private ShutdownDialog? shutdown_dialog = null;
private bool do_resize;
public ListStack stack;
- // Menubar is smaller, but with shadow, we reserve more space
- public const int MENUBAR_HEIGHT = 40;
+ public enum Struts {
+ LEFT,
+ RIGHT,
+ TOP,
+ BOTTOM,
+ LEFT_START,
+ LEFT_END,
+ RIGHT_START,
+ RIGHT_END,
+ TOP_START,
+ TOP_END,
+ BOTTOM_START,
+ BOTTOM_END
+ }
+
+ public enum MenubarPositions {
+ LEFT,
+ RIGHT,
+ TOP,
+ BOTTOM,
+ }
construct
{
@@ -50,9 +70,21 @@ public class MainWindow : Gtk.Window
var accel_group = new Gtk.AccelGroup ();
add_accel_group (accel_group);
- var bg_color = Gdk.RGBA ();
- bg_color.parse (AGSettings.get_string (AGSettings.KEY_BACKGROUND_COLOR));
- override_background_color (Gtk.StateFlags.NORMAL, bg_color);
+ Gtk.StyleContext pContext = get_style_context ();
+ Gtk.CssProvider pProvider = new Gtk.CssProvider ();
+ string sColour = AGSettings.get_string (AGSettings.KEY_BACKGROUND_COLOR);
+ string sCss = "* {background-color: %s;}".printf (sColour);
+
+ try
+ {
+ pProvider.load_from_data (sCss, -1);
+ pContext.add_provider (pProvider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
+ }
+ catch (Error pError)
+ {
+ warning ("Panic: Error loading style for main window: %s", pError.message);
+ }
+
get_accessible ().set_name (_("Login Screen"));
ArcticaGreeter.add_style_class (this);
@@ -75,7 +107,8 @@ public class MainWindow : Gtk.Window
background-repeat: repeat;".printf(shadow_path);
}
- menubox.set_size_request (-1, MENUBAR_HEIGHT);
+ var greeter = new ArcticaGreeter();
+ menubox.set_size_request (-1, greeter.menubar_height);
menubox.show ();
login_box.add (menubox);
ArcticaGreeter.add_style_class (menubox);
@@ -90,32 +123,53 @@ public class MainWindow : Gtk.Window
ArcticaGreeter.add_style_class (menubar);
ArcticaGreeter.add_style_class (menubox);
+ content_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
+ content_box.expand = true;
+ content_box.show ();
+ login_box.add (content_box);
+
+ var content_align = AGSettings.get_string(AGSettings.KEY_CONTENT_ALIGN);
+ var x_align = Gtk.Align.CENTER;
+
+ if (content_align == "left")
+ {
+ x_align = Gtk.Align.START;
+ }
+ else if (content_align == "right")
+ {
+ x_align = Gtk.Align.END;
+ }
+
hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
hbox.expand = true;
hbox.show ();
- login_box.add (hbox);
+ hbox.halign = x_align;
+ hbox.valign = Gtk.Align.CENTER;
- var align = new Gtk.Alignment (0.5f, 0.5f, 0.0f, 0.0f);
- // Hack to avoid gtk 3.20's new allocate logic, which messes us up.
- align.resize_mode = Gtk.ResizeMode.QUEUE;
- align.set_size_request (grid_size, -1);
- align.margin_bottom = MENUBAR_HEIGHT; /* offset for menubar at top */
- align.show ();
- hbox.add (align);
+ if (content_align == "center")
+ {
+ // offset for back button
+ hbox.margin_end = greeter.grid_size;
+ }
+ content_box.add (hbox);
back_button = new FlatButton ();
back_button.get_accessible ().set_name (_("Back"));
Gtk.button_set_focus_on_click (back_button, false);
var image = new Gtk.Image.from_file (Path.build_filename (Config.PKGDATADIR, "arrow_left.png", null));
image.show ();
- back_button.set_size_request (grid_size - GreeterList.BORDER * 2, grid_size - GreeterList.BORDER * 2);
+ back_button.set_size_request (greeter.grid_size - (int)(GreeterList.BORDER * greeter.scaling_factor_widgets * 2),
+ greeter.grid_size - (int)(GreeterList.BORDER * greeter.scaling_factor_widgets * 2));
try
{
var style = new Gtk.CssProvider ();
style.load_from_data ("* {background-color: transparent;
%s
- }".printf(shadow_style), -1);
+ }
+ *.high_contrast {background-color: black;
+ border-color: white;
+ }".printf(shadow_style), -1);
var context = back_button.get_style_context();
context.add_provider (style,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
@@ -127,8 +181,14 @@ public class MainWindow : Gtk.Window
back_button.add (image);
back_button.clicked.connect (pop_list);
-
- align.add (back_button);
+ back_button.halign = Gtk.Align.CENTER;
+ back_button.valign = Gtk.Align.CENTER;
+ back_button.hexpand = false;
+ back_button.vexpand = false;
+ back_button.resize_mode = Gtk.ResizeMode.QUEUE;
+ back_button.set_size_request (greeter.grid_size, -1);
+ back_button.margin_bottom = greeter.menubar_height;
+ hbox.add (back_button);
stack = new ListStack ();
stack.show ();
@@ -146,7 +206,6 @@ public class MainWindow : Gtk.Window
only_on_monitor = AGSettings.get_string(AGSettings.KEY_ONLY_ON_MONITOR);
monitor_setting_ok = only_on_monitor == "auto";
- var greeter = new ArcticaGreeter ();
if (greeter.test_mode)
{
/* Simulate an 800x600 monitor to the left of a 640x480 monitor */
@@ -159,6 +218,7 @@ public class MainWindow : Gtk.Window
}
else
{
+ set_type_hint(Gdk.WindowTypeHint.DOCK);
var screen = get_screen ();
screen.monitors_changed.connect (monitors_changed_cb);
monitors_changed_cb (screen);
@@ -179,18 +239,31 @@ public class MainWindow : Gtk.Window
back_button.hide ();
stack.pop ();
+
+ redraw_main_window();
+ }
+
+ protected void redraw_main_window ()
+ {
+ Gtk.Allocation allocation;
+ this.get_allocation (out allocation);
+ queue_draw_area (allocation.x, allocation.y, allocation.width, allocation.height);
}
+
public override void size_allocate (Gtk.Allocation allocation)
{
base.size_allocate (allocation);
- if (hbox != null)
+ var greeter = new ArcticaGreeter();
+
+ if (content_box != null)
{
- hbox.margin_start = get_grid_offset (get_allocated_width ()) + grid_size;
- hbox.margin_end = get_grid_offset (get_allocated_width ());
- hbox.margin_top = get_grid_offset (get_allocated_height ());
- hbox.margin_bottom = get_grid_offset (get_allocated_height ());
+ var content_align = AGSettings.get_string(AGSettings.KEY_CONTENT_ALIGN);
+ content_box.margin_start = get_grid_offset (get_allocated_width ()) + (content_align == "left" ? greeter.grid_size : 0);
+ content_box.margin_end = get_grid_offset (get_allocated_width ()) + (content_align == "right" ? greeter.grid_size : 0);
+ content_box.margin_top = get_grid_offset (get_allocated_height ());
+ content_box.margin_bottom = get_grid_offset (get_allocated_height ());
}
}
@@ -202,6 +275,102 @@ public class MainWindow : Gtk.Window
move_to_monitor (primary_monitor);
}
+ public void set_struts ()
+ {
+ var greeter = new ArcticaGreeter();
+ /* Substract the 5px shadow from the menubar height, so that indicators' menus render well */
+ _set_struts (MenubarPositions.TOP, greeter.menubar_height);
+ }
+
+ private void getScreenSize (out int nScreenWidth, out int nScreenHeight)
+ {
+ Gdk.Display pDisplay = Gdk.Display.get_default ();
+ int nMonitors = pDisplay.get_n_monitors ();
+
+ if (nMonitors == 0)
+ {
+ nScreenWidth = 0;
+ nScreenHeight = 0;
+
+ return;
+ }
+
+ int x0 = int.MAX;
+ int y0 = int.MAX;
+ int x1 = int.MIN;
+ int y1 = int.MIN;
+
+ for (int nMonitor = 0; nMonitor < nMonitors; nMonitor++)
+ {
+ Gdk.Monitor pMonitor = pDisplay.get_monitor (nMonitor);
+ Gdk.Rectangle cRectangle = pMonitor.get_geometry ();
+
+ x0 = int.min (x0, cRectangle.x);
+ y0 = int.min (y0, cRectangle.y);
+ x1 = int.max (x1, cRectangle.x + cRectangle.width);
+ y1 = int.max (y1, cRectangle.y + cRectangle.height);
+ }
+
+ nScreenWidth = x1 - x0;
+ nScreenHeight = y1 - y0;
+ }
+
+ private void _set_struts (uint position, long menubar_size)
+ {
+ if (!get_realized()) {
+ return;
+ }
+
+ int scale = get_scale_factor();
+
+ if (primary_monitor == null) {
+ return;
+ }
+
+ Gdk.Atom atom;
+ long struts[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ /* Subtract (non-scaled) 5px border + 2px extra spacing (to make indicator menus render nicely below menubar) */
+ menubar_size = menubar_size - 7;
+
+ int nScreenWidth = 0;
+ int nScreenHeight = 0;
+ getScreenSize (out nScreenWidth, out nScreenHeight);
+
+ // Struts dependent on position
+ switch (position) {
+ case MenubarPositions.TOP:
+ struts[Struts.TOP] = (menubar_size + primary_monitor.y) * scale;
+ struts[Struts.TOP_START] = primary_monitor.x * scale;
+ struts[Struts.TOP_END] = (primary_monitor.x + primary_monitor.width) * scale - 1;
+ break;
+ case MenubarPositions.LEFT:
+ struts[Struts.LEFT] = (primary_monitor.x + menubar_size) * scale;
+ struts[Struts.LEFT_START] = primary_monitor.y * scale;
+ struts[Struts.LEFT_END] = (primary_monitor.y + primary_monitor.height) * scale - 1;
+ break;
+ case MenubarPositions.RIGHT:
+ struts[Struts.RIGHT] = (menubar_size + nScreenWidth - primary_monitor.x - primary_monitor.width) * scale;
+ struts[Struts.RIGHT_START] = primary_monitor.y * scale;
+ struts[Struts.RIGHT_END] = (primary_monitor.y + primary_monitor.height) * scale - 1;
+ break;
+ case MenubarPositions.BOTTOM:
+ default:
+ struts[Struts.BOTTOM] = (menubar_size + nScreenHeight - primary_monitor.y - primary_monitor.height) * scale;
+ struts[Struts.BOTTOM_START] = primary_monitor.x * scale;
+ struts[Struts.BOTTOM_END] = (primary_monitor.x + primary_monitor.width) * scale - 1;
+ break;
+ }
+
+ atom = Gdk.Atom.intern("_NET_WM_STRUT", false);
+ Gdk.property_change(get_toplevel().get_window(), atom, Gdk.Atom.intern("CARDINAL", false),
+ 32, Gdk.PropMode.REPLACE, (uint8[])struts, 4);
+
+ atom = Gdk.Atom.intern("_NET_WM_STRUT_PARTIAL", false);
+ Gdk.property_change(get_toplevel().get_window(), atom, Gdk.Atom.intern("CARDINAL", false),
+ 32, Gdk.PropMode.REPLACE, (uint8[])struts, 12);
+ }
+
public override void realize ()
{
base.realize ();
@@ -257,6 +426,8 @@ public class MainWindow : Gtk.Window
{
do_resize = true;
}
+
+ set_struts ();
}
/* Check if a monitor has a unique position */
@@ -428,6 +599,99 @@ public class MainWindow : Gtk.Window
return true;
}
break;
+
+ case Gdk.Key.k:
+
+ if ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0)
+ {
+ bool bActive = AGSettings.get_boolean (AGSettings.KEY_ONSCREEN_KEYBOARD);
+
+ try
+ {
+ DBusConnection pConnection = Bus.get_sync (BusType.SESSION);
+ Variant pActive = new Variant.boolean (!bActive);
+ Variant pTuple = new Variant("(sva{sv})", "onboard", pActive, null);
+ pConnection.call.begin ("org.ayatana.indicator.a11y", "/org/ayatana/indicator/a11y", "org.gtk.Actions", "SetState", pTuple, null, DBusCallFlags.NONE, -1, null);
+ }
+ catch (Error pError)
+ {
+ warning ("%s", pError.message);
+ }
+
+ return true;
+ }
+
+ break;
+
+ case Gdk.Key.h:
+
+ if ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0)
+ {
+ AGSettings pSettings = new AGSettings ();
+ bool bActive = pSettings.high_contrast;
+
+ try
+ {
+ DBusConnection pConnection = Bus.get_sync (BusType.SESSION);
+ Variant pActive = new Variant.boolean (!bActive);
+ Variant pTuple = new Variant("(sva{sv})", "contrast", pActive, null);
+ pConnection.call.begin ("org.ayatana.indicator.a11y", "/org/ayatana/indicator/a11y", "org.gtk.Actions", "SetState", pTuple, null, DBusCallFlags.NONE, -1, null);
+ }
+ catch (Error pError)
+ {
+ warning ("%s", pError.message);
+ }
+
+ return true;
+ }
+
+ break;
+
+ case Gdk.Key.s:
+
+ if ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0)
+ {
+ bool bActive = AGSettings.get_boolean (AGSettings.KEY_SCREEN_READER);
+
+ try
+ {
+ DBusConnection pConnection = Bus.get_sync (BusType.SESSION);
+ Variant pActive = new Variant.boolean (!bActive);
+ Variant pTuple = new Variant("(sva{sv})", "orca", pActive, null);
+ pConnection.call.begin ("org.ayatana.indicator.a11y", "/org/ayatana/indicator/a11y", "org.gtk.Actions", "SetState", pTuple, null, DBusCallFlags.NONE, -1, null);
+ }
+ catch (Error pError)
+ {
+ warning ("%s", pError.message);
+ }
+
+ return true;
+ }
+
+ break;
+
+ case Gdk.Key.m:
+
+ if ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0)
+ {
+ bool bActive = AGSettings.get_boolean (AGSettings.KEY_MAGNIFIER);
+
+ try
+ {
+ DBusConnection pConnection = Bus.get_sync (BusType.SESSION);
+ Variant pActive = new Variant.boolean (!bActive);
+ Variant pTuple = new Variant("(sva{sv})", "magnifier", pActive, null);
+ pConnection.call.begin ("org.ayatana.indicator.a11y", "/org/ayatana/indicator/a11y", "org.gtk.Actions", "SetState", pTuple, null, DBusCallFlags.NONE, -1, null);
+ }
+ catch (Error pError)
+ {
+ warning ("%s", pError.message);
+ }
+
+ return true;
+ }
+
+ break;
}
return base.key_press_event (event);
diff --git a/src/menubar.vala b/src/menubar.vala
index 7b1fb21..0ba2903 100644
--- a/src/menubar.vala
+++ b/src/menubar.vala
@@ -2,7 +2,7 @@
*
* Copyright (C) 2011,2012 Canonical Ltd
* Copyright (C) 2015-2017 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
- * Copyright (C) 2023 Robert Tari
+ * Copyright (C) 2023-2025 Robert Tari
*
* 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
@@ -40,6 +40,19 @@ private class IndicatorMenuItem : Gtk.MenuItem
{
entry.label.show.connect (this.visibility_changed_cb);
entry.label.hide.connect (this.visibility_changed_cb);
+ var pContext = entry.label.get_style_context ();
+ var pProvider = new Gtk.CssProvider ();
+
+ try
+ {
+ pProvider.load_from_data ("*.high_contrast {color: #000000; font-size: 12pt; text-shadow: none;}", -1);
+ }
+ catch (Error pError)
+ {
+ error ("Panic: Failed adding indicator label colour: %s", pError.message);
+ }
+
+ pContext.add_provider (pProvider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
hbox.pack_start (entry.label, false, false, 0);
}
if (entry.image != null)
@@ -88,83 +101,17 @@ private class IndicatorMenuItem : Gtk.MenuItem
}
}
-public class MenuBar : Gtk.MenuBar
+public class MenuBar : Gtk.Grid
{
public Background? background { get; construct; default = null; }
public Gtk.Window? keyboard_window { get; private set; default = null; }
public Gtk.AccelGroup? accel_group { get; construct; }
- private const int HEIGHT = 32;
-
public MenuBar (Background bg, Gtk.AccelGroup ag)
{
Object (background: bg, accel_group: ag);
}
- public override bool draw (Cairo.Context c)
- {
- 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 ();
- */
- }
-
- /* 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 ())
- {
- propagate_draw (child, c);
- }
-
- return false;
- }
-
public static void add_style_class (Gtk.Widget widget)
{
/*
@@ -176,116 +123,64 @@ public class MenuBar : Gtk.MenuBar
}
private List<Indicator.Object> indicator_objects;
+ private Gtk.MenuBar pMenubar;
construct
{
+ // Assure that printf operates in C.UTF-8 locale for float-to-string conversions.
+ Intl.setlocale(LocaleCategory.NUMERIC, "C.UTF-8");
+
+ this.pMenubar = new Gtk.MenuBar ();
+ this.pMenubar.halign = Gtk.Align.END;
+ this.pMenubar.hexpand = true;
+ this.pMenubar.pack_direction = Gtk.PackDirection.RTL;
+ this.pMenubar.show ();
+ this.attach (this.pMenubar, 1, 0, 1, 1);
+ this.show ();
add_style_class (this);
-
- /* Add shadow. */
- var shadow_style = new Gtk.CssProvider ();
+ Gtk.CssProvider pGridProvider = new Gtk.CssProvider ();
+ Gtk.StyleContext pGridContext = this.get_style_context ();
+ Gdk.RGBA pBackground = getBackground (pGridContext, AGSettings.KEY_MENUBAR_BGCOLOR, AGSettings.KEY_MENUBAR_ALPHA);
+ int nBackgroundRed = (int)(pBackground.red * 255.0);
+ int nBackgroundGreen = (int)(pBackground.green * 255.0);
+ int nBackgroundBlue = (int)(pBackground.blue * 255.0);
+ Gdk.RGBA pShadow = getBackground (pGridContext, AGSettings.KEY_MENUBAR_SHADOW_COLOR, AGSettings.KEY_MENUBAR_SHADOW_ALPHA);
+ int nShadowRed = (int)(pShadow.red * 255.0);
+ int nShadowGreen = (int)(pShadow.green * 255.0);
+ int nShadowBlue = (int)(pShadow.blue * 255.0);
+ string sBackground = "* {background-color: rgba(%i, %i, %i, %f); border: none; box-shadow: 0px 5px 5px -5px rgba(%i, %i, %i, %f);}".printf (nBackgroundRed, nBackgroundGreen, nBackgroundBlue, pBackground.alpha, nShadowRed, nShadowGreen, nShadowBlue, pShadow.alpha);
try
{
- shadow_style.load_from_data ("* { box-shadow: 0px 0px 5px 5px #000000; }", -1);
+ pGridProvider.load_from_data (sBackground + " *.high_contrast {background-color: #ffffff; color: #000000; text-shadow: none; box-shadow: none;}", -1);
}
catch (Error pError)
{
- error ("Panic: Failed adding shadow: %s", pError.message);
+ error ("Panic: Failed loading menubar grid colours: %s", pError.message);
}
- this.get_style_context ().add_provider (shadow_style,
- Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
+ pGridContext.add_provider (pGridProvider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
- pack_direction = Gtk.PackDirection.RTL;
+ Gtk.CssProvider pMenubarProvider = new Gtk.CssProvider ();
- if (AGSettings.get_boolean (AGSettings.KEY_SHOW_HOSTNAME))
+ try
{
- 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);
- }
+ pMenubarProvider.load_from_data ("* { background-color: transparent; } *.high_contrast { color: #000000; text-shadow: none; }", -1);
+ }
+ catch (Error pError)
+ {
+ error ("Panic: Failed loading menubar colours: %s", pError.message);
+ }
- hostname_item.set_sensitive (false);
+ this.pMenubar.get_style_context ().add_provider (pMenubarProvider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
- /* The below does not work, so for now we need to stick to "set_right_justified"
- hostname_item.set_hexpand (true);
- hostname_item.set_halign (Gtk.Align.END);*/
- hostname_item.set_right_justified (true);
+ if (AGSettings.get_boolean (AGSettings.KEY_SHOW_HOSTNAME))
+ {
+ Gtk.Label pLabel = new Gtk.Label (Posix.utsname ().nodename);
+ pLabel.vexpand = true;
+ pLabel.margin_start = 6;
+ pLabel.show ();
+ this.attach (pLabel, 0, 0, 1, 1);
}
/* Prevent dragging the window by the menubar */
@@ -293,7 +188,7 @@ public class MenuBar : Gtk.MenuBar
{
var style = new Gtk.CssProvider ();
style.load_from_data ("* {-GtkWidget-window-dragging: false;}", -1);
- get_style_context ().add_provider (style, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
+ this.pMenubar.get_style_context ().add_provider (style, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
}
catch (Error e)
{
@@ -303,10 +198,52 @@ public class MenuBar : Gtk.MenuBar
setup_indicators ();
}
+ public void select_first (bool bSearchSensitive)
+ {
+ this.pMenubar.select_first (bSearchSensitive);
+ }
+
public override void get_preferred_height (out int min, out int nat)
{
- min = HEIGHT;
- nat = HEIGHT;
+ var greeter = new ArcticaGreeter ();
+ min = (int)Math.round(greeter.menubar_height - 8);
+ nat = (int)Math.round(greeter.menubar_height - 8);
+ }
+
+ private Gdk.RGBA getBackground (Gtk.StyleContext pContext, string sBackgroundKey, string sAlphaKey)
+ {
+ string sBackground = AGSettings.get_string (sBackgroundKey);
+ Gdk.RGBA pBackground;
+
+ if (sBackground != "")
+ {
+ pBackground = Gdk.RGBA ();
+ pBackground.parse (sBackground);
+ }
+ else
+ {
+ bool bFound = pContext.lookup_color ("osd_bg", out pBackground);
+
+ if (!bFound)
+ {
+ bFound = pContext.lookup_color ("dark_bg_color", out pBackground);
+
+ if (!bFound)
+ {
+ pBackground = Gdk.RGBA ();
+ pBackground.parse ("#444444");
+ debug ("Failed to retrieve osd_bg and dark_bg_color for %s - falling back to #444444", sBackgroundKey);
+ }
+ else
+ {
+ debug ("Failed to retrieve osd_bg for %s - falling back to dark_bg_color", sBackgroundKey);
+ }
+ }
+ }
+
+ pBackground.alpha = AGSettings.get_double (sAlphaKey);
+
+ return pBackground;
}
private Indicator.Object? load_indicator_file (string indicator_name)
@@ -454,7 +391,7 @@ public class MenuBar : Gtk.MenuBar
{
var index = get_indicator_index (object);
var pos = 0;
- foreach (var child in get_children ())
+ foreach (var child in this.pMenubar.get_children ())
{
if (!(child is IndicatorMenuItem))
break;
@@ -471,19 +408,19 @@ public class MenuBar : Gtk.MenuBar
var menuitem = new IndicatorMenuItem (entry);
menuitem.set_data ("indicator-object", object);
- insert (menuitem, pos);
+ this.pMenubar.insert (menuitem, pos);
}
private void indicator_removed_cb (Indicator.Object object, Indicator.ObjectEntry entry)
{
debug ("Removing indicator object %p", entry);
- foreach (var child in get_children ())
+ foreach (var child in this.pMenubar.get_children ())
{
var menuitem = (IndicatorMenuItem) child;
if (menuitem.entry == entry)
{
- remove (child);
+ this.pMenubar.remove (child);
return;
}
}
diff --git a/src/prompt-box.vala b/src/prompt-box.vala
index 5716ab1..fb3d4ab 100644
--- a/src/prompt-box.vala
+++ b/src/prompt-box.vala
@@ -2,7 +2,7 @@
*
* Copyright (C) 2011,2012 Canonical Ltd
* Copyright (C) 2015,2017 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
- * Copyright (C) 2023 Robert Tari
+ * Copyright (C) 2023-2025 Robert Tari
*
* 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
@@ -96,8 +96,8 @@ public class PromptBox : FadableBox
construct
{
- // Hack to avoid gtk 3.20's new allocate logic, which messes us up.
resize_mode = Gtk.ResizeMode.QUEUE;
+ var greeter = new ArcticaGreeter();
set_start_row ();
reset_last_row ();
@@ -110,7 +110,7 @@ public class PromptBox : FadableBox
box_grid = new Gtk.Grid ();
box_grid.column_spacing = 4;
box_grid.row_spacing = 3;
- box_grid.margin_top = GreeterList.BORDER;
+ box_grid.margin_top = (int)(GreeterList.BORDER * greeter.scaling_factor_widgets);
box_grid.margin_bottom = 6;
box_grid.expand = true;
@@ -126,7 +126,7 @@ public class PromptBox : FadableBox
active_indicator = new ActiveIndicator ();
active_indicator.valign = Gtk.Align.START;
- active_indicator.margin_top = (grid_size - ActiveIndicator.HEIGHT) / 2;
+ active_indicator.margin_top = (greeter.grid_size - ActiveIndicator.HEIGHT) / 2;
active_indicator.show ();
box_grid.attach (active_indicator, COL_ACTIVE, last_row, 1, 1);
@@ -151,7 +151,7 @@ public class PromptBox : FadableBox
small_active_indicator = new ActiveIndicator ();
small_active_indicator.valign = Gtk.Align.START;
- small_active_indicator.margin_top = (grid_size - ActiveIndicator.HEIGHT) / 2;
+ small_active_indicator.margin_top = (greeter.grid_size - ActiveIndicator.HEIGHT) / 2;
small_active_indicator.show ();
small_box_grid.attach (small_active_indicator, 0, 0, 1, 1);
@@ -223,8 +223,8 @@ public class PromptBox : FadableBox
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); }";
+ string sColor = AGSettings.get_string (AGSettings.KEY_PROMPTBOX_COLOR_NORMAL);
+ var css = "* { color: %s; } .high_contrast { color: rgba (0, 0, 0, 1.0); }".printf (sColor);
color_provider.load_from_data (css, -1);
style_ctx.add_provider (color_provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
@@ -234,31 +234,25 @@ public class PromptBox : FadableBox
debug ("Internal error setting color on name label: %s", e.message);
}
+ var greeter = new ArcticaGreeter();
+
name_label.valign = Gtk.Align.START;
name_label.vexpand = true;
name_label.yalign = 0.5f;
name_label.xalign = 0.0f;
name_label.margin_start = 2;
- name_label.set_size_request (-1, grid_size);
+ name_label.set_size_request (-1, greeter.grid_size);
name_label.show ();
name_grid.attach (name_label, COL_NAME_LABEL, ROW_NAME, 1, 1);
message_image = new CachedImage (null);
- try
- {
- message_image.pixbuf = new Gdk.Pixbuf.from_file (Path.build_filename (Config.PKGDATADIR, "message.png", null));
- }
- catch (Error e)
- {
- debug ("Error loading message image: %s", e.message);
- }
-
- var align = new Gtk.Alignment (0.5f, 0.5f, 0.0f, 0.0f);
- align.valign = Gtk.Align.START;
- align.set_size_request (-1, grid_size);
- align.add (message_image);
- align.show ();
- name_grid.attach (align, COL_NAME_MESSAGE, ROW_NAME, 1, 1);
+ message_image.set_from_icon_name("mail-unread", Gtk.IconSize.BUTTON);
+ message_image.halign = Gtk.Align.CENTER;
+ message_image.valign = Gtk.Align.START;
+ message_image.hexpand = false;
+ message_image.vexpand = false;
+ message_image.set_size_request (-1, greeter.grid_size);
+ name_grid.attach (message_image, COL_NAME_MESSAGE, ROW_NAME, 1, 1);
option_button = new FlatButton ();
var option_button_ctx = option_button.get_style_context ();
@@ -327,7 +321,7 @@ public class PromptBox : FadableBox
try
{
var font_provider = new Gtk.CssProvider ();
- var css = "* {font-family: %s; font-size: %dpt;}".printf (font_family, font_size);
+ var css = "* {color: #FFFFFF; font-family: %s; font-size: %dpt;}".printf (font_family, font_size);
font_provider.load_from_data (css, -1);
style_ctx.add_provider (font_provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
@@ -337,22 +331,23 @@ public class PromptBox : FadableBox
debug ("Internal error loading font style (%s, %dpt): %s", font_family, font_size, e.message);
}
- small_name_label.override_color (Gtk.StateFlags.NORMAL, { 1.0f, 1.0f, 1.0f, 1.0f });
+ var greeter = new ArcticaGreeter();
+
small_name_label.yalign = 0.5f;
small_name_label.xalign = 0.0f;
small_name_label.margin_start = 2;
- small_name_label.set_size_request (-1, grid_size);
+ small_name_label.set_size_request (-1, greeter.grid_size);
small_name_label.show ();
small_name_grid.attach (small_name_label, 1, 0, 1, 1);
small_message_image = new CachedImage (null);
- small_message_image.pixbuf = message_image.pixbuf;
-
- var align = new Gtk.Alignment (0.5f, 0.5f, 0.0f, 0.0f);
- align.set_size_request (-1, grid_size);
- align.add (small_message_image);
- align.show ();
- small_name_grid.attach (align, 2, 0, 1, 1);
+ small_message_image.set_from_icon_name("mail-unread", Gtk.IconSize.BUTTON);
+ small_message_image.halign = Gtk.Align.CENTER;
+ small_message_image.valign = Gtk.Align.CENTER;
+ small_message_image.hexpand = false;
+ small_message_image.vexpand = false;
+ small_message_image.set_size_request (-1, greeter.grid_size);
+ small_name_grid.attach (small_message_image, 2, 0, 1, 1);
small_name_grid.show ();
return small_name_grid;
@@ -371,22 +366,28 @@ public class PromptBox : FadableBox
#if HAVE_GTK_3_20_0
private int round_to_grid (int size)
{
- var num_grids = size / grid_size;
- var remainder = size % grid_size;
+ var greeter = new ArcticaGreeter();
+
+ var num_grids = size / greeter.grid_size;
+ var remainder = size % greeter.grid_size;
if (remainder > 0)
num_grids += 1;
num_grids = int.max (num_grids, 3);
- return num_grids * grid_size;
+ return num_grids * greeter.grid_size;
}
public override void get_preferred_height (out int min, out int nat)
{
base.get_preferred_height (out min, out nat);
- min = round_to_grid (min + GreeterList.BORDER * 2) - GreeterList.BORDER * 2;
- nat = round_to_grid (nat + GreeterList.BORDER * 2) - GreeterList.BORDER * 2;
+
+ var greeter = new ArcticaGreeter();
+
+ int double_border_scaled = (int)(GreeterList.BORDER * greeter.scaling_factor_widgets * 2);
+ min = round_to_grid (min + double_border_scaled) - double_border_scaled;
+ nat = round_to_grid (nat + double_border_scaled) - double_border_scaled;
if (position <= -1 || position >= 1)
- min = nat = grid_size;
+ min = nat = greeter.grid_size;
}
#endif
@@ -401,7 +402,7 @@ public class PromptBox : FadableBox
if (option_button == null)
return;
- option_image.pixbuf = image;
+ option_image.set_pixbuf (image);
if (tooltip == null)
option_image.set_tooltip_text("");
@@ -558,7 +559,19 @@ public class PromptBox : FadableBox
ArcticaGreeter.add_style_class (w);
last_row += 1;
- box_grid.attach (w, COL_ENTRIES_START, last_row, COL_ENTRIES_WIDTH, 1);
+ bool bErrorBelow = AGSettings.get_boolean (AGSettings.KEY_ERROR_BELOW_ENTRY);
+
+ if (has_errors && bErrorBelow)
+ {
+ Gtk.Widget pChild = box_grid.get_child_at (COL_ENTRIES_START, last_row-1);
+ box_grid.remove (pChild);
+ box_grid.attach (w, COL_ENTRIES_START, last_row-1, COL_ENTRIES_WIDTH, 1);
+ box_grid.attach (pChild, COL_ENTRIES_START, last_row, COL_ENTRIES_WIDTH, 1);
+ }
+ else
+ {
+ box_grid.attach (w, COL_ENTRIES_START, last_row, COL_ENTRIES_WIDTH, 1);
+ }
update_prompt_visibility (w);
queue_resize ();
@@ -566,15 +579,27 @@ public class PromptBox : FadableBox
public void add_message (string text, bool is_error)
{
- var label = new FadingLabel (text);
- label.set_line_wrap (true);
+ var label = new FadingLabel ("");
var style_ctx = label.get_style_context();
try
{
var font_provider = new Gtk.CssProvider ();
- var css = "* {font-family: %s; font-size: %dpt;}".printf (font_family, font_size-1);
+ var css = "";
+
+ if (is_error)
+ {
+ string sColor = AGSettings.get_string (AGSettings.KEY_PROMPTBOX_COLOR_ERROR);
+ double fOpacity = AGSettings.get_double (AGSettings.KEY_PROMPTBOX_ERROR_BG_OPACITY);
+ css = "* {font-family: %s; font-size: %dpt; color: %s; background-color: rgba(255, 255, 255, %f); text-shadow: none;}".printf (font_family, font_size-1, sColor, fOpacity);
+ }
+ else
+ {
+ string sColor = AGSettings.get_string (AGSettings.KEY_PROMPTBOX_COLOR_NORMAL);
+ css = "* {font-family: %s; font-size: %dpt; color: %s;} *.high_contrast {color: black; }".printf (font_family, font_size-1, sColor);
+ }
+
font_provider.load_from_data (css, -1);
style_ctx.add_provider (font_provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
@@ -584,24 +609,42 @@ public class PromptBox : FadableBox
debug ("Internal error loading font style (%s, %dpt): %s", font_family, font_size-1, e.message);
}
- Gdk.RGBA color = { 1.0f, 1.0f, 1.0f, 1.0f };
- if (is_error) {
- color.parse ("#820900");
+ label.xalign = 0.0f;
+ label.set_data<bool> ("prompt-box-is-error", is_error);
- /*
- * Overriding the background color will look ugly, but at least
- * always make the text readable, which is probably important for
- * error messages.
- * We probably want to find a better way of handling this.
- */
- Gdk.RGBA bg_color = { 1.0f, 1.0f, 1.0f, 1.0f };
- label.override_background_color (Gtk.StateFlags.NORMAL, bg_color);
- }
- label.override_color (Gtk.StateFlags.NORMAL, color);
+ // Wrap the text if needed
+ ArcticaGreeter pGreeter = new ArcticaGreeter ();
+ Pango.Context pContext = label.get_pango_context ();
+ Pango.Layout pLayout = new Pango.Layout (pContext);
+ Pango.FontDescription pDescription = null;
+ Gtk.StateFlags nFlags = style_ctx.get_state ();
+ style_ctx.get (nFlags, "font", out pDescription, null);
+ pLayout.set_font_description (pDescription);
+ StringBuilder pBuilder = new StringBuilder ();
+ string[] lWords = text.split (" ");
+ string sLine = "";
+
+ foreach (string sWord in lWords)
+ {
+ string sTest = sLine == "" ? sWord : sLine + " " + sWord;
+ pLayout.set_text (sTest, -1);
+ int nWidth = 0;
+ pLayout.get_size (out nWidth, null);
+ if (nWidth / Pango.SCALE > (pGreeter.grid_size * GreeterList.BOX_WIDTH - (int)(GreeterList.BORDER * pGreeter.scaling_factor_widgets * 8)) && sLine != "")
+ {
+ pBuilder.append (sLine + "\n");
+ sLine = sWord;
+ }
+ else
+ {
+ sLine = sTest;
+ }
+ }
- label.xalign = 0.0f;
- label.set_data<bool> ("prompt-box-is-error", is_error);
+ pBuilder.append (sLine);
+ label.set_text (pBuilder.str);
+ //~Wrap the text if needed
attach_item (label);
@@ -679,6 +722,31 @@ public class PromptBox : FadableBox
}
entry.respond.connect (entry_activate_cb);
+ Gtk.StyleContext pContext = entry.get_style_context ();
+ Gtk.CssProvider pProvider = new Gtk.CssProvider ();
+ string sKey = "";
+
+ if (has_errors)
+ {
+ sKey = AGSettings.KEY_PROMPTBOX_COLOR_ERROR;
+ }
+ else
+ {
+ sKey = AGSettings.KEY_PROMPTBOX_COLOR_NORMAL;
+ }
+
+ string sColor = AGSettings.get_string (sKey);
+ string sCss = "entry {border: none; outline: none; box-shadow: 1px 0 %s inset, 0 1px %s inset, -1px 0 %s inset, 0 -1px %s inset;}".printf (sColor, sColor, sColor, sColor);
+
+ try
+ {
+ pProvider.load_from_data (sCss, -1);
+ pContext.add_provider (pProvider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
+ }
+ catch (Error pError)
+ {
+ warning ("Panic: Error setting DashEntry border colour: %s", pError.message);
+ }
attach_item (entry);
@@ -859,13 +927,15 @@ private class ActiveIndicator : Gtk.Image
public override void get_preferred_width (out int min, out int nat)
{
- min = WIDTH;
+ var greeter = new ArcticaGreeter();
+ min = (int)Math.round(WIDTH * greeter.scaling_factor_widgets);
nat = min;
}
public override void get_preferred_height (out int min, out int nat)
{
- min = HEIGHT;
+ var greeter = new ArcticaGreeter();
+ min = (int)Math.round(HEIGHT * greeter.scaling_factor_widgets);
nat = min;
}
diff --git a/src/session-list.vala b/src/session-list.vala
index 735ca1f..c4e865f 100644
--- a/src/session-list.vala
+++ b/src/session-list.vala
@@ -47,14 +47,56 @@ public class SessionPrompt : PromptBox
}
else
{
- var sessions = LightDM.get_sessions().copy();
- sessions.sort_with_data((a, b) => GLib.strcmp (a.name.casefold().collate_key(), b.name.casefold().collate_key()));
- foreach (var session in sessions)
+ /* Pick the selected session (if any) and add it as first item.
+ */
+ var dm_sessions = LightDM.get_sessions().copy();
+ foreach (var dm_session in dm_sessions)
{
+ if (dm_session.key == session) {
+ debug ("Adding session %s (%s) as first entry", dm_session.key, dm_session.name);
+ box.add_item (dm_session.key, dm_session.name, SessionList.get_badge (dm_session.key));
+ break;
+ }
+ }
+ /* Pick the default session (if different from selected session) and add it as next item.
+ *
+ * In SUSE and derivatives, LightDM ships a distro-specific patch that hides the default.desktop
+ * (and other) symlink(s) from LightDM.
+ * https://build.opensuse.org/projects/X11:Utilities/packages/lightdm/files/lightdm-ignore-known-symlink-sessions.patch?expand=1
+ *
+ * So the next if-clause won't take effect on SUSE-based systems.
+ */
+ if (session != default_session) {
+ foreach (var dm_session in dm_sessions)
+ {
+ if (dm_session.key == default_session) {
+ debug ("Adding session %s (%s) as second entry", dm_session.key, dm_session.name);
+ box.add_item (dm_session.key, dm_session.name, SessionList.get_badge (dm_session.key));
+ break;
+ }
+ }
+ }
+
+ dm_sessions.sort_with_data((a, b) => GLib.strcmp (a.name.casefold().collate_key(), b.name.casefold().collate_key()));
+ foreach (var dm_session in dm_sessions)
+ {
+ /* Skip the selected session, we already have added that as first time.
+ */
+ if (dm_session.key == session) {
+ continue;
+ }
+
+ /* Skip the default session, we already have added that as first or second item
+ (depending on whether there was a selected session).
+ */
+ if (dm_session.key == default_session) {
+ continue;
+ }
+
/* Apply hide x11/wayland filter */
- if (greeter.validate_session(session.key, false) != null) {
- debug ("Adding session %s (%s)", session.key, session.name);
- box.add_item (session.key, session.name, SessionList.get_badge (session.key));
+ if (greeter.validate_session(dm_session.key, false) != null) {
+ debug ("Adding session %s (%s)", dm_session.key, dm_session.name);
+ box.add_item (dm_session.key, dm_session.name, SessionList.get_badge (dm_session.key));
}
}
}
@@ -79,6 +121,8 @@ public class SessionList : GreeterList
private SessionPrompt prompt;
+ private const int BADGE_SIZE = 22;
+
public SessionList (Background bg, MenuBar mb, string? session, string? default_session)
{
Object (background: bg, menubar: mb, session: session, default_session: default_session);
@@ -100,80 +144,88 @@ public class SessionList : GreeterList
protected override void add_manual_entry () {}
public override void show_authenticated (bool successful = true) {}
- private static string? get_badge_name (string session)
+ private static string? get_badge_name_from_alias_list (string session)
{
+ /*
+ * Only list aliases here, if the badge name can be derived from <session>
+ * via <session>_badge.(svg|png) then the badge file is found automatically.
+ */
switch (session)
{
- case "awesome":
- return "awesome_badge.png";
case "budgie-desktop":
return "budgie_badge.png";
- case "ubuntu":
- case "ubuntu-2d":
- case "unity":
- return "ubuntu_badge.png";
+ case "cairo-dock-fallback":
+ case "cairo-dock-unity":
+ return "cairo-dock_badge.svg";
+ case "cinnamon-wayland":
+ case "cinnamon2d":
+ return "cinnamon_badge.svg";
+ case "fvwm-crystal":
+ case "fvwm1":
+ return "fvwm_badge.png";
case "gnome-classic":
+ case "gnome-classic-xorg":
+ case "gnome-classic-wayland":
case "gnome-flashback-compiz":
case "gnome-flashback-metacity":
case "gnome-shell":
case "gnome-wayland":
case "gnome-xorg":
- case "gnome":
case "openbox-gnome":
return "gnome_badge.png";
- case "sle-classic":
- return "sleclassic_badge.png";
case "wmaker-common":
return "gnustep_badge.png";
- case "kde":
+ case "IceWM-Experimental":
+ case "IceWM-Lite":
+ case "IceWM":
+ case "icewm-session":
+ return "icewm_badge.png";
case "kde-plasma":
case "openbox-kde":
case "plasma":
case "plasma5":
case "plasmawayland":
return "kde_badge.png";
- case "i3":
case "i3-with-shmlog":
return "i3_badge.png";
- case "sway":
- return "sway_badge.svg";
+ case "default":
case "lightdm-xsession":
return "xsession_badge.png";
- case "lomiri":
- return "lomiri_badge.png";
- case "lxde":
case "LXDE":
+ case "lubuntu-nexus7":
+ case "lxgames":
+ case "Lubuntu":
+ case "Lubuntu-Netbook":
+ case "QLubuntu":
return "lxde_badge.png";
- case "lxqt":
case "LXQt":
return "lxqt_badge.png";
- case "matchbox":
- return "matchbox_badge.png";
- case "mate":
- return "mate_badge.png";
case "mir-shell":
return "mirshell_badge.png";
- case "openbox":
- return "openbox_badge.png";
- case "pademelon":
- return "pademelon_badge.png";
- case "sugar":
+ case "sle-classic":
+ return "sleclassic_badge.png";
+ case "sugar-session-0.84":
+ case "sugar-session-0.86":
+ case "sugar-session-0.88":
+ case "sugar-session-0.90":
+ case "sugar-session-0.96":
+ case "sugar-session-0.98":
+ case "usr":
return "sugar_badge.png";
case "surf-display":
return "surf_badge.png";
- case "twm":
- return "twm_badge.png";
- case "xfce":
+ case "ubuntu-2d":
+ case "ubuntu-xorg":
+ case "unity":
+ return "ubuntu_badge.png";
+ case "XBMC":
+ return "xbmc_badge.png";
+ case "xubuntu":
return "xfce_badge.png";
case "xterm":
return "recovery_console_badge.png";
- case "xmonad":
+ case "gnome-xmonad":
return "xmonad_badge.png";
- case "icewm":
- case "icewm-session":
- return "icewm_badge.png";
- case "fynedesk":
- return "fynedesk_badge.svg";
case "remote-login":
return "remote_login_help.png";
default:
@@ -184,7 +236,47 @@ public class SessionList : GreeterList
private static HashTable<string, Gdk.Pixbuf> badges; /* cache of badges */
public static Gdk.Pixbuf? get_badge (string session)
{
- var name = get_badge_name (session);
+ if (session == "default")
+ {
+ var sessions = LightDM.get_sessions().copy();
+ foreach (var find_session in sessions)
+ {
+ if (find_session.key == "default")
+ {
+ foreach (var real_session in sessions)
+ {
+ // Skip the default session
+ if (real_session.key == "default") {
+ continue;
+ }
+
+ // If we find the real session, use its key value for badge icon loading...
+ if (real_session.name == find_session.name)
+ {
+ session = real_session.key;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ var name = get_badge_name_from_alias_list (session);
+
+ if (name == null)
+ {
+ var default_name_svg = "%s_badge.svg".printf (session);
+ var default_name_png = "%s_badge.png".printf (session);
+ var default_name_svg_path = Path.build_filename (Config.PKGDATADIR, default_name_svg, null);
+ var default_name_png_path = Path.build_filename (Config.PKGDATADIR, default_name_png, null);
+ if (FileUtils.test (default_name_svg_path, FileTest.EXISTS)) {
+ name = default_name_svg;
+ }
+ else if (FileUtils.test (default_name_png_path, FileTest.EXISTS)) {
+ name = default_name_png;
+ }
+ }
if (name == null)
{
@@ -206,7 +298,11 @@ public class SessionList : GreeterList
{
try
{
- pixbuf = new Gdk.Pixbuf.from_file (Path.build_filename (Config.PKGDATADIR, name, null));
+ var greeter = new ArcticaGreeter();
+
+ pixbuf = new Gdk.Pixbuf.from_file_at_size (Path.build_filename (Config.PKGDATADIR, name, null),
+ (int)(BADGE_SIZE * _scale_factor * greeter.scaling_factor_widgets),
+ (int)(BADGE_SIZE * _scale_factor * greeter.scaling_factor_widgets));
badges.insert (name, pixbuf);
}
catch (Error e)
diff --git a/src/settings-daemon.vala b/src/settings-daemon.vala
index be34e9c..af6ff90 100644
--- a/src/settings-daemon.vala
+++ b/src/settings-daemon.vala
@@ -25,9 +25,10 @@ public class SettingsDaemon : Object
{
private int sd_pid = 0;
private int logind_inhibit_fd = -1;
- private ScreenSaverInterface screen_saver;
+ private GnomeScreenSaverInterface gnome_screen_saver;
+ private MateScreenSaverInterface mate_screen_saver;
private SessionManagerInterface session_manager;
- private int n_names = 2;
+ private int n_names = 3;
public void start ()
{
@@ -83,14 +84,15 @@ public class SettingsDaemon : Object
* the event to trigger this (which actually comes from mate-session).
* We implement the mate-screensaver interface and start the settings
* daemon once it is registered on the bus so mate-screensaver is not
- * started when it accesses this interface */
- screen_saver = new ScreenSaverInterface ();
+ * started when it accesses this interface.
+ */
+ gnome_screen_saver = new GnomeScreenSaverInterface ();
GLib.Bus.own_name (BusType.SESSION, "org.gnome.ScreenSaver", BusNameOwnerFlags.NONE,
(c) =>
{
try
{
- c.register_object ("/org/gnome/ScreenSaver", screen_saver);
+ c.register_object ("/org/gnome/ScreenSaver", gnome_screen_saver);
}
catch (Error e)
{
@@ -104,6 +106,29 @@ public class SettingsDaemon : Object
},
() => debug ("Failed to acquire name org.gnome.ScreenSaver"));
+ /* MATE components (e.g. mate-notification-daemon) expect org.mate.ScreenSaver being
+ * available. Mimick it so that the real mate-screensaver won't come up.
+ */
+ mate_screen_saver = new MateScreenSaverInterface ();
+ GLib.Bus.own_name (BusType.SESSION, "org.mate.ScreenSaver", BusNameOwnerFlags.NONE,
+ (c) =>
+ {
+ try
+ {
+ c.register_object ("/org/mate/ScreenSaver", mate_screen_saver);
+ }
+ catch (Error e)
+ {
+ warning ("Failed to register /org/mate/ScreenSaver: %s", e.message);
+ }
+ },
+ () =>
+ {
+ debug ("Acquired org.mate.ScreenSaver");
+ start_settings_daemon ();
+ },
+ () => debug ("Failed to acquire name org.mate.ScreenSaver"));
+
/* The media-keys plugin inhibits the power key, but we don't want
all the other keys doing things. So inhibit it ourselves */
/* NOTE: We are using the synchronous method here since there is a bug in Vala/GLib in that
@@ -180,6 +205,24 @@ public class SettingsDaemon : Object
{
debug ("Could not start %s: %s", Config.SD_BINARY, e.message);
}
+
+ /* Start Ayatana Indicators...
+ * The indicator start has been moved here, because the session
+ * indicator requires org.gnome.ScreenSaver to have been setup
+ * accurately (which is happening before the settings-daemon start).
+ */
+ debug ("Launching Ayatana Indicators...");
+ var greeter = new ArcticaGreeter();
+ Timeout.add (50, () =>
+ {
+ greeter.start_notification_daemon ();
+ return false;
+ });
+ Timeout.add (100, () =>
+ {
+ greeter.start_indicators ();
+ return false;
+ });
}
private void stop_settings_daemon ()
@@ -203,7 +246,6 @@ public class SettingsDaemon : Object
}
-[DBus (name="org.gnome.ScreenSaver")]
public class ScreenSaverInterface : Object
{
public signal void active_changed (bool value);
@@ -283,9 +325,24 @@ public class ScreenSaverInterface : Object
public void simulate_user_activity () throws GLib.DBusError, GLib.IOError {}
}
+[DBus (name="org.gnome.ScreenSaver")]
+public class GnomeScreenSaverInterface : ScreenSaverInterface {}
+
+[DBus (name="org.mate.ScreenSaver")]
+public class MateScreenSaverInterface : ScreenSaverInterface {}
+
[DBus (name="org.gnome.SessionManager")]
public class SessionManagerInterface : Object
{
+ private int client_id = 0;
+
+ public async ObjectPath RegisterClient(string app_id, string client_startup_id) throws GLib.DBusError, GLib.IOError
+ {
+ client_id++;
+ string path = "/org/ArcticaProject/artica_greeter/client/%d".printf (client_id);
+ debug ("Our fake org.gnome.SessionManager received RegisterClient request (app_id: %s, client_startup_id: %s), returning ObjectPath %s", app_id, client_startup_id, path);
+ return (ObjectPath)path;
+ }
public bool session_is_active { get { return true; } }
public string session_name { get { return "greeter"; } }
public uint32 inhibited_actions { get { return 0; } }
diff --git a/src/settings.vala b/src/settings.vala
index 8447c0f..2a89c38 100644
--- a/src/settings.vala
+++ b/src/settings.vala
@@ -3,7 +3,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>
- * Copyright (C) 2023 Robert Tari
+ * Copyright (C) 2023-2025 Robert Tari
*
* 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
@@ -43,6 +43,7 @@ public class AGSettings : Object
public const string KEY_CURSOR_THEME_NAME = "cursor-theme-name";
public const string KEY_CURSOR_THEME_SIZE = "cursor-theme-size";
public const string KEY_FONT_NAME = "font-name";
+ public const string KEY_WINDOW_MANAGER = "window-manager";
public const string KEY_XFT_ANTIALIAS = "xft-antialias";
public const string KEY_XFT_DPI = "xft-dpi";
public const string KEY_XFT_HINTSTYLE = "xft-hintstyle";
@@ -56,6 +57,9 @@ public class AGSettings : Object
public const string KEY_PLAY_READY_SOUND = "play-ready-sound";
public const string KEY_INDICATORS = "indicators";
public const string KEY_HIDDEN_USERS = "hidden-users";
+ public const string KEY_HIDDEN_GROUPS = "hidden-groups";
+ public const string KEY_USER_FILTER= "user-filter";
+ public const string KEY_USER_FILTER_ALWAYS = "user-filter-always";
public const string KEY_GROUP_FILTER = "group-filter";
public const string KEY_IDLE_TIMEOUT = "idle-timeout";
public const string KEY_ACTIVATE_NUMLOCK = "activate-numlock";
@@ -74,6 +78,7 @@ public class AGSettings : Object
public const string KEY_FLATBUTTON_BORDERCOLOR = "flatbutton-bordercolor";
public const string KEY_ENABLE_HIDPI = "enable-hidpi";
public const string KEY_FONT_SCALING = "font-scaling";
+ public const string KEY_WIDGET_SCALING = "widget-scaling";
public const string KEY_MENUBAR_ALPHA = "menubar-alpha";
public const string KEY_HIDE_DEFAULT_XSESSION = "hide-default-xsession";
public const string KEY_HIDE_X11_SESSIONS = "hide-x11-sessions";
@@ -84,6 +89,21 @@ public class AGSettings : Object
public const string KEY_PREFERRED_SESSIONS = "preferred-sessions";
public const string KEY_GEOCLUE_AGENT = "geoclue-agent";
public const string KEY_MAGNIFIER = "magnifier";
+ public const string KEY_CONTENT_ALIGN = "content-align";
+ public const string KEY_MAGNIFIER_POSITION = "magnifier-position";
+ public const string KEY_DASHBOX_BGCOLOR = "dash-box-bgcolor";
+ public const string KEY_DASHBOX_OPACITY = "dash-box-opacity";
+ public const string KEY_PROMPTBOX_COLOR_NORMAL = "prompt-box-color-normal";
+ public const string KEY_PROMPTBOX_COLOR_ERROR = "prompt-box-color-error";
+ public const string KEY_PROMPTBOX_ERROR_BG_OPACITY = "prompt-box-error-bg-opacity";
+ public const string KEY_LOGO_POSITION = "logo-position";
+ public const string KEY_LOGO_OFFSET_HORIZONTAL = "logo-offset-horizontal";
+ public const string KEY_LOGO_OFFSET_VERTICAL = "logo-offset-vertical";
+ public const string KEY_ERROR_BELOW_ENTRY = "error-below-entry";
+ public const string KEY_MENUBAR_BGCOLOR = "menubar-bgcolor";
+ public const string KEY_BACKGROUND_POSITION = "background-position";
+ public const string KEY_MENUBAR_SHADOW_COLOR = "menubar-shadow-color";
+ public const string KEY_MENUBAR_SHADOW_ALPHA = "menubar-shadow-alpha";
public static bool get_boolean (string key)
{
@@ -91,19 +111,6 @@ public class AGSettings : Object
return gsettings.get_boolean (key);
}
- /* LP: 1006497 - utility function to make sure we have the key before trying to read it (which will segfault if the key isn't there) */
- public static bool safe_get_boolean (string key, bool default)
- {
- var gsettings = new Settings (SCHEMA);
- string[] keys = gsettings.list_keys ();
- foreach (var k in keys)
- if (k == key)
- return gsettings.get_boolean (key);
-
- /* key not in child list */
- return default;
- }
-
public static int get_integer (string key)
{
var gsettings = new Settings (SCHEMA);
diff --git a/src/shutdown-dialog.vala b/src/shutdown-dialog.vala
index d243188..fd568d7 100644
--- a/src/shutdown-dialog.vala
+++ b/src/shutdown-dialog.vala
@@ -2,7 +2,7 @@
*
* Copyright (C) 2013 Canonical Ltd
* Copyright (C) 2015,2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
- * Copyright (C) 2023 Robert Tari
+ * Copyright (C) 2023-2025 Robert Tari
*
* 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
@@ -306,7 +306,8 @@ public class ShutdownDialog : Gtk.Fixed
var focused = pWindow.get_focus ();
if ((null != focused) && (focused is DialogButton))
{
- (focused as DialogButton).clicked ();
+ DialogButton pButton = (DialogButton) focused;
+ pButton.clicked ();
}
--default_action_time_supplemental;
@@ -695,7 +696,7 @@ private class DialogButton : Gtk.Button
try
{
var font_provider = new Gtk.CssProvider ();
- var css = "* {font-family: %s; font-size: %dpt;}".printf(font_family, font_size);
+ var css = "* {color: #FFFFFF; font-family: %s; font-size: %dpt;}".printf(font_family, font_size);
font_provider.load_from_data (css, -1);
style_ctx.add_provider (font_provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
@@ -705,9 +706,6 @@ private class DialogButton : Gtk.Button
debug ("Internal error loading font style (%s, %dpt): %s", font_family, font_size, e.message);
}
- l.override_color (Gtk.StateFlags.NORMAL, { 1.0f, 1.0f, 1.0f, 0.0f });
- l.override_color (Gtk.StateFlags.FOCUSED, { 1.0f, 1.0f, 1.0f, 1.0f });
- l.override_color (Gtk.StateFlags.ACTIVE, { 1.0f, 1.0f, 1.0f, 1.0f });
this.get_accessible ().set_name (l.get_text ());
}
diff --git a/src/user-list.vala b/src/user-list.vala
index dee3850..3abfb06 100644
--- a/src/user-list.vala
+++ b/src/user-list.vala
@@ -87,10 +87,28 @@ public class UserList : GreeterList
}
var hidden_users = AGSettings.get_strv (AGSettings.KEY_HIDDEN_USERS);
+ string[] lHiddenGroups = AGSettings.get_strv (AGSettings.KEY_HIDDEN_GROUPS);
+
if (!value)
{
foreach (var username in hidden_users)
remove_entry (username);
+
+ foreach (string sGroup in lHiddenGroups)
+ {
+ LightDM.UserList lUsers = LightDM.UserList.get_instance ();
+
+ foreach (LightDM.User pUser in lUsers.users)
+ {
+ bool bInGroup = in_group (sGroup, pUser.name);
+
+ if (bInGroup)
+ {
+ remove_entry (pUser.name);
+ }
+ }
+ }
+
return;
}
@@ -1126,6 +1144,32 @@ public class UserList : GreeterList
foreach (var username in hidden_users)
if (username == user.name)
return;
+
+ string[] lHiddenGroups = AGSettings.get_strv (AGSettings.KEY_HIDDEN_GROUPS);
+
+ foreach (string sGroup in lHiddenGroups)
+ {
+ bool bInGroup = in_group (sGroup, user.name);
+
+ if (bInGroup)
+ {
+ return;
+ }
+ }
+ }
+
+ var user_filter = AGSettings.get_strv (AGSettings.KEY_USER_FILTER);
+ bool user_filter_always = AGSettings.get_boolean (AGSettings.KEY_USER_FILTER_ALWAYS);
+
+ if ((user_filter_always) || (user_filter.length != 0))
+ {
+ var match_found = false;
+ foreach (var username in user_filter)
+ if (username == user.name)
+ match_found = true;
+ // bail-out if user.name is not in user_filter
+ if (match_found == false)
+ return;
}
if (!filter_group (user.name))