bd99ffd4f0b961380b4546308ec1a2adc4c66389
[gregoa/zavai.git] / src / app_power.vala
1 /*
2  * app_power - zavai power handling
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 ui {
25 namespace power {
26
27 // Compute a-b in microseconds
28 static long timediff(Posix.timeval* a, Posix.timeval* b)
29 {
30         return (a->tv_sec - b->tv_sec) * 1000000 + (a->tv_usec - b->tv_usec);
31 }
32
33 public class Power : zavai.Resource, Object
34 {
35         public dynamic DBus.Object usage;
36         public bool screen_locked;
37         private int screen_lock_fd;
38         // Timestamp of the past power button pressed even (0 if the button has
39         // been released)
40         private Posix.timeval last_down;
41
42         private bool hide_after_closing_power_menu;
43
44         public signal void screen_lock_changed(bool state);
45
46         public signal void power_short_press();
47         public signal void power_long_press();
48         private uint button_press_timeout;
49
50         public Power()
51         {
52                 screen_locked = false;
53                 screen_lock_fd = -1;
54                 hide_after_closing_power_menu = false;
55                 last_down.tv_sec = 0;
56                 last_down.tv_usec = 0;
57                 button_press_timeout = 0;
58
59                 usage = zavai.registry.sbus.get_object(
60                         "org.freesmartphone.ousaged",
61                         "/org/freesmartphone/Usage",
62                         "org.freesmartphone.Usage");
63
64                 zavai.input.power_button.power_button += on_power_button;
65                 zavai.input.power_button.request("zavai.ui.powerbutton.power");
66
67                 power_short_press += on_power_short_press;
68                 power_long_press += on_power_long_press;
69         }
70
71         public void shutdown()
72         {
73                 zavai.input.power_button.release("zavai.ui.powerbutton.power");
74         }
75
76         public void do_suspend() { usage.Suspend(); }
77         public void do_shutdown() { usage.Shutdown(); }
78         public void do_reboot() { usage.Reboot(); }
79
80         public void set_screen_lock(bool locked)
81         {
82                 if (locked && screen_locked)
83                         return;
84                 if (!locked && !screen_locked)
85                         return;
86
87                 if (locked)
88                 {
89                         screen_lock_fd = Posix.open("/dev/input/event1", Posix.O_RDWR | Posix.O_NONBLOCK);
90                         if (screen_lock_fd < 0)
91                         {
92                                 zavai.log.error("Cannot open /dev/input/event1");
93                                 return;
94                         }
95
96                         int EVIOCGRAB = 0x40044590;
97                         if (Posix.ioctl(screen_lock_fd, EVIOCGRAB, locked ? 1 : 0) != 0)
98                         {
99                                 zavai.log.error("Cannot EVIOCGRAB /dev/input/event1");
100                                 Posix.close(screen_lock_fd);
101                                 return;
102                         }
103                 } else {
104                         Posix.close(screen_lock_fd);
105                 }
106                 screen_locked = locked;
107                 if (!locked)
108                         backlight.wiggle();
109
110                 screen_lock_changed(locked);
111         }
112
113         private bool on_power_button_timeout()
114         {
115                 last_down.tv_sec = 0;
116                 last_down.tv_usec = 0;
117                 power_long_press();
118                 // Do not reschedule
119                 return false;
120         }
121
122         private void on_power_button(Posix.timeval* t, bool pressed)
123         {
124                 bool short_press = false;
125                 bool long_press = false;
126
127                 if (pressed)
128                 {
129                         if (last_down.tv_sec == 0)
130                         {
131                                 last_down = *t;
132                                 button_press_timeout = Timeout.add(1000, on_power_button_timeout);
133                         }
134                         else
135                         {
136                                 long diff = timediff(t, &last_down);
137                                 long_press = diff >= 1000000;
138                         }
139                 } else {
140                         if (last_down.tv_sec == 0)
141                         {
142                                 // Ignore: release has been simulated with the timeout
143                         } else {
144                                 if (button_press_timeout != 0)
145                                 {
146                                         // Cancel the timeout
147                                         Source.remove(button_press_timeout);
148                                         button_press_timeout = 0;
149                                 }
150                                 long diff = timediff(t, &last_down);
151                                 if (diff >= 1000000)
152                                         long_press = true;
153                                 else
154                                         short_press = true;
155
156                                 last_down.tv_sec = 0;
157                                 last_down.tv_usec = 0;
158                         }
159                 }
160
161                 if (long_press) power_long_press();
162                 if (short_press) power_short_press();
163         }
164
165         private void on_power_short_press()
166         {
167                 if (screen_locked)
168                         // Short press: turn on backlight for a bit
169                         backlight.wiggle();
170                 else
171                         // Short press: toggle power menu
172                         power_menu.toggle();
173         }
174
175         private void on_power_long_press()
176         {
177                 if (screen_locked)
178                         // Long press: unlock
179                         set_screen_lock(false);
180                 else
181                         // Long press: lock screen
182                         set_screen_lock(true);
183         }
184 }
185
186 public class BatteryIcon : Gtk.StatusIcon
187 {
188         public dynamic DBus.Object battery;
189         protected string last_status;
190         protected int last_capacity;
191
192         public BatteryIcon()
193         {
194                 battery = zavai.registry.sbus.get_object(
195                         "org.freesmartphone.odeviced",
196                         "/org/freesmartphone/Device/PowerSupply/battery",
197                         "org.freesmartphone.Device.PowerSupply");
198
199                 // activate += on_activate;
200
201                 battery.PowerStatus += on_power_status;
202                 battery.Capacity += on_capacity;
203
204                 last_status = battery.GetPowerStatus();
205                 last_capacity = battery.GetCapacity();
206                 
207                 update_icon();
208         }
209
210         private void on_power_status(dynamic DBus.Object bat, string status)
211         {
212                 zavai.log.info("New battery status: " + status);
213                 last_status = status;
214                 update_icon();
215         }
216
217         private void on_capacity(dynamic DBus.Object bat, int val)
218         {
219 stderr.printf("NEW CAPACITY: %d\n", val);
220                 last_capacity = val;
221                 update_icon();
222         }
223
224         /*
225         private void on_activate()
226         {
227         }
228         */
229
230         protected void update_icon()
231         {
232                 string name = zavai.config.icondir + "/battery/";
233
234                 if (last_status == "charging")
235                         name += "%02d0_charging_500.png".printf(last_capacity/10);
236                 else
237                         name += "%02d0.png".printf(last_capacity/10);
238
239 stderr.printf("Loading icon from %s\n", name);
240
241                 set_from_file(name);
242         }
243 }
244
245 public class ScreenLockButton : Gtk.Button
246 {
247         public ScreenLockButton()
248         {
249                 label = "Lock screen";
250                 clicked += on_clicked;
251                 set_size_request(0, zavai.config.min_button_height);
252         }
253
254         public void on_clicked()
255         {
256                 zavai.log.info("Locking screen");
257                 power.set_screen_lock(true);
258                 power_menu.hide();
259         }
260 }
261
262 public class SuspendButton : Gtk.Button
263 {
264         public SuspendButton()
265         {
266                 label = "Suspend";
267                 clicked += on_clicked;
268                 set_size_request(0, zavai.config.min_button_height);
269         }
270
271         public void on_clicked()
272         {
273                 zavai.log.info("Suspending the phone via FSO");
274                 power.do_suspend();
275                 power_menu.hide();
276         }
277 }
278
279 public class ShutdownButton : Gtk.Button
280 {
281         public ShutdownButton()
282         {
283                 label = "Shut down";
284                 clicked += on_clicked;
285                 set_size_request(0, zavai.config.min_button_height);
286         }
287
288         public void on_clicked()
289         {
290                 zavai.log.info("Shutting down the phone via FSO");
291                 power.do_shutdown();
292                 power_menu.hide();
293         }
294 }
295
296 public class RebootButton : Gtk.Button
297 {
298         public RebootButton()
299         {
300                 label = "Reboot";
301                 clicked += on_clicked;
302                 set_size_request(0, zavai.config.min_button_height);
303         }
304
305         public void on_clicked()
306         {
307                 zavai.log.info("Rebooting the phone via FSO");
308                 power.do_reboot();
309                 power_menu.hide();
310         }
311 }
312
313 // For a list of dbus services, look in /etc/dbus-1/system.d/
314 public class Backlight: zavai.Service
315 {
316         public dynamic DBus.Object usage;
317
318         public Backlight()
319         {
320                 name = "backlight";
321
322                 usage = zavai.registry.sbus.get_object(
323                         "org.freesmartphone.ousaged",
324                         "/org/freesmartphone/Usage",
325                         "org.freesmartphone.Usage");
326         }
327
328         // Turn the backlight and then let it fade off
329         public void wiggle()
330         {
331                 // There must be a better method
332                 usage.RequestResource("Display");
333                 usage.ReleaseResource("Display");
334         }
335
336         /// Request GPS resource
337         public override void start()
338         {
339                 if (started) return;
340                 try {
341                         usage.RequestResource("Display");
342                         zavai.log.info("Acquired display");
343                         base.start();
344                 } catch (GLib.Error e) {
345                         zavai.log.error(e.message);
346                 }
347                 base.start();
348         }
349
350         // Release usage of GPS
351         public override void stop()
352         {
353                 if (!started) return;
354                 try {
355                         usage.ReleaseResource("Display");
356                         zavai.log.info("Released display");
357                         base.stop();
358                 } catch (GLib.Error e) {
359                         zavai.log.error(e.message);
360                 }
361                 base.stop();
362         }
363 }
364
365 public class PowerMenu : zavai.Resource, Gtk.Window
366 {
367         protected Gtk.VBox vbox;
368         protected ScreenLockButton act_screen_lock;
369         protected SuspendButton act_suspend;
370         protected ShutdownButton act_shutdown;
371         protected RebootButton act_reboot;
372         protected ServiceRequestLink act_backlight_on;
373         protected bool shown;
374
375         public PowerMenu()
376         {
377                 type = Gtk.WindowType.TOPLEVEL;
378                 title = "Power Menu";
379                 shown = false;
380                 destroy_with_parent = true;
381                 set_transient_for(zavai.app);
382                 set_modal(true);
383                 set_position(Gtk.WindowPosition.CENTER_ON_PARENT);
384                 set_size_request(300, 500);
385
386                 vbox = new Gtk.VBox(false, 0);
387                 add(vbox);
388
389                 //destroy += Gtk.main_quit;
390                 //set_events(get_events() | Gdk.EventMask.VISIBILITY_NOTIFY_MASK);
391                 //visibility_notify_event += on_visibility;
392                 set_skip_pager_hint(true);
393                 set_skip_taskbar_hint(true);
394                 set_type_hint(Gdk.WindowTypeHint.POPUP_MENU);
395
396                 act_screen_lock = new ScreenLockButton();
397                 vbox.pack_start(act_screen_lock, false, false, 0);
398
399                 act_suspend = new SuspendButton();
400                 vbox.pack_start(act_suspend, false, false, 0);
401
402                 act_shutdown = new ShutdownButton();
403                 vbox.pack_start(act_shutdown, false, false, 0);
404
405                 act_reboot = new RebootButton();
406                 vbox.pack_start(act_reboot, false, false, 0);
407
408                 act_backlight_on = new ServiceRequestLink("backlight", "Keep backlight on", "Let backlight fade");
409                 vbox.pack_start(act_backlight_on, false, false, 0);
410
411                 //vbox.show_all();
412         }
413
414         public void toggle()
415         {
416                 if (!shown)
417                 {
418                         show_all();
419                         show();
420                         visible = true;
421                         present();
422                         shown = true;
423                 } else {
424                         // TODO: do more in case it is visible but has no visibility (is covered by others)
425                         visible = !visible;
426                         if (visible)
427                                 present();
428                 }                               
429         }
430
431         public void hide()
432         {
433                 visible = false;
434         }
435
436         public void shutdown() {}
437 }
438
439 /*
440 public class TogglePowerMenu : Gtk.Button
441 {
442         public TogglePowerMenu()
443         {
444                 label = "Toggle power menu";
445                 clicked += on_clicked;
446                 set_size_request(0, zavai.config.min_button_height);
447         }
448
449         public void on_clicked()
450         {
451                 zavai.log.info("Toggling power menu");
452                 power_menu.toggle();
453         }
454 }
455 */
456
457 Power power;
458 PowerMenu power_menu;
459 BatteryIcon battery_icon;
460 Backlight backlight;
461 //TogglePowerMenu tpm;
462
463 public void init()
464 {
465         power = new Power();
466         backlight = new Backlight();
467         zavai.registry.register_service(backlight);
468
469         battery_icon = new BatteryIcon();
470         battery_icon.set_visible(true);
471
472         power_menu = new PowerMenu();
473         zavai.registry.register_resource("powermenu", power_menu);
474         
475     //zavai.registry.getmenu("menu.main").add_applet("menu.power");
476         //tpm = new TogglePowerMenu();
477     //zavai.registry.getmenu("menu.main").add_widget(tpm);
478
479     /*
480         raise_icon = new RaiseIcon();
481         raise_icon.set_visible(true);
482
483         close_or_back = new CloseOrBack();
484         close_or_back.set_visible(true);
485
486         window_list = new WindowList("Current apps");
487         zavai.registry.register_applet("wm.list", window_list);
488         zavai.registry.getmenu("menu.main").add_applet("wm.list");
489
490         try {
491                 launcher = new Launcher("Run program");
492         } catch (Error e) {
493                 zavai.log.error("Not running launcher: " + e.message);
494                 launcher = null;
495         }
496
497         if (launcher != null)
498         {
499                 zavai.registry.register_applet("wm.launcher", launcher);
500                 zavai.registry.getmenu("menu.main").add_applet("wm.launcher");
501         }
502     */
503 }
504
505 }
506 }
507 }