diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/animate-timer.vala | 4 | ||||
-rw-r--r-- | src/arctica-greeter.vala | 598 | ||||
-rw-r--r-- | src/background.vala | 69 | ||||
-rw-r--r-- | src/cached-image.vala | 35 | ||||
-rw-r--r-- | src/dash-box.vala | 23 | ||||
-rw-r--r-- | src/greeter-list.vala | 55 | ||||
-rw-r--r-- | src/list-stack.vala | 6 | ||||
-rw-r--r-- | src/main-window.vala | 314 | ||||
-rw-r--r-- | src/menubar.vala | 273 | ||||
-rw-r--r-- | src/prompt-box.vala | 192 | ||||
-rw-r--r-- | src/session-list.vala | 184 | ||||
-rw-r--r-- | src/settings-daemon.vala | 69 | ||||
-rw-r--r-- | src/settings.vala | 35 | ||||
-rw-r--r-- | src/shutdown-dialog.vala | 10 | ||||
-rw-r--r-- | src/user-list.vala | 44 |
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)) |