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