From e83dd571de2b5aa98ef1d27ca91c9ac3dbf8a1e2 Mon Sep 17 00:00:00 2001 From: Leonhard Date: Wed, 24 Jan 2024 00:18:53 +0100 Subject: [PATCH 01/22] Add basic schedule management --- src/Application.vala | 5 +++- src/Backends/ManualSchedule.vala | 37 +++++++++++++++++++++++++ src/Backends/Schedule.vala | 15 +++++++++++ src/Backends/ScheduleManager.vala | 45 +++++++++++++++++++++++++++++++ src/meson.build | 3 +++ 5 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 src/Backends/ManualSchedule.vala create mode 100644 src/Backends/Schedule.vala create mode 100644 src/Backends/ScheduleManager.vala diff --git a/src/Application.vala b/src/Application.vala index 09855167..693af2ad 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -17,6 +17,8 @@ public sealed class SettingsDaemon.Application : Gtk.Application { private Backends.Housekeeping housekeeping; + private Backends.ScheduleManager schedule_manager; + private const string FDO_ACCOUNTS_NAME = "org.freedesktop.Accounts"; private const string FDO_ACCOUNTS_PATH = "/org/freedesktop/Accounts"; @@ -99,7 +101,8 @@ public sealed class SettingsDaemon.Application : Gtk.Application { try { pantheon_service = yield connection.get_proxy (FDO_ACCOUNTS_NAME, path, GET_INVALIDATED_PROPERTIES); - prefers_color_scheme_settings = new Backends.PrefersColorSchemeSettings (pantheon_service); + // prefers_color_scheme_settings = new Backends.PrefersColorSchemeSettings (pantheon_service); + schedule_manager = new Backends.ScheduleManager (pantheon_service); } catch { warning ("Unable to get pantheon's AccountsService proxy, color scheme preference may be incorrect"); } diff --git a/src/Backends/ManualSchedule.vala b/src/Backends/ManualSchedule.vala new file mode 100644 index 00000000..770ed50d --- /dev/null +++ b/src/Backends/ManualSchedule.vala @@ -0,0 +1,37 @@ +public class SettingsDaemon.Backends.ManualSchedule : Schedule { + public double from { get; construct; } + public double to { get; construct; } + + public ManualSchedule (double from, double to) { + Object (from: from, to: to); + } + + construct { + Timeout.add (1000, time_callback); + } + + private bool time_callback () { + var is_in = is_in_time_window (); + + if (active != is_in) { + active = is_in; + } + + return Source.CONTINUE; + } + + private bool is_in_time_window () { + var date_time = new DateTime.now_local (); + double time_double = 0; + time_double += date_time.get_hour (); + time_double += (double) date_time.get_minute () / 60; + + // PM to AM + if (from > to) { + return time_double < to ? time_double <= from : time_double >= from; + } + + // AM to AM, PM to PM, AM to PM + return (time_double >= from && time_double <= to); + } +} \ No newline at end of file diff --git a/src/Backends/Schedule.vala b/src/Backends/Schedule.vala new file mode 100644 index 00000000..39be317c --- /dev/null +++ b/src/Backends/Schedule.vala @@ -0,0 +1,15 @@ +public class SettingsDaemon.Backends.Schedule : Object { + public bool active { get; protected set; default = false; } + public HashTable active_settings { get; private set; } + public HashTable inactive_settings { get; private set; } //Inactive settings should usually be !active_settings but can also be a default wallpaper path + + construct { + active_settings = new HashTable (str_hash, str_equal); + inactive_settings = new HashTable (str_hash, str_equal); + } + + public void add_boolean (string key, bool val) { + active_settings[key] = val; + inactive_settings[key] = !val; + } +} \ No newline at end of file diff --git a/src/Backends/ScheduleManager.vala b/src/Backends/ScheduleManager.vala new file mode 100644 index 00000000..39e037a8 --- /dev/null +++ b/src/Backends/ScheduleManager.vala @@ -0,0 +1,45 @@ +public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { + private const string NIGHT_LIGHT = "night-light"; + private const string DARK_MODE = "dark-mode"; + + public unowned Pantheon.AccountsService pantheon_service { get; construct; } + + private List schedules = new List (); + + public ScheduleManager (Pantheon.AccountsService pantheon_service ) { + Object (pantheon_service: pantheon_service); + } + + construct { + var schedule = new ManualSchedule (0.2, 10); + schedule.add_boolean (DARK_MODE, true); + + add_schedule (schedule); + } + + private void add_schedule (Schedule schedule) { + schedule.notify["active"].connect (() => { + activate_settings (schedule.active ? schedule.active_settings : schedule.inactive_settings); + }); + activate_settings (schedule.active ? schedule.active_settings : schedule.inactive_settings); + + schedules.append (schedule); + } + + public void activate_settings (HashTable settings) { + foreach (var key in settings.get_keys ()) { + switch (key) { + case NIGHT_LIGHT: + if (((bool?) settings[NIGHT_LIGHT]) != null) { + // accounts_service.night_light_enabled = (bool) settings[NIGHT_LIGHT]; + } + break; + case DARK_MODE: + pantheon_service.prefers_color_scheme = ((bool) settings[DARK_MODE]) ? Granite.Settings.ColorScheme.DARK : Granite.Settings.ColorScheme.LIGHT; + break; + default: + break; + } + } + } +} \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index 9c05e9a7..d627ba8b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -7,6 +7,9 @@ sources = files( 'Backends/MouseSettings.vala', 'Backends/NightLightSettings.vala', 'Backends/PrefersColorSchemeSettings.vala', + 'Backends/Schedule.vala', + 'Backends/ScheduleManager.vala', + 'Backends/ManualSchedule.vala', 'Utils/SunriseSunsetCalculator.vala', ) From 03647733f9353900ff90ffe09bdb7800e941ad0e Mon Sep 17 00:00:00 2001 From: Leonhard Date: Wed, 24 Jan 2024 00:48:27 +0100 Subject: [PATCH 02/22] Add storing --- .../io.elementary.settings-daemon.gschema.xml | 8 +++++ src/Backends/ManualSchedule.vala | 17 ++++++++-- src/Backends/Schedule.vala | 31 +++++++++++++++++-- src/Backends/ScheduleManager.vala | 15 ++++++--- 4 files changed, 63 insertions(+), 8 deletions(-) diff --git a/data/io.elementary.settings-daemon.gschema.xml b/data/io.elementary.settings-daemon.gschema.xml index 8ff68a1a..dafbd08a 100644 --- a/data/io.elementary.settings-daemon.gschema.xml +++ b/data/io.elementary.settings-daemon.gschema.xml @@ -70,4 +70,12 @@ The day for calendars to use as the first day of the week + + + + [] + The current schedules. + a(a{sv}a{sv}) + + diff --git a/src/Backends/ManualSchedule.vala b/src/Backends/ManualSchedule.vala index 770ed50d..6a4fcf79 100644 --- a/src/Backends/ManualSchedule.vala +++ b/src/Backends/ManualSchedule.vala @@ -1,11 +1,17 @@ public class SettingsDaemon.Backends.ManualSchedule : Schedule { - public double from { get; construct; } - public double to { get; construct; } + public double from { get; construct set; } + public double to { get; construct set; } public ManualSchedule (double from, double to) { Object (from: from, to: to); } + public ManualSchedule.from_parsed (Parsed parsed) { + base.from_parsed (parsed); + from = (double) parsed.args["from"]; + to = (double) parsed.args["to"]; + } + construct { Timeout.add (1000, time_callback); } @@ -34,4 +40,11 @@ public class SettingsDaemon.Backends.ManualSchedule : Schedule { // AM to AM, PM to PM, AM to PM return (time_double >= from && time_double <= to); } + + protected override HashTable get_private_args () { + var result = new HashTable (str_hash, str_equal); + result["from"] = from; + result["to"] = to; + return result; + } } \ No newline at end of file diff --git a/src/Backends/Schedule.vala b/src/Backends/Schedule.vala index 39be317c..ad19250a 100644 --- a/src/Backends/Schedule.vala +++ b/src/Backends/Schedule.vala @@ -1,15 +1,42 @@ public class SettingsDaemon.Backends.Schedule : Object { + public struct Parsed { + int type; + HashTable args; + HashTable active_settings; + HashTable inactive_settings; + } + public bool active { get; protected set; default = false; } public HashTable active_settings { get; private set; } - public HashTable inactive_settings { get; private set; } //Inactive settings should usually be !active_settings but can also be a default wallpaper path + public HashTable inactive_settings { get; private set; } //Inactive settings should usually be !active_settings but can also be e.g. a default wallpaper path - construct { + public Schedule () { active_settings = new HashTable (str_hash, str_equal); inactive_settings = new HashTable (str_hash, str_equal); } + public Schedule.from_parsed (Parsed parsed) { + active_settings = parsed.active_settings; + inactive_settings = parsed.inactive_settings; + } + + /* Convenience method to add the same boolean inverted to inactive settings */ public void add_boolean (string key, bool val) { active_settings[key] = val; inactive_settings[key] = !val; } + + public Parsed get_parsed () { + Parsed result = { + 0, + get_private_args (), + active_settings, + inactive_settings + }; + return result; + } + + protected virtual HashTable get_private_args () { + return new HashTable (str_hash, str_equal); + } } \ No newline at end of file diff --git a/src/Backends/ScheduleManager.vala b/src/Backends/ScheduleManager.vala index 39e037a8..410026fa 100644 --- a/src/Backends/ScheduleManager.vala +++ b/src/Backends/ScheduleManager.vala @@ -2,6 +2,8 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { private const string NIGHT_LIGHT = "night-light"; private const string DARK_MODE = "dark-mode"; + private static Settings settings = new Settings ("io.elementary.settings-daemon.schedules"); + public unowned Pantheon.AccountsService pantheon_service { get; construct; } private List schedules = new List (); @@ -11,10 +13,15 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { } construct { - var schedule = new ManualSchedule (0.2, 10); - schedule.add_boolean (DARK_MODE, true); - - add_schedule (schedule); + foreach (var parsed_schedule in (Schedule.Parsed[]) settings.get_value ("schedules")) { + switch (parsed_schedule.type) { + case 0: + add_schedule (new ManualSchedule.from_parsed (parsed_schedule)); + break; + default: + break; + } + } } private void add_schedule (Schedule schedule) { From 15789324241ac0a7a73b2e902fff4e818996133f Mon Sep 17 00:00:00 2001 From: Leonhard Date: Wed, 24 Jan 2024 00:52:40 +0100 Subject: [PATCH 03/22] Folder structure --- src/Backends/{ => ScheduleManager}/ManualSchedule.vala | 0 src/Backends/{ => ScheduleManager}/Schedule.vala | 6 +++++- src/Backends/{ => ScheduleManager}/ScheduleManager.vala | 0 src/meson.build | 6 +++--- 4 files changed, 8 insertions(+), 4 deletions(-) rename src/Backends/{ => ScheduleManager}/ManualSchedule.vala (100%) rename src/Backends/{ => ScheduleManager}/Schedule.vala (95%) rename src/Backends/{ => ScheduleManager}/ScheduleManager.vala (100%) diff --git a/src/Backends/ManualSchedule.vala b/src/Backends/ScheduleManager/ManualSchedule.vala similarity index 100% rename from src/Backends/ManualSchedule.vala rename to src/Backends/ScheduleManager/ManualSchedule.vala diff --git a/src/Backends/Schedule.vala b/src/Backends/ScheduleManager/Schedule.vala similarity index 95% rename from src/Backends/Schedule.vala rename to src/Backends/ScheduleManager/Schedule.vala index ad19250a..c0c724f9 100644 --- a/src/Backends/Schedule.vala +++ b/src/Backends/ScheduleManager/Schedule.vala @@ -1,6 +1,10 @@ public class SettingsDaemon.Backends.Schedule : Object { + public enum Type { + MANUAL + } + public struct Parsed { - int type; + Type type; HashTable args; HashTable active_settings; HashTable inactive_settings; diff --git a/src/Backends/ScheduleManager.vala b/src/Backends/ScheduleManager/ScheduleManager.vala similarity index 100% rename from src/Backends/ScheduleManager.vala rename to src/Backends/ScheduleManager/ScheduleManager.vala diff --git a/src/meson.build b/src/meson.build index d627ba8b..3c6922ae 100644 --- a/src/meson.build +++ b/src/meson.build @@ -7,9 +7,9 @@ sources = files( 'Backends/MouseSettings.vala', 'Backends/NightLightSettings.vala', 'Backends/PrefersColorSchemeSettings.vala', - 'Backends/Schedule.vala', - 'Backends/ScheduleManager.vala', - 'Backends/ManualSchedule.vala', + 'Backends' / 'ScheduleManager' / 'ManualSchedule.vala', + 'Backends' / 'ScheduleManager' / 'Schedule.vala', + 'Backends' / 'ScheduleManager' / 'ScheduleManager.vala', 'Utils/SunriseSunsetCalculator.vala', ) From 767463522ae1ec43338d4d68cc9fc5fbcf3b7480 Mon Sep 17 00:00:00 2001 From: Leonhard Date: Wed, 24 Jan 2024 01:02:25 +0100 Subject: [PATCH 04/22] Fixes + add daylight schedule --- .../ScheduleManager/DaylightSchedule.vala | 62 +++++++++++++++++++ src/Backends/ScheduleManager/Schedule.vala | 3 +- .../ScheduleManager/ScheduleManager.vala | 9 +-- src/meson.build | 1 + 4 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 src/Backends/ScheduleManager/DaylightSchedule.vala diff --git a/src/Backends/ScheduleManager/DaylightSchedule.vala b/src/Backends/ScheduleManager/DaylightSchedule.vala new file mode 100644 index 00000000..0cac002e --- /dev/null +++ b/src/Backends/ScheduleManager/DaylightSchedule.vala @@ -0,0 +1,62 @@ +public class SettingsDaemon.Backends.DaylightSchedule : Schedule { + private double sunrise = 6.0; + private double sunset = 20.0; + + public DaylightSchedule.from_parsed (Parsed parsed) { + base.from_parsed (parsed); + } + + construct { + get_location.begin (); + Timeout.add (1000, time_callback); + } + + private bool time_callback () { + var is_in = is_in_time_window (); + + if (active != is_in) { + active = is_in; + } + + return Source.CONTINUE; + } + + private async void get_location () { + try { + var simple = yield new GClue.Simple (Build.PROJECT_NAME, GClue.AccuracyLevel.CITY, null); + + simple.notify["location"].connect (() => { + on_location_updated (simple.location.latitude, simple.location.longitude); + }); + + on_location_updated (simple.location.latitude, simple.location.longitude); + } catch (Error e) { + warning ("Failed to connect to GeoClue2 service: %s", e.message); + return; + } + } + + private void on_location_updated (double latitude, double longitude) { + var now = new DateTime.now_local (); + double _sunrise, _sunset; + if (SettingsDaemon.Utils.SunriseSunsetCalculator.get_sunrise_and_sunset (now, latitude, longitude, out _sunrise, out _sunset)) { + sunrise = _sunrise; + sunset = _sunset; + } + } + + private bool is_in_time_window () { + var date_time = new DateTime.now_local (); + double time_double = 0; + time_double += date_time.get_hour (); + time_double += (double) date_time.get_minute () / 60; + + // PM to AM + if (sunset > sunrise) { + return time_double < sunrise ? time_double <= sunset : time_double >= sunset; + } + + // AM to AM, PM to PM, AM to PM + return (time_double >= sunset && time_double <= sunrise); + } +} \ No newline at end of file diff --git a/src/Backends/ScheduleManager/Schedule.vala b/src/Backends/ScheduleManager/Schedule.vala index c0c724f9..d6ad48f2 100644 --- a/src/Backends/ScheduleManager/Schedule.vala +++ b/src/Backends/ScheduleManager/Schedule.vala @@ -1,6 +1,7 @@ public class SettingsDaemon.Backends.Schedule : Object { public enum Type { - MANUAL + MANUAL, + DAYLIGHT } public struct Parsed { diff --git a/src/Backends/ScheduleManager/ScheduleManager.vala b/src/Backends/ScheduleManager/ScheduleManager.vala index 410026fa..4af4769e 100644 --- a/src/Backends/ScheduleManager/ScheduleManager.vala +++ b/src/Backends/ScheduleManager/ScheduleManager.vala @@ -15,9 +15,12 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { construct { foreach (var parsed_schedule in (Schedule.Parsed[]) settings.get_value ("schedules")) { switch (parsed_schedule.type) { - case 0: + case MANUAL: add_schedule (new ManualSchedule.from_parsed (parsed_schedule)); break; + case DAYLIGHT: + add_schedule (new DaylightSchedule.from_parsed (parsed_schedule)); + break; default: break; } @@ -37,9 +40,7 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { foreach (var key in settings.get_keys ()) { switch (key) { case NIGHT_LIGHT: - if (((bool?) settings[NIGHT_LIGHT]) != null) { - // accounts_service.night_light_enabled = (bool) settings[NIGHT_LIGHT]; - } + //TODO break; case DARK_MODE: pantheon_service.prefers_color_scheme = ((bool) settings[DARK_MODE]) ? Granite.Settings.ColorScheme.DARK : Granite.Settings.ColorScheme.LIGHT; diff --git a/src/meson.build b/src/meson.build index 3c6922ae..80b80a19 100644 --- a/src/meson.build +++ b/src/meson.build @@ -7,6 +7,7 @@ sources = files( 'Backends/MouseSettings.vala', 'Backends/NightLightSettings.vala', 'Backends/PrefersColorSchemeSettings.vala', + 'Backends' / 'ScheduleManager' / 'DaylightSchedule.vala', 'Backends' / 'ScheduleManager' / 'ManualSchedule.vala', 'Backends' / 'ScheduleManager' / 'Schedule.vala', 'Backends' / 'ScheduleManager' / 'ScheduleManager.vala', From 336c43960dfc3192be28d5ce3518494652e462bd Mon Sep 17 00:00:00 2001 From: Leonhard Date: Wed, 24 Jan 2024 01:06:12 +0100 Subject: [PATCH 05/22] Lint --- src/Backends/ScheduleManager/DaylightSchedule.vala | 2 +- src/Backends/ScheduleManager/ManualSchedule.vala | 2 +- src/Backends/ScheduleManager/Schedule.vala | 2 +- src/Backends/ScheduleManager/ScheduleManager.vala | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Backends/ScheduleManager/DaylightSchedule.vala b/src/Backends/ScheduleManager/DaylightSchedule.vala index 0cac002e..b0670b37 100644 --- a/src/Backends/ScheduleManager/DaylightSchedule.vala +++ b/src/Backends/ScheduleManager/DaylightSchedule.vala @@ -59,4 +59,4 @@ public class SettingsDaemon.Backends.DaylightSchedule : Schedule { // AM to AM, PM to PM, AM to PM return (time_double >= sunset && time_double <= sunrise); } -} \ No newline at end of file +} diff --git a/src/Backends/ScheduleManager/ManualSchedule.vala b/src/Backends/ScheduleManager/ManualSchedule.vala index 6a4fcf79..63b464eb 100644 --- a/src/Backends/ScheduleManager/ManualSchedule.vala +++ b/src/Backends/ScheduleManager/ManualSchedule.vala @@ -47,4 +47,4 @@ public class SettingsDaemon.Backends.ManualSchedule : Schedule { result["to"] = to; return result; } -} \ No newline at end of file +} diff --git a/src/Backends/ScheduleManager/Schedule.vala b/src/Backends/ScheduleManager/Schedule.vala index d6ad48f2..8d34e402 100644 --- a/src/Backends/ScheduleManager/Schedule.vala +++ b/src/Backends/ScheduleManager/Schedule.vala @@ -44,4 +44,4 @@ public class SettingsDaemon.Backends.Schedule : Object { protected virtual HashTable get_private_args () { return new HashTable (str_hash, str_equal); } -} \ No newline at end of file +} diff --git a/src/Backends/ScheduleManager/ScheduleManager.vala b/src/Backends/ScheduleManager/ScheduleManager.vala index 4af4769e..bc70ccd9 100644 --- a/src/Backends/ScheduleManager/ScheduleManager.vala +++ b/src/Backends/ScheduleManager/ScheduleManager.vala @@ -50,4 +50,4 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { } } } -} \ No newline at end of file +} From 335cf2d4ebc7ef5b1bfff3178940527c7a5a3869 Mon Sep 17 00:00:00 2001 From: Leonhard Date: Wed, 24 Jan 2024 01:41:20 +0100 Subject: [PATCH 06/22] Support dnd --- src/Backends/ScheduleManager/ScheduleManager.vala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Backends/ScheduleManager/ScheduleManager.vala b/src/Backends/ScheduleManager/ScheduleManager.vala index bc70ccd9..15285dcf 100644 --- a/src/Backends/ScheduleManager/ScheduleManager.vala +++ b/src/Backends/ScheduleManager/ScheduleManager.vala @@ -1,8 +1,10 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { private const string NIGHT_LIGHT = "night-light"; private const string DARK_MODE = "dark-mode"; + private const string DND = "dnd"; private static Settings settings = new Settings ("io.elementary.settings-daemon.schedules"); + private static Settings dnd_settings = new Settings ("io.elementary.notifications"); public unowned Pantheon.AccountsService pantheon_service { get; construct; } @@ -45,6 +47,9 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { case DARK_MODE: pantheon_service.prefers_color_scheme = ((bool) settings[DARK_MODE]) ? Granite.Settings.ColorScheme.DARK : Granite.Settings.ColorScheme.LIGHT; break; + case DND: + dnd_settings.set_boolean ("do-not-disturb", (bool) settings[DND]); + break; default: break; } From 216824a591a0b5a4fca68de349867dbda2116080 Mon Sep 17 00:00:00 2001 From: Leonhard Date: Wed, 24 Jan 2024 18:47:12 +0100 Subject: [PATCH 07/22] Add name and enabled --- data/io.elementary.settings-daemon.gschema.xml | 2 +- src/Backends/ScheduleManager/ManualSchedule.vala | 7 +++++-- src/Backends/ScheduleManager/Schedule.vala | 8 ++++++++ src/Backends/ScheduleManager/ScheduleManager.vala | 12 ++++++++---- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/data/io.elementary.settings-daemon.gschema.xml b/data/io.elementary.settings-daemon.gschema.xml index dafbd08a..7edb7116 100644 --- a/data/io.elementary.settings-daemon.gschema.xml +++ b/data/io.elementary.settings-daemon.gschema.xml @@ -72,7 +72,7 @@ - + [] The current schedules. a(a{sv}a{sv}) diff --git a/src/Backends/ScheduleManager/ManualSchedule.vala b/src/Backends/ScheduleManager/ManualSchedule.vala index 63b464eb..29db6632 100644 --- a/src/Backends/ScheduleManager/ManualSchedule.vala +++ b/src/Backends/ScheduleManager/ManualSchedule.vala @@ -2,8 +2,11 @@ public class SettingsDaemon.Backends.ManualSchedule : Schedule { public double from { get; construct set; } public double to { get; construct set; } - public ManualSchedule (double from, double to) { - Object (from: from, to: to); + public ManualSchedule (string name, double from, double to) { + base (); + this.name = name; + this.from = from; + this.to = to; } public ManualSchedule.from_parsed (Parsed parsed) { diff --git a/src/Backends/ScheduleManager/Schedule.vala b/src/Backends/ScheduleManager/Schedule.vala index 8d34e402..4bf9bd01 100644 --- a/src/Backends/ScheduleManager/Schedule.vala +++ b/src/Backends/ScheduleManager/Schedule.vala @@ -5,12 +5,16 @@ public class SettingsDaemon.Backends.Schedule : Object { } public struct Parsed { + string name; Type type; + bool enabled; HashTable args; HashTable active_settings; HashTable inactive_settings; } + public string name { get; protected set; } + public bool enabled { get; protected set; default = true; } public bool active { get; protected set; default = false; } public HashTable active_settings { get; private set; } public HashTable inactive_settings { get; private set; } //Inactive settings should usually be !active_settings but can also be e.g. a default wallpaper path @@ -21,6 +25,8 @@ public class SettingsDaemon.Backends.Schedule : Object { } public Schedule.from_parsed (Parsed parsed) { + name = parsed.name; + enabled = parsed.enabled; active_settings = parsed.active_settings; inactive_settings = parsed.inactive_settings; } @@ -33,7 +39,9 @@ public class SettingsDaemon.Backends.Schedule : Object { public Parsed get_parsed () { Parsed result = { + name, 0, + enabled, get_private_args (), active_settings, inactive_settings diff --git a/src/Backends/ScheduleManager/ScheduleManager.vala b/src/Backends/ScheduleManager/ScheduleManager.vala index 15285dcf..6291dd48 100644 --- a/src/Backends/ScheduleManager/ScheduleManager.vala +++ b/src/Backends/ScheduleManager/ScheduleManager.vala @@ -30,14 +30,18 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { } private void add_schedule (Schedule schedule) { - schedule.notify["active"].connect (() => { - activate_settings (schedule.active ? schedule.active_settings : schedule.inactive_settings); - }); - activate_settings (schedule.active ? schedule.active_settings : schedule.inactive_settings); + schedule.notify["active"].connect (() => schedule_active_changed (schedule)); + schedule_active_changed (schedule); schedules.append (schedule); } + private void schedule_active_changed (Schedule schedule) { + if (schedule.enabled) { + activate_settings (schedule.active ? schedule.active_settings : schedule.inactive_settings); + } + } + public void activate_settings (HashTable settings) { foreach (var key in settings.get_keys ()) { switch (key) { From 3cf82d23d77c780a41db73b69f79f440179779a7 Mon Sep 17 00:00:00 2001 From: Leonhard Date: Wed, 24 Jan 2024 19:15:47 +0100 Subject: [PATCH 08/22] Add dbus api --- src/Application.vala | 12 +- .../ScheduleManager/DaylightSchedule.vala | 1 + .../ScheduleManager/ManualSchedule.vala | 7 ++ src/Backends/ScheduleManager/Schedule.vala | 9 +- .../ScheduleManager/ScheduleManager.vala | 103 ++++++++++++++---- 5 files changed, 111 insertions(+), 21 deletions(-) diff --git a/src/Application.vala b/src/Application.vala index 693af2ad..ceaa5199 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -37,6 +37,8 @@ public sealed class SettingsDaemon.Application : Gtk.Application { GLib.Intl.textdomain (Build.GETTEXT_PACKAGE); add_main_option ("version", 'v', NONE, NONE, "Display the version", null); + + schedule_manager = new Backends.ScheduleManager (); } protected override int handle_local_options (VariantDict options) { @@ -66,6 +68,14 @@ public sealed class SettingsDaemon.Application : Gtk.Application { hold (); } + protected override bool dbus_register (DBusConnection connection, string object_path) throws Error { + base.dbus_register (connection, object_path); + + connection.register_object (object_path, schedule_manager); + + return true; + } + private async void setup_accounts_services () { unowned GLib.DBusConnection connection; string path; @@ -102,7 +112,7 @@ public sealed class SettingsDaemon.Application : Gtk.Application { try { pantheon_service = yield connection.get_proxy (FDO_ACCOUNTS_NAME, path, GET_INVALIDATED_PROPERTIES); // prefers_color_scheme_settings = new Backends.PrefersColorSchemeSettings (pantheon_service); - schedule_manager = new Backends.ScheduleManager (pantheon_service); + schedule_manager.pantheon_service = pantheon_service; } catch { warning ("Unable to get pantheon's AccountsService proxy, color scheme preference may be incorrect"); } diff --git a/src/Backends/ScheduleManager/DaylightSchedule.vala b/src/Backends/ScheduleManager/DaylightSchedule.vala index b0670b37..406a5ad6 100644 --- a/src/Backends/ScheduleManager/DaylightSchedule.vala +++ b/src/Backends/ScheduleManager/DaylightSchedule.vala @@ -7,6 +7,7 @@ public class SettingsDaemon.Backends.DaylightSchedule : Schedule { } construct { + schedule_type = DAYLIGHT; get_location.begin (); Timeout.add (1000, time_callback); } diff --git a/src/Backends/ScheduleManager/ManualSchedule.vala b/src/Backends/ScheduleManager/ManualSchedule.vala index 29db6632..e611b706 100644 --- a/src/Backends/ScheduleManager/ManualSchedule.vala +++ b/src/Backends/ScheduleManager/ManualSchedule.vala @@ -11,6 +11,7 @@ public class SettingsDaemon.Backends.ManualSchedule : Schedule { public ManualSchedule.from_parsed (Parsed parsed) { base.from_parsed (parsed); + schedule_type = MANUAL; from = (double) parsed.args["from"]; to = (double) parsed.args["to"]; } @@ -44,6 +45,12 @@ public class SettingsDaemon.Backends.ManualSchedule : Schedule { return (time_double >= from && time_double <= to); } + protected override void update (Schedule.Parsed parsed) { + base.update (parsed); + from = (double) parsed.args["from"]; + to = (double) parsed.args["to"]; + } + protected override HashTable get_private_args () { var result = new HashTable (str_hash, str_equal); result["from"] = from; diff --git a/src/Backends/ScheduleManager/Schedule.vala b/src/Backends/ScheduleManager/Schedule.vala index 4bf9bd01..f3df7c8f 100644 --- a/src/Backends/ScheduleManager/Schedule.vala +++ b/src/Backends/ScheduleManager/Schedule.vala @@ -14,6 +14,7 @@ public class SettingsDaemon.Backends.Schedule : Object { } public string name { get; protected set; } + public Type schedule_type { get; construct set; } public bool enabled { get; protected set; default = true; } public bool active { get; protected set; default = false; } public HashTable active_settings { get; private set; } @@ -40,7 +41,7 @@ public class SettingsDaemon.Backends.Schedule : Object { public Parsed get_parsed () { Parsed result = { name, - 0, + schedule_type, enabled, get_private_args (), active_settings, @@ -49,6 +50,12 @@ public class SettingsDaemon.Backends.Schedule : Object { return result; } + public virtual void update (Schedule.Parsed parsed) { + enabled = parsed.enabled; + active_settings = parsed.active_settings; + inactive_settings = parsed.inactive_settings; + } + protected virtual HashTable get_private_args () { return new HashTable (str_hash, str_equal); } diff --git a/src/Backends/ScheduleManager/ScheduleManager.vala b/src/Backends/ScheduleManager/ScheduleManager.vala index 6291dd48..c388ec4b 100644 --- a/src/Backends/ScheduleManager/ScheduleManager.vala +++ b/src/Backends/ScheduleManager/ScheduleManager.vala @@ -1,3 +1,4 @@ +[DBus (name="io.elementary.settings_daemon.ScheduleManager")] public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { private const string NIGHT_LIGHT = "night-light"; private const string DARK_MODE = "dark-mode"; @@ -6,34 +7,87 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { private static Settings settings = new Settings ("io.elementary.settings-daemon.schedules"); private static Settings dnd_settings = new Settings ("io.elementary.notifications"); - public unowned Pantheon.AccountsService pantheon_service { get; construct; } + [DBus (visible=false)] + public unowned Pantheon.AccountsService? pantheon_service { get; set; } - private List schedules = new List (); - - public ScheduleManager (Pantheon.AccountsService pantheon_service ) { - Object (pantheon_service: pantheon_service); - } + private HashTable schedules = new HashTable (str_hash, str_equal); construct { foreach (var parsed_schedule in (Schedule.Parsed[]) settings.get_value ("schedules")) { - switch (parsed_schedule.type) { - case MANUAL: - add_schedule (new ManualSchedule.from_parsed (parsed_schedule)); - break; - case DAYLIGHT: - add_schedule (new DaylightSchedule.from_parsed (parsed_schedule)); - break; - default: - break; - } + create_schedule_internal (parsed_schedule); + } + } + + public void create_schedule (Schedule.Parsed parsed) throws DBusError, IOError { + if (parsed.name in schedules) { + throw new IOError.EXISTS ("Schedule with the same name already exists"); + } + + create_schedule_internal (parsed); + + save_schedules (); + } + + private void create_schedule_internal (Schedule.Parsed parsed) { + if (parsed.name in schedules) { + warning ("Schedule with the same name already exists"); + return; + } + + switch (parsed.type) { + case MANUAL: + add_schedule (new ManualSchedule.from_parsed (parsed)); + break; + case DAYLIGHT: + add_schedule (new DaylightSchedule.from_parsed (parsed)); + break; + default: + break; + } + } + + public Schedule.Parsed[] list_schedules () throws DBusError, IOError { + Schedule.Parsed[] parsed_schedules = {}; + foreach (var schedule in schedules.get_values ()) { + parsed_schedules += schedule.get_parsed (); + } + + return parsed_schedules; + } + + public void update_schedule (Schedule.Parsed parsed) throws DBusError, IOError { + if (!(parsed.name in schedules)) { + throw new IOError.NOT_FOUND ("Schedule with the same name not found"); } + + var schedule = schedules[parsed.name]; + + if (schedule.schedule_type != parsed.type) { + schedules.remove (schedule.name); + create_schedule_internal (parsed); + return; + } + + schedule.update (parsed); + + save_schedules (); + } + + public void delete_schedule (string name) throws DBusError, IOError { + if (!(name in schedules)) { + throw new IOError.NOT_FOUND ("Schedule with the same name not found"); + } + + schedules.remove (name); + + save_schedules (); } private void add_schedule (Schedule schedule) { schedule.notify["active"].connect (() => schedule_active_changed (schedule)); schedule_active_changed (schedule); - schedules.append (schedule); + schedules[schedule.name] = schedule; } private void schedule_active_changed (Schedule schedule) { @@ -42,14 +96,16 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { } } - public void activate_settings (HashTable settings) { + private void activate_settings (HashTable settings) { foreach (var key in settings.get_keys ()) { switch (key) { case NIGHT_LIGHT: //TODO break; case DARK_MODE: - pantheon_service.prefers_color_scheme = ((bool) settings[DARK_MODE]) ? Granite.Settings.ColorScheme.DARK : Granite.Settings.ColorScheme.LIGHT; + if (pantheon_service != null) { + pantheon_service.prefers_color_scheme = ((bool) settings[DARK_MODE]) ? Granite.Settings.ColorScheme.DARK : Granite.Settings.ColorScheme.LIGHT; + } break; case DND: dnd_settings.set_boolean ("do-not-disturb", (bool) settings[DND]); @@ -59,4 +115,13 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { } } } + + private void save_schedules () { + Schedule.Parsed[] parsed_schedules = {}; + foreach (var schedule in schedules.get_values ()) { + parsed_schedules += schedule.get_parsed (); + } + + settings.set_value ("schedules", parsed_schedules); + } } From 0e62c8b90a3182011cdcbef0b74f0710856022dc Mon Sep 17 00:00:00 2001 From: Leonhard Date: Wed, 24 Jan 2024 19:17:51 +0100 Subject: [PATCH 09/22] Fix lint --- src/Backends/ScheduleManager/ScheduleManager.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Backends/ScheduleManager/ScheduleManager.vala b/src/Backends/ScheduleManager/ScheduleManager.vala index c388ec4b..eaabe6be 100644 --- a/src/Backends/ScheduleManager/ScheduleManager.vala +++ b/src/Backends/ScheduleManager/ScheduleManager.vala @@ -46,7 +46,7 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { } } - public Schedule.Parsed[] list_schedules () throws DBusError, IOError { + public Schedule.Parsed[] list_schedules () throws DBusError, IOError { Schedule.Parsed[] parsed_schedules = {}; foreach (var schedule in schedules.get_values ()) { parsed_schedules += schedule.get_parsed (); From e61e98032db7e8dcd1c7b11e726afde824cb49b9 Mon Sep 17 00:00:00 2001 From: Leonhard Date: Wed, 24 Jan 2024 19:24:21 +0100 Subject: [PATCH 10/22] Cleanup --- .../ScheduleManager/ScheduleManager.vala | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Backends/ScheduleManager/ScheduleManager.vala b/src/Backends/ScheduleManager/ScheduleManager.vala index eaabe6be..0a2b7da5 100644 --- a/src/Backends/ScheduleManager/ScheduleManager.vala +++ b/src/Backends/ScheduleManager/ScheduleManager.vala @@ -3,9 +3,11 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { private const string NIGHT_LIGHT = "night-light"; private const string DARK_MODE = "dark-mode"; private const string DND = "dnd"; + private const string MONOCHROME = "monochrome"; private static Settings settings = new Settings ("io.elementary.settings-daemon.schedules"); private static Settings dnd_settings = new Settings ("io.elementary.notifications"); + private static Settings monochrome_settings = new Settings ("io.elementary.desktop.wm.accessibility"); [DBus (visible=false)] public unowned Pantheon.AccountsService? pantheon_service { get; set; } @@ -60,15 +62,8 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { throw new IOError.NOT_FOUND ("Schedule with the same name not found"); } - var schedule = schedules[parsed.name]; - - if (schedule.schedule_type != parsed.type) { - schedules.remove (schedule.name); - create_schedule_internal (parsed); - return; - } - - schedule.update (parsed); + schedules.remove (parsed.name); + create_schedule_internal (parsed); save_schedules (); } @@ -110,6 +105,9 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { case DND: dnd_settings.set_boolean ("do-not-disturb", (bool) settings[DND]); break; + case MONOCHROME: + monochrome_settings.set_boolean ("enable-monochrome-filter", (bool) settings[MONOCHROME]); + break; default: break; } From 3779d82d59116345b1841822c1b92aa813d12aa7 Mon Sep 17 00:00:00 2001 From: Leonhard Date: Wed, 24 Jan 2024 19:54:35 +0100 Subject: [PATCH 11/22] save schedule in create_schedule_internal --- src/Backends/ScheduleManager/ScheduleManager.vala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Backends/ScheduleManager/ScheduleManager.vala b/src/Backends/ScheduleManager/ScheduleManager.vala index 0a2b7da5..fef0fbc7 100644 --- a/src/Backends/ScheduleManager/ScheduleManager.vala +++ b/src/Backends/ScheduleManager/ScheduleManager.vala @@ -26,8 +26,6 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { } create_schedule_internal (parsed); - - save_schedules (); } private void create_schedule_internal (Schedule.Parsed parsed) { @@ -46,6 +44,8 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { default: break; } + + save_schedules (); } public Schedule.Parsed[] list_schedules () throws DBusError, IOError { From bba4c68ee1dd6d59202b3e99c30c7aebfb77d688 Mon Sep 17 00:00:00 2001 From: Leonhard Date: Sun, 4 Feb 2024 22:46:43 +0100 Subject: [PATCH 12/22] Add ids --- .../io.elementary.settings-daemon.gschema.xml | 2 +- src/Backends/ScheduleManager/Schedule.vala | 11 +++++++--- .../ScheduleManager/ScheduleManager.vala | 21 ++++++------------- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/data/io.elementary.settings-daemon.gschema.xml b/data/io.elementary.settings-daemon.gschema.xml index 7edb7116..d8b75b87 100644 --- a/data/io.elementary.settings-daemon.gschema.xml +++ b/data/io.elementary.settings-daemon.gschema.xml @@ -72,7 +72,7 @@ - + [] The current schedules. a(a{sv}a{sv}) diff --git a/src/Backends/ScheduleManager/Schedule.vala b/src/Backends/ScheduleManager/Schedule.vala index f3df7c8f..acd642ca 100644 --- a/src/Backends/ScheduleManager/Schedule.vala +++ b/src/Backends/ScheduleManager/Schedule.vala @@ -5,27 +5,31 @@ public class SettingsDaemon.Backends.Schedule : Object { } public struct Parsed { - string name; + string id; Type type; + string name; bool enabled; HashTable args; HashTable active_settings; HashTable inactive_settings; } - public string name { get; protected set; } + public string id { get; protected set; } public Type schedule_type { get; construct set; } + public string name { get; protected set; } public bool enabled { get; protected set; default = true; } public bool active { get; protected set; default = false; } public HashTable active_settings { get; private set; } public HashTable inactive_settings { get; private set; } //Inactive settings should usually be !active_settings but can also be e.g. a default wallpaper path public Schedule () { + id = Uuid.string_random (); active_settings = new HashTable (str_hash, str_equal); inactive_settings = new HashTable (str_hash, str_equal); } public Schedule.from_parsed (Parsed parsed) { + id = parsed.id; name = parsed.name; enabled = parsed.enabled; active_settings = parsed.active_settings; @@ -40,8 +44,9 @@ public class SettingsDaemon.Backends.Schedule : Object { public Parsed get_parsed () { Parsed result = { - name, + id, schedule_type, + name, enabled, get_private_args (), active_settings, diff --git a/src/Backends/ScheduleManager/ScheduleManager.vala b/src/Backends/ScheduleManager/ScheduleManager.vala index fef0fbc7..0af37d32 100644 --- a/src/Backends/ScheduleManager/ScheduleManager.vala +++ b/src/Backends/ScheduleManager/ScheduleManager.vala @@ -20,14 +20,6 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { } } - public void create_schedule (Schedule.Parsed parsed) throws DBusError, IOError { - if (parsed.name in schedules) { - throw new IOError.EXISTS ("Schedule with the same name already exists"); - } - - create_schedule_internal (parsed); - } - private void create_schedule_internal (Schedule.Parsed parsed) { if (parsed.name in schedules) { warning ("Schedule with the same name already exists"); @@ -58,22 +50,21 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { } public void update_schedule (Schedule.Parsed parsed) throws DBusError, IOError { - if (!(parsed.name in schedules)) { - throw new IOError.NOT_FOUND ("Schedule with the same name not found"); + if (parsed.id in schedules) { + schedules.remove (parsed.id); } - schedules.remove (parsed.name); create_schedule_internal (parsed); save_schedules (); } - public void delete_schedule (string name) throws DBusError, IOError { - if (!(name in schedules)) { + public void delete_schedule (string id) throws DBusError, IOError { + if (!(id in schedules)) { throw new IOError.NOT_FOUND ("Schedule with the same name not found"); } - schedules.remove (name); + schedules.remove (id); save_schedules (); } @@ -82,7 +73,7 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { schedule.notify["active"].connect (() => schedule_active_changed (schedule)); schedule_active_changed (schedule); - schedules[schedule.name] = schedule; + schedules[schedule.id] = schedule; } private void schedule_active_changed (Schedule schedule) { From ffacbab2140ed3f3b6d2765598deb3a7fceb6403 Mon Sep 17 00:00:00 2001 From: Leonhard Kargl Date: Tue, 8 Apr 2025 23:37:41 +0200 Subject: [PATCH 13/22] Use parsed as backing store, cleanup --- .../ScheduleManager/ManualSchedule.vala | 60 ----------------- src/Backends/ScheduleManager/Schedule.vala | 67 +++++++++---------- .../ScheduleManager/ScheduleManager.vala | 40 +++-------- ...DaylightSchedule.vala => TimeTracker.vala} | 40 +++++------ src/meson.build | 3 +- 5 files changed, 65 insertions(+), 145 deletions(-) delete mode 100644 src/Backends/ScheduleManager/ManualSchedule.vala rename src/Backends/ScheduleManager/{DaylightSchedule.vala => TimeTracker.vala} (65%) diff --git a/src/Backends/ScheduleManager/ManualSchedule.vala b/src/Backends/ScheduleManager/ManualSchedule.vala deleted file mode 100644 index e611b706..00000000 --- a/src/Backends/ScheduleManager/ManualSchedule.vala +++ /dev/null @@ -1,60 +0,0 @@ -public class SettingsDaemon.Backends.ManualSchedule : Schedule { - public double from { get; construct set; } - public double to { get; construct set; } - - public ManualSchedule (string name, double from, double to) { - base (); - this.name = name; - this.from = from; - this.to = to; - } - - public ManualSchedule.from_parsed (Parsed parsed) { - base.from_parsed (parsed); - schedule_type = MANUAL; - from = (double) parsed.args["from"]; - to = (double) parsed.args["to"]; - } - - construct { - Timeout.add (1000, time_callback); - } - - private bool time_callback () { - var is_in = is_in_time_window (); - - if (active != is_in) { - active = is_in; - } - - return Source.CONTINUE; - } - - private bool is_in_time_window () { - var date_time = new DateTime.now_local (); - double time_double = 0; - time_double += date_time.get_hour (); - time_double += (double) date_time.get_minute () / 60; - - // PM to AM - if (from > to) { - return time_double < to ? time_double <= from : time_double >= from; - } - - // AM to AM, PM to PM, AM to PM - return (time_double >= from && time_double <= to); - } - - protected override void update (Schedule.Parsed parsed) { - base.update (parsed); - from = (double) parsed.args["from"]; - to = (double) parsed.args["to"]; - } - - protected override HashTable get_private_args () { - var result = new HashTable (str_hash, str_equal); - result["from"] = from; - result["to"] = to; - return result; - } -} diff --git a/src/Backends/ScheduleManager/Schedule.vala b/src/Backends/ScheduleManager/Schedule.vala index acd642ca..99c96f86 100644 --- a/src/Backends/ScheduleManager/Schedule.vala +++ b/src/Backends/ScheduleManager/Schedule.vala @@ -14,26 +14,27 @@ public class SettingsDaemon.Backends.Schedule : Object { HashTable inactive_settings; } - public string id { get; protected set; } - public Type schedule_type { get; construct set; } - public string name { get; protected set; } - public bool enabled { get; protected set; default = true; } + public Parsed parsed { get; construct set; } + + public string id { get { return parsed.id; } } + public Type schedule_type { get { return parsed.type; } } + public string name { get { return parsed.name; } } + public bool enabled { get { return parsed.enabled; } } + public HashTable args { get { return parsed.args; } } + public HashTable active_settings { get { return parsed.active_settings; } } + public HashTable inactive_settings { get { return parsed.inactive_settings; } } //Inactive settings should usually be !active_settings but can also be e.g. a default wallpaper path + public bool active { get; protected set; default = false; } - public HashTable active_settings { get; private set; } - public HashTable inactive_settings { get; private set; } //Inactive settings should usually be !active_settings but can also be e.g. a default wallpaper path - public Schedule () { - id = Uuid.string_random (); - active_settings = new HashTable (str_hash, str_equal); - inactive_settings = new HashTable (str_hash, str_equal); + private TimeTracker time_tracker; + + public Schedule (Parsed parsed) { + Object (parsed: parsed); } - public Schedule.from_parsed (Parsed parsed) { - id = parsed.id; - name = parsed.name; - enabled = parsed.enabled; - active_settings = parsed.active_settings; - inactive_settings = parsed.inactive_settings; + construct { + time_tracker = new TimeTracker (); + Timeout.add (1000, time_callback); } /* Convenience method to add the same boolean inverted to inactive settings */ @@ -42,26 +43,22 @@ public class SettingsDaemon.Backends.Schedule : Object { inactive_settings[key] = !val; } - public Parsed get_parsed () { - Parsed result = { - id, - schedule_type, - name, - enabled, - get_private_args (), - active_settings, - inactive_settings - }; - return result; - } + private bool time_callback () { + bool is_in = false; + switch (schedule_type) { + case MANUAL: + is_in = time_tracker.is_in_time_window_manual (args["from"].get_double (), args["to"].get_double ()); + break; - public virtual void update (Schedule.Parsed parsed) { - enabled = parsed.enabled; - active_settings = parsed.active_settings; - inactive_settings = parsed.inactive_settings; - } + case DAYLIGHT: + is_in = time_tracker.is_in_time_window_daylight (); + break; + } + + if (active != is_in) { + active = is_in; + } - protected virtual HashTable get_private_args () { - return new HashTable (str_hash, str_equal); + return Source.CONTINUE; } } diff --git a/src/Backends/ScheduleManager/ScheduleManager.vala b/src/Backends/ScheduleManager/ScheduleManager.vala index 0af37d32..5a71afbb 100644 --- a/src/Backends/ScheduleManager/ScheduleManager.vala +++ b/src/Backends/ScheduleManager/ScheduleManager.vala @@ -16,34 +16,23 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { construct { foreach (var parsed_schedule in (Schedule.Parsed[]) settings.get_value ("schedules")) { - create_schedule_internal (parsed_schedule); + add_schedule (parsed_schedule); } } - private void create_schedule_internal (Schedule.Parsed parsed) { - if (parsed.name in schedules) { - warning ("Schedule with the same name already exists"); - return; - } + private void add_schedule (Schedule.Parsed parsed) { + var schedule = new Schedule (parsed); - switch (parsed.type) { - case MANUAL: - add_schedule (new ManualSchedule.from_parsed (parsed)); - break; - case DAYLIGHT: - add_schedule (new DaylightSchedule.from_parsed (parsed)); - break; - default: - break; - } + schedule.notify["active"].connect ((obj, pspec) => schedule_active_changed ((Schedule) obj)); + schedule_active_changed (schedule); - save_schedules (); + schedules[schedule.id] = schedule; } public Schedule.Parsed[] list_schedules () throws DBusError, IOError { Schedule.Parsed[] parsed_schedules = {}; foreach (var schedule in schedules.get_values ()) { - parsed_schedules += schedule.get_parsed (); + parsed_schedules += schedule.parsed; } return parsed_schedules; @@ -51,11 +40,11 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { public void update_schedule (Schedule.Parsed parsed) throws DBusError, IOError { if (parsed.id in schedules) { - schedules.remove (parsed.id); + schedules[parsed.id].parsed = parsed; + } else { + add_schedule (parsed); } - create_schedule_internal (parsed); - save_schedules (); } @@ -69,13 +58,6 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { save_schedules (); } - private void add_schedule (Schedule schedule) { - schedule.notify["active"].connect (() => schedule_active_changed (schedule)); - schedule_active_changed (schedule); - - schedules[schedule.id] = schedule; - } - private void schedule_active_changed (Schedule schedule) { if (schedule.enabled) { activate_settings (schedule.active ? schedule.active_settings : schedule.inactive_settings); @@ -108,7 +90,7 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { private void save_schedules () { Schedule.Parsed[] parsed_schedules = {}; foreach (var schedule in schedules.get_values ()) { - parsed_schedules += schedule.get_parsed (); + parsed_schedules += schedule.parsed; } settings.set_value ("schedules", parsed_schedules); diff --git a/src/Backends/ScheduleManager/DaylightSchedule.vala b/src/Backends/ScheduleManager/TimeTracker.vala similarity index 65% rename from src/Backends/ScheduleManager/DaylightSchedule.vala rename to src/Backends/ScheduleManager/TimeTracker.vala index 406a5ad6..14447541 100644 --- a/src/Backends/ScheduleManager/DaylightSchedule.vala +++ b/src/Backends/ScheduleManager/TimeTracker.vala @@ -1,30 +1,17 @@ -public class SettingsDaemon.Backends.DaylightSchedule : Schedule { +[SingleInstance] +public class SettingsDaemon.Backends.TimeTracker : Object { + private GClue.Simple simple; + private double sunrise = 6.0; private double sunset = 20.0; - public DaylightSchedule.from_parsed (Parsed parsed) { - base.from_parsed (parsed); - } - construct { - schedule_type = DAYLIGHT; get_location.begin (); - Timeout.add (1000, time_callback); - } - - private bool time_callback () { - var is_in = is_in_time_window (); - - if (active != is_in) { - active = is_in; - } - - return Source.CONTINUE; } private async void get_location () { try { - var simple = yield new GClue.Simple (Build.PROJECT_NAME, GClue.AccuracyLevel.CITY, null); + simple = yield new GClue.Simple (Build.PROJECT_NAME, GClue.AccuracyLevel.CITY, null); simple.notify["location"].connect (() => { on_location_updated (simple.location.latitude, simple.location.longitude); @@ -46,7 +33,7 @@ public class SettingsDaemon.Backends.DaylightSchedule : Schedule { } } - private bool is_in_time_window () { + public bool is_in_time_window_daylight () { var date_time = new DateTime.now_local (); double time_double = 0; time_double += date_time.get_hour (); @@ -60,4 +47,19 @@ public class SettingsDaemon.Backends.DaylightSchedule : Schedule { // AM to AM, PM to PM, AM to PM return (time_double >= sunset && time_double <= sunrise); } + + public bool is_in_time_window_manual (double from, double to) { + var date_time = new DateTime.now_local (); + double time_double = 0; + time_double += date_time.get_hour (); + time_double += (double) date_time.get_minute () / 60; + + // PM to AM + if (from > to) { + return time_double < to ? time_double <= from : time_double >= from; + } + + // AM to AM, PM to PM, AM to PM + return (time_double >= from && time_double <= to); + } } diff --git a/src/meson.build b/src/meson.build index 6cf71512..c540ef8e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -9,10 +9,9 @@ sources = files( 'Backends/NightLightSettings.vala', 'Backends/PowerProfilesSync.vala', 'Backends/PrefersColorSchemeSettings.vala', - 'Backends' / 'ScheduleManager' / 'DaylightSchedule.vala', - 'Backends' / 'ScheduleManager' / 'ManualSchedule.vala', 'Backends' / 'ScheduleManager' / 'Schedule.vala', 'Backends' / 'ScheduleManager' / 'ScheduleManager.vala', + 'Backends' / 'ScheduleManager' / 'TimeTracker.vala', 'Backends/SystemUpdate.vala', 'Utils/PkUtils.vala', 'Utils/SessionUtils.vala', From 0cfb6f46c7894a9abbc69b15b01a55a6651cbbdc Mon Sep 17 00:00:00 2001 From: Leonhard Kargl Date: Wed, 9 Apr 2025 12:36:37 +0200 Subject: [PATCH 14/22] Use ListModel structure --- .../ScheduleManager/ScheduleManager.vala | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/Backends/ScheduleManager/ScheduleManager.vala b/src/Backends/ScheduleManager/ScheduleManager.vala index 5a71afbb..0195762a 100644 --- a/src/Backends/ScheduleManager/ScheduleManager.vala +++ b/src/Backends/ScheduleManager/ScheduleManager.vala @@ -9,12 +9,18 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { private static Settings dnd_settings = new Settings ("io.elementary.notifications"); private static Settings monochrome_settings = new Settings ("io.elementary.desktop.wm.accessibility"); + public signal void items_changed (uint pos, uint removed, uint added); + [DBus (visible=false)] public unowned Pantheon.AccountsService? pantheon_service { get; set; } private HashTable schedules = new HashTable (str_hash, str_equal); + private ListStore schedules_list; construct { + schedules_list = new ListStore (typeof (Schedule)); + schedules_list.items_changed.connect ((pos, rem, add) => items_changed (pos, rem, add)); + foreach (var parsed_schedule in (Schedule.Parsed[]) settings.get_value ("schedules")) { add_schedule (parsed_schedule); } @@ -27,15 +33,15 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { schedule_active_changed (schedule); schedules[schedule.id] = schedule; + schedules_list.append (schedule); } - public Schedule.Parsed[] list_schedules () throws DBusError, IOError { - Schedule.Parsed[] parsed_schedules = {}; - foreach (var schedule in schedules.get_values ()) { - parsed_schedules += schedule.parsed; - } + public uint get_n_schedules () throws DBusError, IOError { + return schedules_list.n_items; + } - return parsed_schedules; + public Schedule.Parsed get_schedule (uint pos) throws DBusError, IOError { + return ((Schedule) schedules_list.get_item (pos)).parsed; } public void update_schedule (Schedule.Parsed parsed) throws DBusError, IOError { @@ -53,6 +59,11 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { throw new IOError.NOT_FOUND ("Schedule with the same name not found"); } + uint pos; + if (schedules_list.find (schedules[id], out pos)) { + schedules_list.remove (pos); + } + schedules.remove (id); save_schedules (); From 910f81cb131de7e6b14ffee9fae43889c95be33d Mon Sep 17 00:00:00 2001 From: Leonhard Kargl Date: Wed, 9 Apr 2025 13:20:13 +0200 Subject: [PATCH 15/22] Fix warnings if from/to time are not available --- src/Backends/ScheduleManager/Schedule.vala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Backends/ScheduleManager/Schedule.vala b/src/Backends/ScheduleManager/Schedule.vala index 99c96f86..15129a9a 100644 --- a/src/Backends/ScheduleManager/Schedule.vala +++ b/src/Backends/ScheduleManager/Schedule.vala @@ -47,7 +47,9 @@ public class SettingsDaemon.Backends.Schedule : Object { bool is_in = false; switch (schedule_type) { case MANUAL: - is_in = time_tracker.is_in_time_window_manual (args["from"].get_double (), args["to"].get_double ()); + if ("from" in args && "to" in args) { + is_in = time_tracker.is_in_time_window_manual (args["from"].get_double (), args["to"].get_double ()); + } break; case DAYLIGHT: From aeb1b8aedea7cfdf897276af524a483afaef6a30 Mon Sep 17 00:00:00 2001 From: Leonhard Kargl Date: Wed, 9 Apr 2025 14:19:32 +0200 Subject: [PATCH 16/22] Reapply settings when schedule changes --- src/Backends/ScheduleManager/Schedule.vala | 27 ++++++++++++------- .../ScheduleManager/ScheduleManager.vala | 11 ++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Backends/ScheduleManager/Schedule.vala b/src/Backends/ScheduleManager/Schedule.vala index 15129a9a..7c44d205 100644 --- a/src/Backends/ScheduleManager/Schedule.vala +++ b/src/Backends/ScheduleManager/Schedule.vala @@ -14,7 +14,16 @@ public class SettingsDaemon.Backends.Schedule : Object { HashTable inactive_settings; } - public Parsed parsed { get; construct set; } + public signal void apply_settings (HashTable settings); + + private Parsed _parsed; + public Parsed parsed { + get { return _parsed; } + set { + _parsed = value; + dirty = true; + } + } public string id { get { return parsed.id; } } public Type schedule_type { get { return parsed.type; } } @@ -24,9 +33,9 @@ public class SettingsDaemon.Backends.Schedule : Object { public HashTable active_settings { get { return parsed.active_settings; } } public HashTable inactive_settings { get { return parsed.inactive_settings; } } //Inactive settings should usually be !active_settings but can also be e.g. a default wallpaper path - public bool active { get; protected set; default = false; } - private TimeTracker time_tracker; + private bool active = false; + private bool dirty = false; public Schedule (Parsed parsed) { Object (parsed: parsed); @@ -37,12 +46,6 @@ public class SettingsDaemon.Backends.Schedule : Object { Timeout.add (1000, time_callback); } - /* Convenience method to add the same boolean inverted to inactive settings */ - public void add_boolean (string key, bool val) { - active_settings[key] = val; - inactive_settings[key] = !val; - } - private bool time_callback () { bool is_in = false; switch (schedule_type) { @@ -57,8 +60,12 @@ public class SettingsDaemon.Backends.Schedule : Object { break; } - if (active != is_in) { + if (dirty || active != is_in) { + dirty = false; active = is_in; + if (enabled) { + apply_settings (active ? active_settings : inactive_settings); + } } return Source.CONTINUE; diff --git a/src/Backends/ScheduleManager/ScheduleManager.vala b/src/Backends/ScheduleManager/ScheduleManager.vala index 0195762a..a1f84e99 100644 --- a/src/Backends/ScheduleManager/ScheduleManager.vala +++ b/src/Backends/ScheduleManager/ScheduleManager.vala @@ -29,8 +29,7 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { private void add_schedule (Schedule.Parsed parsed) { var schedule = new Schedule (parsed); - schedule.notify["active"].connect ((obj, pspec) => schedule_active_changed ((Schedule) obj)); - schedule_active_changed (schedule); + schedule.apply_settings.connect (apply_settings); schedules[schedule.id] = schedule; schedules_list.append (schedule); @@ -69,13 +68,7 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { save_schedules (); } - private void schedule_active_changed (Schedule schedule) { - if (schedule.enabled) { - activate_settings (schedule.active ? schedule.active_settings : schedule.inactive_settings); - } - } - - private void activate_settings (HashTable settings) { + private void apply_settings (HashTable settings) { foreach (var key in settings.get_keys ()) { switch (key) { case NIGHT_LIGHT: From b7059f0aea8415e668c65bc53af25da7d7437beb Mon Sep 17 00:00:00 2001 From: Leonhard Kargl Date: Wed, 9 Apr 2025 18:02:16 +0200 Subject: [PATCH 17/22] Rm color scheme settings, rm night light, set also interface --- src/Application.vala | 3 - src/Backends/PrefersColorSchemeSettings.vala | 161 ------------------ .../ScheduleManager/ScheduleManager.vala | 10 +- src/meson.build | 1 - 4 files changed, 5 insertions(+), 170 deletions(-) delete mode 100644 src/Backends/PrefersColorSchemeSettings.vala diff --git a/src/Application.vala b/src/Application.vala index 0fd53846..43b12d71 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -15,7 +15,6 @@ public sealed class SettingsDaemon.Application : Gtk.Application { private Backends.MouseSettings mouse_settings; private Backends.InterfaceSettings interface_settings; private Backends.NightLightSettings night_light_settings; - private Backends.PrefersColorSchemeSettings prefers_color_scheme_settings; private Backends.AccentColorManager accent_color_manager; private Backends.Housekeeping housekeeping; @@ -128,9 +127,7 @@ public sealed class SettingsDaemon.Application : Gtk.Application { try { pantheon_service = yield connection.get_proxy (FDO_ACCOUNTS_NAME, path, GET_INVALIDATED_PROPERTIES); - // prefers_color_scheme_settings = new Backends.PrefersColorSchemeSettings (pantheon_service); schedule_manager.pantheon_service = pantheon_service; - accent_color_manager = new Backends.AccentColorManager (pantheon_service); } catch { warning ("Unable to get pantheon's AccountsService proxy, color scheme preference may be incorrect"); diff --git a/src/Backends/PrefersColorSchemeSettings.vala b/src/Backends/PrefersColorSchemeSettings.vala deleted file mode 100644 index 311b7b06..00000000 --- a/src/Backends/PrefersColorSchemeSettings.vala +++ /dev/null @@ -1,161 +0,0 @@ -/* -* Copyright 2020–2021 elementary, Inc. (https://elementary.io) -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public -* License as published by the Free Software Foundation; either -* version 3 of the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -* Boston, MA 02110-1301 USA -* -* Authored by: Marius Meisenzahl -*/ - -public class SettingsDaemon.Backends.PrefersColorSchemeSettings : Object { - public unowned Pantheon.AccountsService accounts_service { get; construct; } - - private Settings color_settings; - private double sunrise = -1.0; - private double sunset = -1.0; - - private uint time_id = 0; - - public PrefersColorSchemeSettings (Pantheon.AccountsService accounts_service) { - Object (accounts_service: accounts_service); - } - - construct { - color_settings = new Settings ("io.elementary.settings-daemon.prefers-color-scheme"); - - var schedule = color_settings.get_string ("prefer-dark-schedule"); - if (schedule == "sunset-to-sunrise") { - var variant = color_settings.get_value ("last-coordinates"); - on_location_updated (variant.get_child_value (0).get_double (), variant.get_child_value (1).get_double ()); - } - - color_settings.changed["prefer-dark-schedule"].connect (update_timer); - - update_timer (); - } - - private void update_timer () { - var schedule = color_settings.get_string ("prefer-dark-schedule"); - - if (schedule == "sunset-to-sunrise") { - get_location.begin (); - - start_timer (); - } else if (schedule == "manual") { - start_timer (); - } else { - stop_timer (); - } - } - - private void start_timer () { - if (time_id == 0) { - var time = new TimeoutSource (1000); - time.set_callback (time_callback); - time_id = time.attach (null); - } - } - - private void stop_timer () { - if (time_id != 0) { - Source.remove (time_id); - time_id = 0; - } - } - - private async void get_location () { - try { - var simple = yield new GClue.Simple (Build.PROJECT_NAME, GClue.AccuracyLevel.CITY, null); - - simple.notify["location"].connect (() => { - on_location_updated (simple.location.latitude, simple.location.longitude); - }); - - on_location_updated (simple.location.latitude, simple.location.longitude); - } catch (Error e) { - warning ("Failed to connect to GeoClue2 service: %s", e.message); - return; - } - } - - private bool time_callback () { - var schedule = color_settings.get_string ("prefer-dark-schedule"); - - var now = new DateTime.now_local (); - double from, to; - if (schedule == "sunset-to-sunrise") { - if (sunrise >= 0 && sunset >= 0) { - from = sunset; - to = sunrise; - } else { - // fallback times (6AM and 8PM) for when an invalid result was returned - // from the calculation (i.e. probably wasn't able to get a location) - from = 20.0; - to = 6.0; - } - } else if (schedule == "manual") { - from = color_settings.get_double ("prefer-dark-schedule-from"); - to = color_settings.get_double ("prefer-dark-schedule-to"); - } else { - return true; - } - - var is_in = is_in_time_window (date_time_double (now), from, to); - var new_color_scheme = Granite.Settings.ColorScheme.NO_PREFERENCE; - if (is_in) { - new_color_scheme = Granite.Settings.ColorScheme.DARK; - } - - if (new_color_scheme == accounts_service.prefers_color_scheme) { - return true; - } - - accounts_service.prefers_color_scheme = new_color_scheme; - - var mutter_settings = new GLib.Settings ("org.gnome.desktop.interface"); - mutter_settings.set_enum ("color-scheme", new_color_scheme); - - return true; - } - - private void on_location_updated (double latitude, double longitude) { - color_settings.set_value ("last-coordinates", new Variant.tuple ({latitude, longitude})); - - var now = new DateTime.now_local (); - double _sunrise, _sunset; - if (SettingsDaemon.Utils.SunriseSunsetCalculator.get_sunrise_and_sunset (now, latitude, longitude, out _sunrise, out _sunset)) { - sunrise = _sunrise; - sunset = _sunset; - } - } - - public static bool is_in_time_window (double time_double, double from, double to) { - // PM to AM - if (from > to) { - return time_double < to ? time_double <= from : time_double >= from; - } - - // AM to AM, PM to PM, AM to PM - return (time_double >= from && time_double <= to); - } - - public static double date_time_double (DateTime date_time) { - double time_double = 0; - time_double += date_time.get_hour (); - time_double += (double) date_time.get_minute () / 60; - - return time_double; - } -} diff --git a/src/Backends/ScheduleManager/ScheduleManager.vala b/src/Backends/ScheduleManager/ScheduleManager.vala index a1f84e99..0a16fdf4 100644 --- a/src/Backends/ScheduleManager/ScheduleManager.vala +++ b/src/Backends/ScheduleManager/ScheduleManager.vala @@ -1,11 +1,11 @@ [DBus (name="io.elementary.settings_daemon.ScheduleManager")] public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { - private const string NIGHT_LIGHT = "night-light"; private const string DARK_MODE = "dark-mode"; private const string DND = "dnd"; private const string MONOCHROME = "monochrome"; private static Settings settings = new Settings ("io.elementary.settings-daemon.schedules"); + private static Settings interface_settings = new Settings ("org.gnome.desktop.interface"); private static Settings dnd_settings = new Settings ("io.elementary.notifications"); private static Settings monochrome_settings = new Settings ("io.elementary.desktop.wm.accessibility"); @@ -71,13 +71,12 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { private void apply_settings (HashTable settings) { foreach (var key in settings.get_keys ()) { switch (key) { - case NIGHT_LIGHT: - //TODO - break; case DARK_MODE: + var scheme = ((bool) settings[DARK_MODE]) ? Granite.Settings.ColorScheme.DARK : Granite.Settings.ColorScheme.LIGHT; if (pantheon_service != null) { - pantheon_service.prefers_color_scheme = ((bool) settings[DARK_MODE]) ? Granite.Settings.ColorScheme.DARK : Granite.Settings.ColorScheme.LIGHT; + pantheon_service.prefers_color_scheme = scheme; } + interface_settings.set_enum ("color-scheme", scheme); break; case DND: dnd_settings.set_boolean ("do-not-disturb", (bool) settings[DND]); @@ -86,6 +85,7 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { monochrome_settings.set_boolean ("enable-monochrome-filter", (bool) settings[MONOCHROME]); break; default: + warning ("Tried to apply unknown setting: %s", key); break; } } diff --git a/src/meson.build b/src/meson.build index c540ef8e..a9a321f5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -8,7 +8,6 @@ sources = files( 'Backends/MouseSettings.vala', 'Backends/NightLightSettings.vala', 'Backends/PowerProfilesSync.vala', - 'Backends/PrefersColorSchemeSettings.vala', 'Backends' / 'ScheduleManager' / 'Schedule.vala', 'Backends' / 'ScheduleManager' / 'ScheduleManager.vala', 'Backends' / 'ScheduleManager' / 'TimeTracker.vala', From 975c70249cb7dc4904f9856aa855afd76c559d1f Mon Sep 17 00:00:00 2001 From: Leonhard Kargl Date: Tue, 8 Jul 2025 19:02:27 +0200 Subject: [PATCH 18/22] Add license headers --- src/Backends/ScheduleManager/Schedule.vala | 6 ++++++ src/Backends/ScheduleManager/ScheduleManager.vala | 6 ++++++ src/Backends/ScheduleManager/TimeTracker.vala | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/src/Backends/ScheduleManager/Schedule.vala b/src/Backends/ScheduleManager/Schedule.vala index 7c44d205..02bbce0c 100644 --- a/src/Backends/ScheduleManager/Schedule.vala +++ b/src/Backends/ScheduleManager/Schedule.vala @@ -1,3 +1,9 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io) + * Authored by: Leonhard Kargl + */ + public class SettingsDaemon.Backends.Schedule : Object { public enum Type { MANUAL, diff --git a/src/Backends/ScheduleManager/ScheduleManager.vala b/src/Backends/ScheduleManager/ScheduleManager.vala index 0a16fdf4..deabea1c 100644 --- a/src/Backends/ScheduleManager/ScheduleManager.vala +++ b/src/Backends/ScheduleManager/ScheduleManager.vala @@ -1,3 +1,9 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io) + * Authored by: Leonhard Kargl + */ + [DBus (name="io.elementary.settings_daemon.ScheduleManager")] public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { private const string DARK_MODE = "dark-mode"; diff --git a/src/Backends/ScheduleManager/TimeTracker.vala b/src/Backends/ScheduleManager/TimeTracker.vala index 14447541..3073c5cb 100644 --- a/src/Backends/ScheduleManager/TimeTracker.vala +++ b/src/Backends/ScheduleManager/TimeTracker.vala @@ -1,3 +1,9 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io) + * Authored by: Leonhard Kargl + */ + [SingleInstance] public class SettingsDaemon.Backends.TimeTracker : Object { private GClue.Simple simple; From f0fbdfb865711c057dcece914f526c1986b16134 Mon Sep 17 00:00:00 2001 From: Leonhard Kargl Date: Tue, 8 Jul 2025 19:16:25 +0200 Subject: [PATCH 19/22] Readd color scheme settings --- src/Application.vala | 3 ++- src/Backends/PrefersColorSchemeSettings.vala | 6 ------ src/Backends/ScheduleManager/ScheduleManager.vala | 10 ++-------- src/meson.build | 1 + 4 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/Application.vala b/src/Application.vala index 90c0212b..62eca84a 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -15,6 +15,7 @@ public sealed class SettingsDaemon.Application : Gtk.Application { private Backends.MouseSettings mouse_settings; private Backends.InterfaceSettings interface_settings; private Backends.NightLightSettings night_light_settings; + private Backends.PrefersColorSchemeSettings prefers_color_scheme_settings; private Backends.AccentColorManager accent_color_manager; private Backends.Housekeeping housekeeping; @@ -129,7 +130,7 @@ public sealed class SettingsDaemon.Application : Gtk.Application { try { pantheon_service = yield connection.get_proxy (FDO_ACCOUNTS_NAME, path, GET_INVALIDATED_PROPERTIES); - schedule_manager.pantheon_service = pantheon_service; + prefers_color_scheme_settings = new Backends.PrefersColorSchemeSettings (pantheon_service); accent_color_manager = new Backends.AccentColorManager (pantheon_service, accounts_service); } catch { warning ("Unable to get pantheon's AccountsService proxy, color scheme preference may be incorrect"); diff --git a/src/Backends/PrefersColorSchemeSettings.vala b/src/Backends/PrefersColorSchemeSettings.vala index 5db160c5..e13b861d 100644 --- a/src/Backends/PrefersColorSchemeSettings.vala +++ b/src/Backends/PrefersColorSchemeSettings.vala @@ -37,12 +37,6 @@ public class SettingsDaemon.Backends.PrefersColorSchemeSettings : Object { private void update_color_scheme () { var color_scheme = color_settings.get_enum (COLOR_SCHEME); - if ( - color_scheme == Granite.Settings.ColorScheme.DARK && !is_in_schedule () || - color_scheme != Granite.Settings.ColorScheme.DARK && is_in_schedule () - ) { - color_settings.set_boolean (DARK_SCHEDULE_SNOOZED, true); - } accounts_service.prefers_color_scheme = color_scheme; diff --git a/src/Backends/ScheduleManager/ScheduleManager.vala b/src/Backends/ScheduleManager/ScheduleManager.vala index deabea1c..b0ac0ab0 100644 --- a/src/Backends/ScheduleManager/ScheduleManager.vala +++ b/src/Backends/ScheduleManager/ScheduleManager.vala @@ -11,15 +11,12 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { private const string MONOCHROME = "monochrome"; private static Settings settings = new Settings ("io.elementary.settings-daemon.schedules"); - private static Settings interface_settings = new Settings ("org.gnome.desktop.interface"); + private static Settings color_scheme_settings = new Settings ("io.elementary.settings-daemon.prefers-color-scheme"); private static Settings dnd_settings = new Settings ("io.elementary.notifications"); private static Settings monochrome_settings = new Settings ("io.elementary.desktop.wm.accessibility"); public signal void items_changed (uint pos, uint removed, uint added); - [DBus (visible=false)] - public unowned Pantheon.AccountsService? pantheon_service { get; set; } - private HashTable schedules = new HashTable (str_hash, str_equal); private ListStore schedules_list; @@ -79,10 +76,7 @@ public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { switch (key) { case DARK_MODE: var scheme = ((bool) settings[DARK_MODE]) ? Granite.Settings.ColorScheme.DARK : Granite.Settings.ColorScheme.LIGHT; - if (pantheon_service != null) { - pantheon_service.prefers_color_scheme = scheme; - } - interface_settings.set_enum ("color-scheme", scheme); + color_scheme_settings.set_enum ("color-scheme", scheme); break; case DND: dnd_settings.set_boolean ("do-not-disturb", (bool) settings[DND]); diff --git a/src/meson.build b/src/meson.build index b0c48c43..90cd77a2 100644 --- a/src/meson.build +++ b/src/meson.build @@ -9,6 +9,7 @@ sources = files( 'Backends/MouseSettings.vala', 'Backends/NightLightSettings.vala', 'Backends/PowerProfilesSync.vala', + 'Backends/PrefersColorSchemeSettings.vala', 'Backends' / 'ScheduleManager' / 'Schedule.vala', 'Backends' / 'ScheduleManager' / 'ScheduleManager.vala', 'Backends' / 'ScheduleManager' / 'TimeTracker.vala', From a0fc20e8578a9c8aaee521b006c5f8528d6379a2 Mon Sep 17 00:00:00 2001 From: Leonhard Kargl Date: Tue, 8 Jul 2025 19:18:11 +0200 Subject: [PATCH 20/22] Fix some diff and formatting --- src/Application.vala | 1 - src/meson.build | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Application.vala b/src/Application.vala index 62eca84a..232fc661 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -129,7 +129,6 @@ public sealed class SettingsDaemon.Application : Gtk.Application { try { pantheon_service = yield connection.get_proxy (FDO_ACCOUNTS_NAME, path, GET_INVALIDATED_PROPERTIES); - prefers_color_scheme_settings = new Backends.PrefersColorSchemeSettings (pantheon_service); accent_color_manager = new Backends.AccentColorManager (pantheon_service, accounts_service); } catch { diff --git a/src/meson.build b/src/meson.build index 90cd77a2..4c11b12d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -9,7 +9,7 @@ sources = files( 'Backends/MouseSettings.vala', 'Backends/NightLightSettings.vala', 'Backends/PowerProfilesSync.vala', - 'Backends/PrefersColorSchemeSettings.vala', + 'Backends/PrefersColorSchemeSettings.vala', 'Backends' / 'ScheduleManager' / 'Schedule.vala', 'Backends' / 'ScheduleManager' / 'ScheduleManager.vala', 'Backends' / 'ScheduleManager' / 'TimeTracker.vala', From 8bfb4027de58fa48ac24a61ffb3779ddfe3983fe Mon Sep 17 00:00:00 2001 From: Leonhard Kargl Date: Sun, 19 Oct 2025 13:19:08 +0200 Subject: [PATCH 21/22] Rebrand to modes --- .../io.elementary.settings-daemon.gschema.xml | 6 +- src/Application.vala | 6 +- src/Backends/FocusModes/GLibSetting.vala | 37 +++++ src/Backends/FocusModes/Manager.vala | 86 +++++++++++ src/Backends/FocusModes/Mode.vala | 140 ++++++++++++++++++ src/Backends/FocusModes/Setting.vala | 10 ++ .../TimeTracker.vala | 2 +- src/Backends/ScheduleManager/Schedule.vala | 79 ---------- .../ScheduleManager/ScheduleManager.vala | 102 ------------- src/meson.build | 8 +- 10 files changed, 285 insertions(+), 191 deletions(-) create mode 100644 src/Backends/FocusModes/GLibSetting.vala create mode 100644 src/Backends/FocusModes/Manager.vala create mode 100644 src/Backends/FocusModes/Mode.vala create mode 100644 src/Backends/FocusModes/Setting.vala rename src/Backends/{ScheduleManager => FocusModes}/TimeTracker.vala (96%) delete mode 100644 src/Backends/ScheduleManager/Schedule.vala delete mode 100644 src/Backends/ScheduleManager/ScheduleManager.vala diff --git a/data/io.elementary.settings-daemon.gschema.xml b/data/io.elementary.settings-daemon.gschema.xml index 88c0ed60..8700afc0 100644 --- a/data/io.elementary.settings-daemon.gschema.xml +++ b/data/io.elementary.settings-daemon.gschema.xml @@ -97,10 +97,10 @@ - - + + [] - The current schedules. + The current focus modes. a(a{sv}a{sv}) diff --git a/src/Application.vala b/src/Application.vala index 232fc661..036b2d7f 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -22,7 +22,7 @@ public sealed class SettingsDaemon.Application : Gtk.Application { private Backends.PowerProfilesSync power_profiles_sync; private Backends.ApplicationShortcuts application_shortcuts; - private Backends.ScheduleManager schedule_manager; + private Backends.FocusModes.Manager focus_modes_manager; private const string FDO_ACCOUNTS_NAME = "org.freedesktop.Accounts"; private const string FDO_ACCOUNTS_PATH = "/org/freedesktop/Accounts"; @@ -43,7 +43,7 @@ public sealed class SettingsDaemon.Application : Gtk.Application { add_main_option ("version", 'v', NONE, NONE, "Display the version", null); - schedule_manager = new Backends.ScheduleManager (); + focus_modes_manager = new Backends.FocusModes.Manager (); } protected override int handle_local_options (VariantDict options) { @@ -84,7 +84,7 @@ public sealed class SettingsDaemon.Application : Gtk.Application { protected override bool dbus_register (DBusConnection connection, string object_path) throws Error { base.dbus_register (connection, object_path); - connection.register_object (object_path, schedule_manager); + connection.register_object (object_path, focus_modes_manager); connection.register_object (object_path, new Backends.SystemUpdate ()); #if UBUNTU_DRIVERS diff --git a/src/Backends/FocusModes/GLibSetting.vala b/src/Backends/FocusModes/GLibSetting.vala new file mode 100644 index 00000000..81fe65de --- /dev/null +++ b/src/Backends/FocusModes/GLibSetting.vala @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io) + * Authored by: Leonhard Kargl + */ + +public class SettingsDaemon.Backends.FocusModes.GLibSetting : Object, Setting { + public Settings settings { get; construct; } + public string key { get; construct; } + public Variant active_value { get; construct; } + + private int applied = 0; + private Variant? last_value = null; + + public GLibSetting (string schema_id, string key, Variant active_value) { + Object (settings: new Settings (schema_id), key: key, active_value: active_value); + } + + public void apply (Variant value) { + applied++; + + if (applied == 1) { + last_value = settings.get_value (key); + settings.set_value (key, active_value); + } + } + + public void unapply () { + applied--; + + assert (applied >= 0); + + if (applied == 0 && settings.get_value (key).equal (active_value) && last_value != null) { + settings.set_value (key, last_value); + } + } +} diff --git a/src/Backends/FocusModes/Manager.vala b/src/Backends/FocusModes/Manager.vala new file mode 100644 index 00000000..7b85ee20 --- /dev/null +++ b/src/Backends/FocusModes/Manager.vala @@ -0,0 +1,86 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io) + * Authored by: Leonhard Kargl + */ + +[DBus (name="io.elementary.settings_daemon.ModeManager")] +public class SettingsDaemon.Backends.FocusModes.Manager : GLib.Object { + private static Settings settings = new Settings ("io.elementary.settings-daemon.focus-modes"); + + public signal void items_changed (uint pos, uint removed, uint added); + + private HashTable modes = new HashTable (str_hash, str_equal); + private ListStore modes_list; + + construct { + modes_list = new ListStore (typeof (Mode)); + modes_list.items_changed.connect ((pos, rem, add) => items_changed (pos, rem, add)); + + foreach (var parsed_mode in (Mode.Parsed[]) settings.get_value ("focus-modes")) { + add_mode (parsed_mode); + } + } + + public uint get_n_modes () throws DBusError, IOError { + return modes_list.n_items; + } + + public Mode.Parsed get_mode (uint pos) throws DBusError, IOError { + return ((Mode) modes_list.get_item (pos)).parsed; + } + + public void update_mode (Mode.Parsed parsed) throws DBusError, IOError { + if (parsed.id in modes) { + modes[parsed.id].parsed = parsed; + } else { + add_mode (parsed); + } + + save_modes (); + } + + private void add_mode (Mode.Parsed parsed) { + var mode = new Mode (parsed); + + mode.notify.connect (on_mode_notify); + + modes[mode.id] = mode; + modes_list.append (mode); + } + + private void on_mode_notify (Object obj, ParamSpec pspec) requires (obj is Mode) { + var mode = (Mode) obj; + + uint pos; + if (modes_list.find (mode, out pos)) { + modes_list.items_changed (pos, 1, 1); + } else { + warning ("Unknown mode notified"); + } + } + + public void delete_mode (string id) throws DBusError, IOError { + if (!(id in modes)) { + throw new IOError.NOT_FOUND ("Mode with the same name not found"); + } + + uint pos; + if (modes_list.find (modes[id], out pos)) { + modes_list.remove (pos); + } + + modes.remove (id); + + save_modes (); + } + + private void save_modes () { + Mode.Parsed[] parsed_modes = {}; + foreach (var mode in modes.get_values ()) { + parsed_modes += mode.parsed; + } + + settings.set_value ("focus-modes", parsed_modes); + } +} diff --git a/src/Backends/FocusModes/Mode.vala b/src/Backends/FocusModes/Mode.vala new file mode 100644 index 00000000..3d3b1038 --- /dev/null +++ b/src/Backends/FocusModes/Mode.vala @@ -0,0 +1,140 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io) + * Authored by: Leonhard Kargl + */ + +public class SettingsDaemon.Backends.FocusModes.Mode : Object { + public enum Type { + MANUAL, + DAYLIGHT + } + + public struct Parsed { + string id; + string name; + bool enabled; + bool active; + HashTable args; + HashTable settings; + } + + private const string DARK_MODE = "dark-mode"; + private const string DND = "dnd"; + private const string MONOCHROME = "monochrome"; + + private static HashTable setting_handlers; + + static construct { + setting_handlers = new HashTable (str_hash, str_equal); + + setting_handlers[DARK_MODE] = new GLibSetting ("io.elementary.settings-daemon.prefers-color-scheme", "color-scheme", "prefer-dark"); + setting_handlers[DND] = new GLibSetting ("io.elementary.notifications", "do-not-disturb", true); + setting_handlers[MONOCHROME] = new GLibSetting ("io.elementary.desktop.wm.accessibility", "enable-monochrome-filter", true); + + // setting_handlers[DARK_MODE] = new SettingsDaemon.Backends.Modes.Settings.ColorSchemeSetting (); + // setting_handlers[DND] = new SettingsDaemon.Backends.Modes.Settings.DNDSetting (); + // setting_handlers[MONOCHROME] = new SettingsDaemon.Backends.Modes.Settings.MonochromeSetting (); + } + + private Parsed _parsed; + public Parsed parsed { + get { return _parsed; } + set { + if (is_active) { + unapply_settings (); + } + + _parsed = value; + check_triggers (); + } + } + + public string id { get { return parsed.id; } } + public string name { get { return parsed.name; } } + public bool enabled { get { return parsed.enabled; } } + private bool active { get { return parsed.active; } set { parsed.active = value; } } + public HashTable args { get { return parsed.args; } } + public HashTable settings { get { return parsed.settings; } } + + private TimeTracker time_tracker; + private bool is_active = false; + + public Mode (Parsed parsed) { + Object (parsed: parsed); + } + + construct { + time_tracker = new TimeTracker (); + } + + private void check_triggers () { + bool should_activate = active; + // switch (mode_type) { + // case MANUAL: + // if ("from" in args && "to" in args) { + // is_in = time_tracker.is_in_time_window_manual (args["from"].get_double (), args["to"].get_double ()); + // } + // break; + + // case DAYLIGHT: + // is_in = time_tracker.is_in_time_window_daylight (); + // break; + // } + + if (is_active != should_activate) { + active = should_activate; + + if (enabled) { + if (should_activate) { + apply_settings (); + } else { + unapply_settings (); + } + } + } + } + + // private void apply_settings (HashTable settings) { + // foreach (var key in settings.get_keys ()) { + // switch (key) { + // case DARK_MODE: + // var scheme = ((bool) settings[DARK_MODE]) ? Granite.Settings.ColorScheme.DARK : Granite.Settings.ColorScheme.LIGHT; + // color_scheme_settings.set_enum ("color-scheme", scheme); + // break; + // case DND: + // dnd_settings.set_boolean ("do-not-disturb", (bool) settings[DND]); + // break; + // case MONOCHROME: + // monochrome_settings.set_boolean ("enable-monochrome-filter", (bool) settings[MONOCHROME]); + // break; + // default: + // warning ("Tried to apply unknown setting: %s", key); + // break; + // } + // } + // } + private void apply_settings () requires (!is_active) { + is_active = true; + + foreach (var key in settings.get_keys ()) { + if (key in setting_handlers) { + setting_handlers[key].apply (settings[key]); + } else { + warning ("Tried to apply unknown setting: %s", key); + } + } + } + + private void unapply_settings () requires (is_active) { + is_active = false; + + foreach (var key in settings.get_keys ()) { + if (key in setting_handlers) { + setting_handlers[key].unapply (); + } else { + warning ("Tried to unapply unknown setting: %s", key); + } + } + } +} diff --git a/src/Backends/FocusModes/Setting.vala b/src/Backends/FocusModes/Setting.vala new file mode 100644 index 00000000..66a284d9 --- /dev/null +++ b/src/Backends/FocusModes/Setting.vala @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io) + * Authored by: Leonhard Kargl + */ + +public interface SettingsDaemon.Backends.FocusModes.Setting : Object { + public abstract void apply (Variant value); + public abstract void unapply (); +} diff --git a/src/Backends/ScheduleManager/TimeTracker.vala b/src/Backends/FocusModes/TimeTracker.vala similarity index 96% rename from src/Backends/ScheduleManager/TimeTracker.vala rename to src/Backends/FocusModes/TimeTracker.vala index 3073c5cb..3dd53b95 100644 --- a/src/Backends/ScheduleManager/TimeTracker.vala +++ b/src/Backends/FocusModes/TimeTracker.vala @@ -5,7 +5,7 @@ */ [SingleInstance] -public class SettingsDaemon.Backends.TimeTracker : Object { +public class SettingsDaemon.Backends.FocusModes.TimeTracker : Object { private GClue.Simple simple; private double sunrise = 6.0; diff --git a/src/Backends/ScheduleManager/Schedule.vala b/src/Backends/ScheduleManager/Schedule.vala deleted file mode 100644 index 02bbce0c..00000000 --- a/src/Backends/ScheduleManager/Schedule.vala +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SPDX-License-Identifier: GPL-3.0-or-later - * SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io) - * Authored by: Leonhard Kargl - */ - -public class SettingsDaemon.Backends.Schedule : Object { - public enum Type { - MANUAL, - DAYLIGHT - } - - public struct Parsed { - string id; - Type type; - string name; - bool enabled; - HashTable args; - HashTable active_settings; - HashTable inactive_settings; - } - - public signal void apply_settings (HashTable settings); - - private Parsed _parsed; - public Parsed parsed { - get { return _parsed; } - set { - _parsed = value; - dirty = true; - } - } - - public string id { get { return parsed.id; } } - public Type schedule_type { get { return parsed.type; } } - public string name { get { return parsed.name; } } - public bool enabled { get { return parsed.enabled; } } - public HashTable args { get { return parsed.args; } } - public HashTable active_settings { get { return parsed.active_settings; } } - public HashTable inactive_settings { get { return parsed.inactive_settings; } } //Inactive settings should usually be !active_settings but can also be e.g. a default wallpaper path - - private TimeTracker time_tracker; - private bool active = false; - private bool dirty = false; - - public Schedule (Parsed parsed) { - Object (parsed: parsed); - } - - construct { - time_tracker = new TimeTracker (); - Timeout.add (1000, time_callback); - } - - private bool time_callback () { - bool is_in = false; - switch (schedule_type) { - case MANUAL: - if ("from" in args && "to" in args) { - is_in = time_tracker.is_in_time_window_manual (args["from"].get_double (), args["to"].get_double ()); - } - break; - - case DAYLIGHT: - is_in = time_tracker.is_in_time_window_daylight (); - break; - } - - if (dirty || active != is_in) { - dirty = false; - active = is_in; - if (enabled) { - apply_settings (active ? active_settings : inactive_settings); - } - } - - return Source.CONTINUE; - } -} diff --git a/src/Backends/ScheduleManager/ScheduleManager.vala b/src/Backends/ScheduleManager/ScheduleManager.vala deleted file mode 100644 index b0ac0ab0..00000000 --- a/src/Backends/ScheduleManager/ScheduleManager.vala +++ /dev/null @@ -1,102 +0,0 @@ -/* - * SPDX-License-Identifier: GPL-3.0-or-later - * SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io) - * Authored by: Leonhard Kargl - */ - -[DBus (name="io.elementary.settings_daemon.ScheduleManager")] -public class SettingsDaemon.Backends.ScheduleManager : GLib.Object { - private const string DARK_MODE = "dark-mode"; - private const string DND = "dnd"; - private const string MONOCHROME = "monochrome"; - - private static Settings settings = new Settings ("io.elementary.settings-daemon.schedules"); - private static Settings color_scheme_settings = new Settings ("io.elementary.settings-daemon.prefers-color-scheme"); - private static Settings dnd_settings = new Settings ("io.elementary.notifications"); - private static Settings monochrome_settings = new Settings ("io.elementary.desktop.wm.accessibility"); - - public signal void items_changed (uint pos, uint removed, uint added); - - private HashTable schedules = new HashTable (str_hash, str_equal); - private ListStore schedules_list; - - construct { - schedules_list = new ListStore (typeof (Schedule)); - schedules_list.items_changed.connect ((pos, rem, add) => items_changed (pos, rem, add)); - - foreach (var parsed_schedule in (Schedule.Parsed[]) settings.get_value ("schedules")) { - add_schedule (parsed_schedule); - } - } - - private void add_schedule (Schedule.Parsed parsed) { - var schedule = new Schedule (parsed); - - schedule.apply_settings.connect (apply_settings); - - schedules[schedule.id] = schedule; - schedules_list.append (schedule); - } - - public uint get_n_schedules () throws DBusError, IOError { - return schedules_list.n_items; - } - - public Schedule.Parsed get_schedule (uint pos) throws DBusError, IOError { - return ((Schedule) schedules_list.get_item (pos)).parsed; - } - - public void update_schedule (Schedule.Parsed parsed) throws DBusError, IOError { - if (parsed.id in schedules) { - schedules[parsed.id].parsed = parsed; - } else { - add_schedule (parsed); - } - - save_schedules (); - } - - public void delete_schedule (string id) throws DBusError, IOError { - if (!(id in schedules)) { - throw new IOError.NOT_FOUND ("Schedule with the same name not found"); - } - - uint pos; - if (schedules_list.find (schedules[id], out pos)) { - schedules_list.remove (pos); - } - - schedules.remove (id); - - save_schedules (); - } - - private void apply_settings (HashTable settings) { - foreach (var key in settings.get_keys ()) { - switch (key) { - case DARK_MODE: - var scheme = ((bool) settings[DARK_MODE]) ? Granite.Settings.ColorScheme.DARK : Granite.Settings.ColorScheme.LIGHT; - color_scheme_settings.set_enum ("color-scheme", scheme); - break; - case DND: - dnd_settings.set_boolean ("do-not-disturb", (bool) settings[DND]); - break; - case MONOCHROME: - monochrome_settings.set_boolean ("enable-monochrome-filter", (bool) settings[MONOCHROME]); - break; - default: - warning ("Tried to apply unknown setting: %s", key); - break; - } - } - } - - private void save_schedules () { - Schedule.Parsed[] parsed_schedules = {}; - foreach (var schedule in schedules.get_values ()) { - parsed_schedules += schedule.parsed; - } - - settings.set_value ("schedules", parsed_schedules); - } -} diff --git a/src/meson.build b/src/meson.build index 4c11b12d..851819c4 100644 --- a/src/meson.build +++ b/src/meson.build @@ -10,9 +10,11 @@ sources = files( 'Backends/NightLightSettings.vala', 'Backends/PowerProfilesSync.vala', 'Backends/PrefersColorSchemeSettings.vala', - 'Backends' / 'ScheduleManager' / 'Schedule.vala', - 'Backends' / 'ScheduleManager' / 'ScheduleManager.vala', - 'Backends' / 'ScheduleManager' / 'TimeTracker.vala', + 'Backends' / 'FocusModes' / 'GLibSetting.vala', + 'Backends' / 'FocusModes' / 'Manager.vala', + 'Backends' / 'FocusModes' / 'Mode.vala', + 'Backends' / 'FocusModes' / 'Setting.vala', + 'Backends' / 'FocusModes' / 'TimeTracker.vala', 'Backends/SystemUpdate.vala', 'DBus/DesktopIntegration.vala', 'DBus/ShellKeyGrabber.vala', From 00047d82697569d91124e8ae102ba48ad2fc96fd Mon Sep 17 00:00:00 2001 From: Leonhard Kargl Date: Sun, 19 Oct 2025 17:13:03 +0200 Subject: [PATCH 22/22] Implement schedules again --- src/Backends/FocusModes/Manager.vala | 3 +- src/Backends/FocusModes/Mode.vala | 99 ++++++++++++++-------------- 2 files changed, 52 insertions(+), 50 deletions(-) diff --git a/src/Backends/FocusModes/Manager.vala b/src/Backends/FocusModes/Manager.vala index 7b85ee20..e156364e 100644 --- a/src/Backends/FocusModes/Manager.vala +++ b/src/Backends/FocusModes/Manager.vala @@ -9,6 +9,7 @@ public class SettingsDaemon.Backends.FocusModes.Manager : GLib.Object { private static Settings settings = new Settings ("io.elementary.settings-daemon.focus-modes"); public signal void items_changed (uint pos, uint removed, uint added); + public signal void properties_changed (uint pos); private HashTable modes = new HashTable (str_hash, str_equal); private ListStore modes_list; @@ -54,7 +55,7 @@ public class SettingsDaemon.Backends.FocusModes.Manager : GLib.Object { uint pos; if (modes_list.find (mode, out pos)) { - modes_list.items_changed (pos, 1, 1); + properties_changed (pos); } else { warning ("Unknown mode notified"); } diff --git a/src/Backends/FocusModes/Mode.vala b/src/Backends/FocusModes/Mode.vala index 3d3b1038..962c39a0 100644 --- a/src/Backends/FocusModes/Mode.vala +++ b/src/Backends/FocusModes/Mode.vala @@ -15,7 +15,7 @@ public class SettingsDaemon.Backends.FocusModes.Mode : Object { string name; bool enabled; bool active; - HashTable args; + HashTable schedule; HashTable settings; } @@ -31,10 +31,6 @@ public class SettingsDaemon.Backends.FocusModes.Mode : Object { setting_handlers[DARK_MODE] = new GLibSetting ("io.elementary.settings-daemon.prefers-color-scheme", "color-scheme", "prefer-dark"); setting_handlers[DND] = new GLibSetting ("io.elementary.notifications", "do-not-disturb", true); setting_handlers[MONOCHROME] = new GLibSetting ("io.elementary.desktop.wm.accessibility", "enable-monochrome-filter", true); - - // setting_handlers[DARK_MODE] = new SettingsDaemon.Backends.Modes.Settings.ColorSchemeSetting (); - // setting_handlers[DND] = new SettingsDaemon.Backends.Modes.Settings.DNDSetting (); - // setting_handlers[MONOCHROME] = new SettingsDaemon.Backends.Modes.Settings.MonochromeSetting (); } private Parsed _parsed; @@ -42,10 +38,14 @@ public class SettingsDaemon.Backends.FocusModes.Mode : Object { get { return _parsed; } set { if (is_active) { + // If we're currently active unapply the old settings and reapply the new ones unapply_settings (); + _parsed = value; + apply_settings (); + } else { + _parsed = value; } - _parsed = value; check_triggers (); } } @@ -53,12 +53,13 @@ public class SettingsDaemon.Backends.FocusModes.Mode : Object { public string id { get { return parsed.id; } } public string name { get { return parsed.name; } } public bool enabled { get { return parsed.enabled; } } - private bool active { get { return parsed.active; } set { parsed.active = value; } } - public HashTable args { get { return parsed.args; } } + public bool active { get { return parsed.active; } set { _parsed.active = value; } } + public HashTable schedule { get { return parsed.schedule; } } public HashTable settings { get { return parsed.settings; } } private TimeTracker time_tracker; private bool is_active = false; + private bool user_override = false; public Mode (Parsed parsed) { Object (parsed: parsed); @@ -66,54 +67,54 @@ public class SettingsDaemon.Backends.FocusModes.Mode : Object { construct { time_tracker = new TimeTracker (); + Timeout.add_seconds (1, () => { + check_triggers (); + return Source.CONTINUE; + }); } private void check_triggers () { - bool should_activate = active; - // switch (mode_type) { - // case MANUAL: - // if ("from" in args && "to" in args) { - // is_in = time_tracker.is_in_time_window_manual (args["from"].get_double (), args["to"].get_double ()); - // } - // break; - - // case DAYLIGHT: - // is_in = time_tracker.is_in_time_window_daylight (); - // break; - // } - - if (is_active != should_activate) { - active = should_activate; - - if (enabled) { - if (should_activate) { - apply_settings (); - } else { - unapply_settings (); - } + var is_in = false; + + if ("daylight" in schedule) { + is_in = time_tracker.is_in_time_window_daylight (); + } else if ("manual" in schedule && schedule["manual"].n_children () == 2) { + var from_time = schedule["manual"].get_child_value (0).get_double (); + var to_time = schedule["manual"].get_child_value (1).get_double (); + is_in = time_tracker.is_in_time_window_manual (from_time, to_time); + } + + if (active != is_active) { + // The user toggled the mode manually + user_override = true; + } + + if (user_override && is_in == active) { + // The schedule agrees with the user again so we disable the override and + // use the schedule as the source of truth again + user_override = false; + } + + var should_activate = user_override ? active : is_in; + + if (should_activate && !is_active) { + if (!active) { + active = true; } + + apply_settings (); + } else if (!should_activate && is_active) { + if (active) { + active = false; + } + + unapply_settings (); } + + assert (user_override ^ active == is_in); + assert (active == is_active); } - // private void apply_settings (HashTable settings) { - // foreach (var key in settings.get_keys ()) { - // switch (key) { - // case DARK_MODE: - // var scheme = ((bool) settings[DARK_MODE]) ? Granite.Settings.ColorScheme.DARK : Granite.Settings.ColorScheme.LIGHT; - // color_scheme_settings.set_enum ("color-scheme", scheme); - // break; - // case DND: - // dnd_settings.set_boolean ("do-not-disturb", (bool) settings[DND]); - // break; - // case MONOCHROME: - // monochrome_settings.set_boolean ("enable-monochrome-filter", (bool) settings[MONOCHROME]); - // break; - // default: - // warning ("Tried to apply unknown setting: %s", key); - // break; - // } - // } - // } private void apply_settings () requires (!is_active) { is_active = true;