/* * app_power - zavai power handling * * 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 ui { namespace power { // Compute a-b in microseconds static long timediff(Posix.timeval* a, Posix.timeval* b) { return (a->tv_sec - b->tv_sec) * 1000000 + (a->tv_usec - b->tv_usec); } public class Power : zavai.Resource, Object { public dynamic DBus.Object usage; public bool screen_locked; private int screen_lock_fd; // Timestamp of the past power button pressed even (0 if the button has // been released) private Posix.timeval last_down; private Posix.timeval last_short_press; private bool hide_after_closing_power_menu; public signal void screen_lock_changed(bool state); public signal void power_short_press(Posix.timeval* t); public signal void power_long_press(); private uint button_press_timeout; public Power() { screen_locked = false; screen_lock_fd = -1; hide_after_closing_power_menu = false; last_down.tv_sec = 0; last_down.tv_usec = 0; last_short_press.tv_sec = 0; last_short_press.tv_usec = 0; button_press_timeout = 0; usage = zavai.registry.sbus.get_object( "org.freesmartphone.ousaged", "/org/freesmartphone/Usage", "org.freesmartphone.Usage"); zavai.input.power_button.power_button += on_power_button; zavai.input.power_button.request("zavai.ui.powerbutton.power"); power_short_press += on_power_short_press; power_long_press += on_power_long_press; } public void shutdown() { zavai.input.power_button.release("zavai.ui.powerbutton.power"); } public void do_suspend() { try { usage.Suspend(); } catch (Error e) { zavai.log.error("Suspending phone: " + e.message); } } public void do_shutdown() { try { //usage.Shutdown(); zavai.app.run_script("shutdown -h now"); } catch (Error e) { zavai.log.error("Shutting down phone: " + e.message); } } public void do_reboot() { try { //usage.Reboot(); zavai.app.run_script("shutdown -r now"); } catch (Error e) { zavai.log.error("Rebooting phone: " + e.message); } } public void set_screen_lock(bool locked) { if (locked && screen_locked) return; if (!locked && !screen_locked) return; if (locked) { screen_lock_fd = Posix.open("/dev/input/event1", Posix.O_RDWR | Posix.O_NONBLOCK); if (screen_lock_fd < 0) { zavai.log.error("Cannot open /dev/input/event1"); return; } // FIXME: X won't see events, but it's still generating interrupts, // isn't it? int EVIOCGRAB = 0x40044590; if (Posix.ioctl(screen_lock_fd, EVIOCGRAB, locked ? 1 : 0) != 0) { zavai.log.error("Cannot EVIOCGRAB /dev/input/event1"); Posix.close(screen_lock_fd); return; } backlight.lock_screen(); } else { Posix.close(screen_lock_fd); backlight.unlock_screen(); } screen_locked = locked; if (!locked) backlight.wiggle(); screen_lock_changed(locked); } private bool on_power_button_timeout() { last_down.tv_sec = 0; last_down.tv_usec = 0; power_long_press(); // Do not reschedule return false; } private void on_power_button(Posix.timeval* t, bool pressed) { bool short_press = false; bool long_press = false; if (pressed) { if (last_down.tv_sec == 0) { last_down = *t; button_press_timeout = Timeout.add(1000, on_power_button_timeout); } else { long diff = timediff(t, &last_down); long_press = diff >= 1000000; } } else { if (last_down.tv_sec == 0) { // Ignore: release has been simulated with the timeout } else { if (button_press_timeout != 0) { // Cancel the timeout Source.remove(button_press_timeout); button_press_timeout = 0; } long diff = timediff(t, &last_down); if (diff >= 1000000) long_press = true; else short_press = true; last_down.tv_sec = 0; last_down.tv_usec = 0; } } if (long_press) { power_long_press(); last_short_press.tv_sec = 0; last_short_press.tv_usec = 0; } if (short_press) power_short_press(t); } private void on_power_short_press(Posix.timeval* t) { long diff = timediff(t, &last_short_press); bool combo = screen_locked && (diff <= 5000000); last_short_press = *t; if (screen_locked) { // Short press: turn on backlight for a bit backlight.wiggle(); if (combo) { app.back_to_main(); app.toggle_visibility(); } } else // Short press: toggle power menu power_menu.toggle(); } private void on_power_long_press() { if (screen_locked) // Long press: unlock set_screen_lock(false); else // Long press: lock screen set_screen_lock(true); } } public class BatteryIcon : Gtk.StatusIcon { public dynamic DBus.Object battery; protected string last_status; protected int last_capacity; public BatteryIcon() { battery = zavai.registry.sbus.get_object( "org.freesmartphone.odeviced", "/org/freesmartphone/Device/PowerSupply/battery", "org.freesmartphone.Device.PowerSupply"); // activate += on_activate; battery.PowerStatus += on_power_status; battery.Capacity += on_capacity; last_status = battery.GetPowerStatus(); last_capacity = battery.GetCapacity(); update_icon(); } private void on_power_status(dynamic DBus.Object bat, string status) { zavai.log.info("New battery status: " + status); last_status = status; update_icon(); } private void on_capacity(dynamic DBus.Object bat, int val) { stderr.printf("NEW CAPACITY: %d\n", val); last_capacity = val; update_icon(); } /* private void on_activate() { } */ protected void update_icon() { string name = zavai.config.icondir + "/battery/"; if (last_status == "charging") name += "%02d0_charging_500.png".printf(last_capacity/10); else name += "%02d0.png".printf(last_capacity/10); stderr.printf("Loading icon from %s\n", name); set_from_file(name); } } public class ScreenLockButton : Gtk.Button { public ScreenLockButton() { label = "Lock screen"; clicked += on_clicked; set_size_request(0, zavai.config.min_button_height); } public void on_clicked() { zavai.log.info("Locking screen"); power.set_screen_lock(true); power_menu.hide_menu(); } } public class SuspendButton : Gtk.Button { public SuspendButton() { label = "Suspend"; clicked += on_clicked; set_size_request(0, zavai.config.min_button_height); } public void on_clicked() { zavai.log.info("Suspending the phone via FSO"); power.do_suspend(); power_menu.hide_menu(); } } public class ShutdownButton : Gtk.Button { public ShutdownButton() { label = "Shut down"; clicked += on_clicked; set_size_request(0, zavai.config.min_button_height); } public void on_clicked() { zavai.log.info("Shutting down the phone via FSO"); power.do_shutdown(); power_menu.hide_menu(); } } public class RebootButton : Gtk.Button { public RebootButton() { label = "Reboot"; clicked += on_clicked; set_size_request(0, zavai.config.min_button_height); } public void on_clicked() { zavai.log.info("Rebooting the phone via FSO"); power.do_reboot(); power_menu.hide_menu(); } } // For a list of dbus services, look in /etc/dbus-1/system.d/ public class Backlight: zavai.Service { public dynamic DBus.Object usage; public Backlight() { name = "backlight"; usage = zavai.registry.sbus.get_object( "org.freesmartphone.ousaged", "/org/freesmartphone/Usage", "org.freesmartphone.Usage"); } // Turn the backlight and then let it fade off public void wiggle() { // There must be a better method try { //display.SetBacklightPower(true); //usage.SetResourcePolicy("Display", "auto"); usage.RequestResource("Display"); usage.ReleaseResource("Display"); } catch (Error e) { zavai.log.error("Requesting/releasing resource Display: " + e.message); } } public void lock_screen() { if (!started) { try { //display.SetBacklightPower(false); zavai.app.run_script(zavai.config.xset_dpms_turn_off); /* string policy = usage.GetResourcePolicy("Display"); if (policy == "auto") { usage.SetResourcePolicy("Display", "disabled"); } */ } catch (GLib.Error e) { zavai.log.error(e.message); } } } public void unlock_screen() { try { //display.SetBacklightPower(true); zavai.app.run_script(zavai.config.xset_dpms_long_wait); //usage.SetResourcePolicy("Display", "auto"); } catch (GLib.Error e) { zavai.log.error(e.message); } } /// Request GPS resource public override void start() { if (started) return; try { usage.RequestResource("Display"); zavai.app.run_script(zavai.config.xset_dpms_always_on); zavai.log.info("Acquired display"); base.start(); } catch (GLib.Error e) { zavai.log.error(e.message); } base.start(); } // Release usage of GPS public override void stop() { if (!started) return; try { usage.ReleaseResource("Display"); zavai.app.run_script(zavai.config.xset_dpms_long_wait); zavai.log.info("Released display"); base.stop(); } catch (GLib.Error e) { zavai.log.error(e.message); } base.stop(); } } public class PowerMenu : zavai.Resource, Gtk.Window { protected Gtk.VBox vbox; protected ScreenLockButton act_screen_lock; protected SuspendButton act_suspend; protected ShutdownButton act_shutdown; protected RebootButton act_reboot; protected ServiceRequestLink act_backlight_on; protected bool shown; public PowerMenu() { type = Gtk.WindowType.TOPLEVEL; title = "Power Menu"; shown = false; destroy_with_parent = true; set_transient_for(zavai.app); set_modal(true); set_position(Gtk.WindowPosition.CENTER_ON_PARENT); set_size_request(300, 500); vbox = new Gtk.VBox(false, 0); add(vbox); //destroy += Gtk.main_quit; //set_events(get_events() | Gdk.EventMask.VISIBILITY_NOTIFY_MASK); //visibility_notify_event += on_visibility; set_skip_pager_hint(true); set_skip_taskbar_hint(true); set_type_hint(Gdk.WindowTypeHint.POPUP_MENU); act_screen_lock = new ScreenLockButton(); vbox.pack_start(act_screen_lock, false, false, 0); act_suspend = new SuspendButton(); vbox.pack_start(act_suspend, false, false, 0); act_shutdown = new ShutdownButton(); vbox.pack_start(act_shutdown, false, false, 0); act_reboot = new RebootButton(); vbox.pack_start(act_reboot, false, false, 0); act_backlight_on = new ServiceRequestLink("backlight", "Keep backlight on", "Let backlight fade"); act_backlight_on.toggled += (src) => { this.hide_menu(); }; vbox.pack_start(act_backlight_on, false, false, 0); //vbox.show_all(); } public void toggle() { if (!shown) { show_all(); show(); visible = true; present(); shown = true; } else { // TODO: do more in case it is visible but has no visibility (is covered by others) visible = !visible; if (visible) present(); } } public void hide_menu() { visible = false; } public void shutdown() {} } /* public class TogglePowerMenu : Gtk.Button { public TogglePowerMenu() { label = "Toggle power menu"; clicked += on_clicked; set_size_request(0, zavai.config.min_button_height); } public void on_clicked() { zavai.log.info("Toggling power menu"); power_menu.toggle(); } } */ Power power; PowerMenu power_menu; BatteryIcon battery_icon; Backlight backlight; //TogglePowerMenu tpm; public void init() { power = new Power(); backlight = new Backlight(); zavai.registry.register_service(backlight); battery_icon = new BatteryIcon(); battery_icon.set_visible(true); power_menu = new PowerMenu(); zavai.registry.register_resource("powermenu", power_menu); //zavai.registry.getmenu("menu.main").add_applet("menu.power"); //tpm = new TogglePowerMenu(); //zavai.registry.getmenu("menu.main").add_widget(tpm); /* raise_icon = new RaiseIcon(); raise_icon.set_visible(true); close_or_back = new CloseOrBack(); close_or_back.set_visible(true); window_list = new WindowList("Current apps"); zavai.registry.register_applet("wm.list", window_list); zavai.registry.getmenu("menu.main").add_applet("wm.list"); try { launcher = new Launcher("Run program"); } catch (Error e) { zavai.log.error("Not running launcher: " + e.message); launcher = null; } if (launcher != null) { zavai.registry.register_applet("wm.launcher", launcher); zavai.registry.getmenu("menu.main").add_applet("wm.launcher"); } */ } } } }