/* * clock - clock resource for zavai * * Copyright (C) 2009 Enrico Zini * * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ using GLib; namespace zavai { namespace clock { public enum SourceType { SYSTEM, GPS } /* TODO: schedule alarms via at Uses the 'z' queue. atq -q z can be used to list the jobs (for example, at startup, or after a job has run) at -c id can be used to query a job, parsing its contents (which can have comments or variables being set) zavai --notify ... can be used to notify the job (and start zavai if it's not running) Alarm needs to be able to serialize itself to an at invocation and to deserialize itself from the output of at -c Alarm needs to deserialize also a job with no special markers whatsoever: a generic at job. refresh_alarms() { oldtime = next_alarm ? next_alarm.time : 0 next_alarm = the first alarm from atq if (oldtime != next_alarm.time) { remove existing triggers (triggers can be skipped if we don't need to support non-zavai alarms) schedule a trigger calling refresh_alarms() at next_alarm.time + 30 seconds (triggers can be skipped if we don't need to support non-zavai alarms) } } at clock constructor: refresh_alarms() inotifywait -e close /usr/bin/at -> refresh_alarms() (shows when someone has just used at) (can be skipped if we don't need to support non-zavai alarms) at alarm triggered through zavai: refresh_alarms() */ public class Alarm : Object { public at.Event ev; public string label; // Schedule with at public static void schedule(string timespec, string label) throws Error { string argv[5]; argv[0] = "/usr/bin/at"; argv[1] = "-q"; argv[2] = "z"; argv[3] = timespec; argv[4] = null; Pid pid; int stdinfd; if (!Process.spawn_async_with_pipes("/", argv, null, SpawnFlags.STDERR_TO_DEV_NULL, null, out pid, out stdinfd, null, null)) return; { FileStream fs = FileStream.fdopen(stdinfd, "w"); string display = GLib.Environment.get_variable("DISPLAY"); if (display != null) fs.printf("DISPLAY=\"%s\"; export DISPLAY\n", display); fs.printf("# Zavai variables start here\n"); fs.printf("ZAVAI_LABEL=\"%s\"\n", label.escape("")); fs.printf("# Zavai commands starts here\n"); fs.printf("%s notify \"$ZAVAI_LABEL\"", zavai.config.argv0); } Process.close_pid(pid); } // Get the label of the job with the given at ID public static string? getLabel(int atID) { string label = null; at.jobContents(atID, fd => { FileStream fs = FileStream.fdopen(fd, "r"); while (true) { string? line = fs.read_line(); if (line == null) break; if (line.has_prefix("ZAVAI_LABEL=\"")) { size_t size = line.size(); if (size < 15) continue; label = line.substring(13, (long)(size - 14)); label = label.compress(); break; } } return true; }); return label; } } [DBus (name = "org.enricozini.zavai.Alarm")] public class ZavaiClock : Object { public void Notify (string label) { clock.notify_alarm(label); } } public class Clock: zavai.Service { protected time_t last_gps_time; protected time_t last_gps_time_system_time; protected time_t last_system_time; protected dynamic DBus.Object gps_time; protected uint system_time_timeout; protected time_t last_minute; protected time_t chosen_time; protected SourceType chosen_type; protected ZavaiClock dbusClock; protected dynamic DBus.Object otimed_alarm; protected dynamic DBus.Object rtc; protected SList alarms; // Ticks once a minute public signal void minute_changed(long time, SourceType source); public signal void schedule_changed(Alarm? next); public signal void alarm_triggered(string label); public Clock() { Object(name: "clock"); alarms = null; dbusClock = new ZavaiClock(); last_minute = 0; last_gps_time = 0; last_gps_time_system_time = 0; last_system_time = time_t(); chosen_time = last_system_time; gps_time = zavai.registry.sbus.get_object( "org.freesmartphone.ogpsd", "/org/freedesktop/Gypsy", "org.freedesktop.Gypsy.Time"); // FSO alarm system otimed_alarm = zavai.registry.sbus.get_object( "org.freesmartphone.otimed", "/org/freesmartphone/Time/Alarm", "org.freesmartphone.Time.Alarm"); rtc = zavai.registry.sbus.get_object( "org.freesmartphone.odeviced", "/org/freesmartphone/Device/RTC/0", "org.freesmartphone.Device.RealtimeClock"); zavai.registry.sbus.register_object("/org/enricozini/Zavai/Clock", dbusClock); } public void notify_alarm(string label) { stderr.printf("Notifying %s\n", label); alarm_triggered(label); schedule_changed(next_alarm()); } public Alarm? next_alarm() { at.Event ev; ev = at.earliestID("z"); if (ev.deadline == 0) return null; string label = Alarm.getLabel(ev.id); Alarm res = new Alarm(); res.ev = ev; res.label = label; return res; } public void schedule(string timespec, string label) throws Error { Alarm.schedule(timespec, label); schedule_changed(next_alarm()); } private void on_gps_time(dynamic DBus.Object pos, int t) { if (t == 0) { last_gps_time_system_time = 0; update_time(); } else { last_gps_time = (time_t)t; last_gps_time_system_time = time_t(); update_time(); } } private bool on_system_time() { last_system_time = time_t(); update_time(); return true; } private void update_time() { if (last_gps_time_system_time + 10 > last_system_time) { chosen_time = last_gps_time; chosen_type = SourceType.GPS; } else { chosen_time = last_system_time; chosen_type = SourceType.SYSTEM; } if (chosen_time / 60 != last_minute) { last_minute = chosen_time / 60; minute_changed(chosen_time, chosen_type); } } /// Request GPS resource public override void start() { if (started) return; system_time_timeout = Timeout.add(5000, on_system_time); gps_time.TimeChanged += on_gps_time; last_system_time = time_t(); update_time(); base.start(); } public override void stop() { if (!started) return; Source.remove(system_time_timeout); gps_time.TimeChanged -= on_gps_time; base.stop(); } } public Clock clock = null; public void init() { clock = new Clock(); zavai.registry.register_service(clock); } } }