[gnome-clocks/systemd-timers: 5/5] background: Save alarms to systemd user .timer units
- From: Julian Sparber <jsparber src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-clocks/systemd-timers: 5/5] background: Save alarms to systemd user .timer units
- Date: Wed, 25 Nov 2020 13:24:27 +0000 (UTC)
commit 9f9865f0a615f27a538f790ca17794a45165698a
Author: Julian Sparber <julian sparber net>
Date: Thu Nov 12 16:42:05 2020 +0100
background: Save alarms to systemd user .timer units
meson.build | 1 +
src/alarm-face.vala | 12 ++++
src/alarm-item.vala | 19 +++++
src/config.vapi | 2 +
src/meson.build | 1 +
src/systemd-utils.vala | 187 +++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 222 insertions(+)
---
diff --git a/meson.build b/meson.build
index f30fd0b..3b911cb 100644
--- a/meson.build
+++ b/meson.build
@@ -75,6 +75,7 @@ conf.set_quoted('PACKAGE_VERSION', meson.project_version())
conf.set_quoted('PROFILE', profile)
conf.set_quoted('VERSION', meson.project_version())
conf.set_quoted('GETTEXT_PACKAGE', meson.project_name())
+conf.set_quoted('BINDIR', join_paths(get_option('prefix'), get_option('bindir')))
conf.set_quoted('DATADIR', join_paths(get_option('prefix'), get_option('datadir')))
conf.set_quoted('GNOMELOCALEDIR', join_paths(get_option('prefix'), get_option('localedir')))
if have_first_weekday
diff --git a/src/alarm-face.vala b/src/alarm-face.vala
index 2caa602..3f37440 100644
--- a/src/alarm-face.vala
+++ b/src/alarm-face.vala
@@ -104,7 +104,17 @@ public class Face : Gtk.Stack, Clocks.Clock {
}
private void save () {
+ var systemd_timer = SystemdUtils.Timer.get_default ();
settings.set_value ("alarms", alarms.serialize ());
+ // Update systemd files
+ systemd_timer.clear ();
+ alarms.foreach ((item) => {
+ var alarm = item as Alarm.Item;
+ if (alarm != null)
+ ((!) alarm).save_to_systemd (systemd_timer);
+ });
+ // FIXME: this could cause a race condition when save () is called multible times
+ systemd_timer.commit.begin ();
}
internal void edit (Item alarm) {
@@ -115,6 +125,7 @@ public class Face : Gtk.Stack, Clocks.Clock {
((SetupDialog) dialog).apply_to_alarm ();
save ();
} else if (response == DELETE_ALARM) {
+ alarm.active = false;
alarms.delete_item (alarm);
save ();
}
@@ -124,6 +135,7 @@ public class Face : Gtk.Stack, Clocks.Clock {
}
internal void delete (Item alarm) {
+ alarm.active = false;
alarms.delete_item (alarm);
save ();
}
diff --git a/src/alarm-item.vala b/src/alarm-item.vala
index bed3067..0e96d13 100644
--- a/src/alarm-item.vala
+++ b/src/alarm-item.vala
@@ -132,6 +132,25 @@ private class Item : Object, ContentItem {
days: days);
}
+ public void save_to_systemd (SystemdUtils.Timer systemd_timer) {
+ var wallclock = Utils.WallClock.get_default ();
+ var now = wallclock.date_time;
+ if (snooze_time != null) {
+ // Add timer only if the snooze time is in the future
+ if (this.active && now.compare ((!) snooze_time) <= 0) {
+ systemd_timer.add_time (((!) snooze_time).get_hour (),
+ ((!) snooze_time).get_minute ());
+ }
+ }
+
+ // Add the timer only if the alarm needs to go off in the future
+ if (this.active && (now.compare (time) <= 0 || recurring)) {
+ systemd_timer.add_time (time.get_hour (),
+ time.get_minute (),
+ days);
+ }
+ }
+
private void setup_bell () {
bell = new Utils.Bell ("alarm-clock-elapsed");
notification = new GLib.Notification (_("Alarm"));
diff --git a/src/config.vapi b/src/config.vapi
index 1126340..ad092e9 100644
--- a/src/config.vapi
+++ b/src/config.vapi
@@ -1,7 +1,9 @@
[CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "config.h")]
namespace Config {
public const string VERSION;
+ public const string BINDIR;
public const string PROFILE;
+ public const string PACKAGE_NAME;
public const string NAME_PREFIX;
public const string GETTEXT_PACKAGE;
public const string GNOMELOCALEDIR;
diff --git a/src/meson.build b/src/meson.build
index 4c51188..d44d253 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -18,6 +18,7 @@ clocks_vala_sources = files(
'stopwatch-face.vala',
'stopwatch-lap.vala',
'stopwatch-laps-row.vala',
+ 'systemd-utils.vala',
'timer-face.vala',
'timer-item.vala',
'timer-row.vala',
diff --git a/src/systemd-utils.vala b/src/systemd-utils.vala
new file mode 100644
index 0000000..a7b4ff0
--- /dev/null
+++ b/src/systemd-utils.vala
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2020 Purism SPC
+ *
+ * Authors:
+ * Julian Sparber <julian sparber net>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+
+namespace Clocks {
+namespace SystemdUtils {
+
+ [DBus (name = "org.freedesktop.systemd1.Manager")]
+ interface Systemd1 : Object {
+ public abstract void enable_unit_files (string[] files,
+ bool runtime = false,
+ bool force = false) throws GLib.Error;
+ public abstract void disable_unit_files (string[] files,
+ bool runtime = false) throws GLib.Error;
+ public abstract void start_unit (string name,
+ string mode) throws GLib.Error;
+ public abstract void stop_unit (string name,
+ string mode) throws GLib.Error;
+ public abstract void reload () throws GLib.Error;
+ }
+
+ private struct Times {
+ int hour;
+ int minute;
+ Utils.Weekdays? days;
+ }
+
+ public class Timer : GLib.Object {
+ private static Timer? instance;
+ const string TIMER_FILE = Config.PACKAGE_NAME + Config.PROFILE + ".timer";
+ const string SERVICE_FILE = Config.PACKAGE_NAME + Config.PROFILE + ".service";
+ SList<Times?> times = new SList<Times?> ();
+ bool in_progess = false;
+
+ public static Timer get_default () {
+ if (instance == null) {
+ instance = new Timer ();
+ }
+ return (!) instance;
+ }
+
+ private Timer () {
+ ensure_service ();
+ }
+
+ public void clear () {
+ times = new SList<Times?> ();
+ }
+
+ public void add_time (int hour, int minute, Utils.Weekdays? days = null) {
+ times.append ( {hour, minute, days } );
+ }
+
+ public async void commit () throws Error {
+ if (in_progess)
+ return;
+ in_progess = true;
+ File file = get_unit_file ();
+ Systemd1 systemd1 = yield Bus.get_proxy (BusType.SESSION,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1");
+
+ if ((SList<Times?>?) times != null) {
+ debug ("Update .timer unit");
+ FileOutputStream os = file.replace (null,
+ false,
+ FileCreateFlags.PRIVATE |
+ FileCreateFlags.REPLACE_DESTINATION);
+ var builder = new StringBuilder ();
+ builder.append ("[Unit]\n");
+ builder.append ("Description=GNOME clocks timer\n");
+ builder.append ("\n");
+ builder.append ("[Timer]\n");
+ builder.append ("Unit=gnome-clocks.service\n");
+ builder.append (build_timers ());
+ builder.append ("Persistent=true\n");
+ builder.append ("\n");
+ builder.append ("[Install]\n");
+ builder.append ("WantedBy=default.target\n");
+ os.write (builder.str.data);
+
+ debug ("Enable and start .timer unit");
+ systemd1.reload ();
+ systemd1.enable_unit_files ({TIMER_FILE});
+ systemd1.start_unit (TIMER_FILE, "replace");
+ } else {
+ if (file.query_exists ()) {
+ debug ("Stop and disable .timer unit");
+ systemd1.disable_unit_files ({TIMER_FILE});
+ systemd1.stop_unit (TIMER_FILE, "replace");
+
+ debug ("Delete .timer unit");
+ yield file.delete_async ();
+ }
+ }
+ in_progess = false;
+ }
+
+ private string build_timers () {
+ var builder = new StringBuilder ();
+ times.foreach ( (t) => {
+ if (t != null) {
+ var time = (!) t;
+ builder.append_printf ("OnCalendar=%s *-*-* %d:%d\n",
+ get_enabled_days_string (time.days),
+ time.hour, time.minute);
+ }
+ });
+ return builder.str;
+ }
+
+ private static string get_enabled_days_string (Utils.Weekdays? days) {
+ if (days == null || ((!) days).empty) {
+ return "";
+ }
+ var builder = new StringBuilder ();
+ var first = true;
+ for (var day = 0; day < 7; day++) {
+ if (((!)days).get (day)) {
+ if (!first)
+ builder.append (",");
+ else
+ first = false;
+ builder.append (get_day_string (day));
+ }
+ }
+ return builder.str;
+ }
+
+ private static string get_day_string (Utils.Weekdays.Day day) {
+ switch (day) {
+ case Utils.Weekdays.Day.MON:
+ return "Mon";
+ case Utils.Weekdays.Day.TUE:
+ return "Tue";
+ case Utils.Weekdays.Day.WED:
+ return "Wed";
+ case Utils.Weekdays.Day.THU:
+ return "Thu";
+ case Utils.Weekdays.Day.FRI:
+ return "Fri";
+ case Utils.Weekdays.Day.SAT:
+ return "Sat";
+ case Utils.Weekdays.Day.SUN:
+ return "Sun";
+ default:
+ return "";
+ }
+ }
+
+ private File get_unit_file () {
+ File file = File.new_build_filename (GLib.Environment.get_user_config_dir (),
+ "/systemd/user/",
+ TIMER_FILE);
+ return file;
+ }
+
+ private void ensure_service () {
+ File file = File.new_build_filename (GLib.Environment.get_user_config_dir (),
+ "/systemd/user/",
+ SERVICE_FILE);
+ if (!file.query_exists ()) {
+ debug ("Add new %s unit", SERVICE_FILE);
+ try {
+ FileOutputStream os = file.create (FileCreateFlags.PRIVATE);
+ var builder = new StringBuilder ();
+ builder.append ("[Unit]\n");
+ builder.append ("Description=GNOME Clocks (simple clock application)\n");
+ builder.append ("\n");
+ builder.append ("[Service]\n");
+ builder.append ("Type=simple\n");
+ builder.append_printf ("ExecStart=%s/gnome-clocks\n", Config.BINDIR);
+ os.write (builder.str.data);
+ } catch (Error error) {
+ warning ("Couldn't create systemd unit: %s", error.message);
+ }
+ }
+ }
+ }
+}
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]