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