]> ToastFreeware Gitweb - gregoa/zavai.git/blob - src/clock.vala
Retabbed
[gregoa/zavai.git] / src / clock.vala
1 /*
2  * clock - clock resource for zavai
3  *
4  * Copyright (C) 2009  Enrico Zini <enrico@enricozini.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 using GLib;
22
23 namespace zavai {
24 namespace clock {
25
26 public enum SourceType
27 {
28     SYSTEM,
29     GPS
30 }
31
32 /*
33 TODO: schedule alarms via at
34
35 Uses the 'z' queue.
36
37 atq -q z  can be used to list the jobs (for example, at startup, or after a job has run)
38 at -c id  can be used to query a job, parsing its contents (which can have
39           comments or variables being set)
40 zavai --notify ...  can be used to notify the job (and start zavai if it's not running)
41
42 Alarm needs to be able to serialize itself to an at invocation and to
43 deserialize itself from the output of at -c
44
45 Alarm needs to deserialize also a job with no special markers whatsoever: a
46 generic at job.
47
48
49
50 refresh_alarms()
51 {
52     oldtime = next_alarm ? next_alarm.time : 0
53     next_alarm = the first alarm from atq
54     if (oldtime != next_alarm.time)
55     {
56         remove existing triggers
57           (triggers can be skipped if we don't need to support non-zavai alarms)
58         schedule a trigger calling refresh_alarms() at next_alarm.time + 30 seconds
59           (triggers can be skipped if we don't need to support non-zavai alarms)
60     }
61 }
62
63 at clock constructor: refresh_alarms()
64 inotifywait -e close /usr/bin/at -> refresh_alarms()  (shows when someone has just used at)
65   (can be skipped if we don't need to support non-zavai alarms)
66 at alarm triggered through zavai: refresh_alarms()
67
68
69 */
70
71
72 public class Alarm : Object
73 {
74     public at.Event ev;
75     public string label;
76
77     // Schedule with at
78     public static void schedule(string timespec, string label) throws Error
79     {
80         string argv[5];
81         argv[0] = "/usr/bin/at";
82         argv[1] = "-q";
83         argv[2] = "z";
84         argv[3] = timespec;
85         argv[4] = null;
86
87         Pid pid;
88         int stdinfd;
89
90         if (!Process.spawn_async_with_pipes("/", argv, null, SpawnFlags.STDERR_TO_DEV_NULL, null, out pid, out stdinfd, null, null))
91             return;
92
93         {
94             FileStream fs = FileStream.fdopen(stdinfd, "w");
95             string display = GLib.Environment.get_variable("DISPLAY");
96             if (display != null)
97                 fs.printf("DISPLAY=\"%s\"; export DISPLAY\n", display);
98             fs.printf("# Zavai variables start here\n");
99             fs.printf("ZAVAI_LABEL=\"%s\"\n", label.escape(""));
100             fs.printf("# Zavai commands starts here\n");
101             fs.printf("%s notify \"$ZAVAI_LABEL\"", zavai.config.argv0);
102         }
103         
104         Process.close_pid(pid);
105     }
106
107     // Get the label of the job with the given at ID
108     public static string? getLabel(int atID)
109     {
110         string label = null;
111         at.jobContents(atID, fd => {
112             FileStream fs = FileStream.fdopen(fd, "r");
113             while (true)
114             {
115                 string? line = fs.read_line();
116                 if (line == null) break;
117                 if (line.has_prefix("ZAVAI_LABEL=\""))
118                 {
119                     size_t size = line.size();
120                     if (size < 15) continue;
121                     label = line.substring(13, (long)(size - 14));
122                     label = label.compress();
123                     break;
124                 }
125             }
126             return true;
127         });
128         return label;
129     }
130 }
131
132 [DBus (name = "org.enricozini.zavai.Alarm")]
133 public class ZavaiClock : Object {
134     public void Notify (string label) {
135         clock.notify_alarm(label);
136     }
137 }
138
139 public class Clock: zavai.Service
140 {
141     protected time_t last_gps_time;
142     protected time_t last_gps_time_system_time;
143     protected time_t last_system_time;
144     protected dynamic DBus.Object gps_time;
145     protected uint system_time_timeout;
146     protected time_t last_minute;
147     protected time_t chosen_time;
148     protected SourceType chosen_type;
149     protected ZavaiClock dbusClock;
150
151     protected dynamic DBus.Object otimed_alarm;
152     protected dynamic DBus.Object rtc;
153     protected SList<Alarm> alarms;
154
155     // Ticks once a minute
156     public signal void minute_changed(long time, SourceType source);
157     public signal void schedule_changed(Alarm? next);
158     public signal void alarm_triggered(string label);
159
160     public Clock()
161     {
162         Object(name: "clock");
163         alarms = null;
164         dbusClock = new ZavaiClock();
165         last_minute = 0;
166         last_gps_time = 0;
167         last_gps_time_system_time = 0;
168         last_system_time = time_t();
169         chosen_time = last_system_time;
170
171         gps_time = zavai.registry.sbus.get_object(
172                 "org.freesmartphone.ogpsd",
173                 "/org/freedesktop/Gypsy",
174                 "org.freedesktop.Gypsy.Time");
175
176         // FSO alarm system
177         otimed_alarm = zavai.registry.sbus.get_object(
178                 "org.freesmartphone.otimed",
179                 "/org/freesmartphone/Time/Alarm",
180                 "org.freesmartphone.Time.Alarm");
181
182         rtc = zavai.registry.sbus.get_object(
183                 "org.freesmartphone.odeviced",
184                 "/org/freesmartphone/Device/RTC/0",
185                 "org.freesmartphone.Device.RealtimeClock");
186
187         zavai.registry.sbus.register_object("/org/enricozini/Zavai/Clock", dbusClock);
188     }
189
190     public void notify_alarm(string label)
191     {
192         stderr.printf("Notifying %s\n", label);
193         alarm_triggered(label);
194         schedule_changed(next_alarm());
195     }
196
197     public Alarm? next_alarm()
198     {
199         at.Event ev;
200         ev = at.earliestID("z");
201         if (ev.deadline == 0)
202             return null;
203         string label = Alarm.getLabel(ev.id);
204         Alarm res = new Alarm();
205         res.ev = ev;
206         res.label = label;
207         return res;
208     }
209
210     public void schedule(string timespec, string label) throws Error
211     {
212         Alarm.schedule(timespec, label);
213         schedule_changed(next_alarm());
214     }
215
216     private void on_gps_time(dynamic DBus.Object pos, int t)
217     {
218         if (t == 0)
219         {
220             last_gps_time_system_time = 0;
221             update_time();
222         } else {
223             last_gps_time = (time_t)t;
224             last_gps_time_system_time = time_t();
225             update_time();
226         }
227     }
228
229     private bool on_system_time()
230     {
231         last_system_time = time_t();
232         update_time();
233         return true;
234     }
235
236     private void update_time()
237     {
238         if (last_gps_time_system_time + 10 > last_system_time)
239         {
240             chosen_time = last_gps_time;
241             chosen_type = SourceType.GPS;
242         }
243         else
244         {
245             chosen_time = last_system_time;
246             chosen_type = SourceType.SYSTEM;
247         }
248         if (chosen_time / 60 != last_minute)
249         {
250             last_minute = chosen_time / 60;
251             minute_changed(chosen_time, chosen_type);
252         }
253     }
254
255     /// Request GPS resource
256     public override void start()
257     {
258         if (started) return;
259
260         system_time_timeout = Timeout.add(5000, on_system_time);
261         gps_time.TimeChanged += on_gps_time;
262         last_system_time = time_t();
263         update_time();
264
265         base.start();
266     }
267
268     public override void stop()
269     {
270         if (!started) return;
271
272         Source.remove(system_time_timeout);
273         gps_time.TimeChanged -= on_gps_time;
274
275         base.stop();
276     }
277 }
278
279 public Clock clock = null;
280
281 public void init()
282 {
283     clock = new Clock();
284
285     zavai.registry.register_service(clock);
286 }
287
288 }
289 }