]> ToastFreeware Gitweb - gregoa/zavai.git/blobdiff - src/input.vala
Merge branch 'master' into gregoa
[gregoa/zavai.git] / src / input.vala
index 882e69d8b599ac570aee2dcf11604df1893f47fd..ffa0add9fad3fffc5f9bce4216c0956e6417f0fd 100644 (file)
 namespace zavai {
 namespace input {
 
-public abstract class DevInput : zavai.Service
+public class DevInput : zavai.Service
 {
-       public string device { get; construct; }
+    public string device { get; construct; }
 
-       public signal bool event(LinuxInput.Event* ev);
+    public signal bool event(LinuxInput.Event* ev);
 
     protected IOChannel fd = null;
     protected uint fd_watch = 0;
 
+    public DevInput(string name, string device)
+    {
+        Object(name: "input.power_button", device: "/dev/input/event0");
+    }
+
     protected void close_fd()
     {
         if (fd != null)
@@ -46,16 +51,21 @@ public abstract class DevInput : zavai.Service
 
     protected bool on_input_data(IOChannel source, IOCondition condition)
     {
-               if (condition != IOCondition.IN) return true;
+        if (condition != IOCondition.IN) return true;
 
-        stderr.printf("GOT INPUT ON %s %d\n", device, source.unix_get_fd());
+        //stderr.printf("GOT INPUT ON %s %d\n", device, source.unix_get_fd());
         char[] buf = new char[sizeof(LinuxInput.Event)];
-               size_t count_read;
-               source.read_chars(buf, out count_read);
-        stderr.printf("READ %zu chars\n", count_read);
+        size_t count_read;
+        try {
+            source.read_chars(buf, out count_read);
+        } catch (Error e) {
+            zavai.log.error("Reading from " + device + ": " + e.message);
+            return true;
+        }
+        //stderr.printf("READ %zu chars\n", count_read);
 
-               LinuxInput.Event* ie = (LinuxInput.Event*)buf;
-        stderr.printf("INPUT EVENT time %lu.%lu type %hu code %hu val %d\n", (ulong)ie->time.tv_sec, ie->time.tv_usec, ie->type, ie->code, ie->val);
+        LinuxInput.Event* ie = (LinuxInput.Event*)buf;
+        //stderr.printf("INPUT EVENT time %lu.%lu type %hu code %hu val %d\n", (ulong)ie->time.tv_sec, ie->time.tv_usec, ie->type, ie->code, ie->val);
 
         /*
         ts1, ts2, type, code, value = struct.unpack("LLHHI", buf)
@@ -81,28 +91,32 @@ public abstract class DevInput : zavai.Service
         return event(ie);
     }
 
-       /// Start reading from the device
-       public override void start()
-       {
-               if (started) return;
+    /// Start reading from the device
+    public override void start()
+    {
+        if (started) return;
 
         if (fd != null)
             close_fd();
 
         // Open the device and listed to it using the GObject main loop
-               zavai.log.info("Opening device " + device);
+        zavai.log.info("Opening device " + device);
         fd = new IOChannel.file(device, "r");
-               fd.set_encoding(null);
-               fd.set_buffered(false);
+        try {
+            fd.set_encoding(null);
+        } catch (Error e) {
+            zavai.log.error("Setting encoding to null on " + device + ": " + e.message);
+        }
+        fd.set_buffered(false);
         fd_watch = fd.add_watch(IOCondition.IN, on_input_data);
 
-               base.start();
-       }
+        base.start();
+    }
 
-       // Stop reading from the device
-       public override void stop()
-       {
-               if (!started) return;
+    // Stop reading from the device
+    public override void stop()
+    {
+        if (!started) return;
 
         if (fd != null)
         {
@@ -110,36 +124,152 @@ public abstract class DevInput : zavai.Service
             close_fd();
         }
 
-               base.stop();
-       }
+        base.stop();
+    }
+}
+
+public class HotKeys : zavai.Service
+{
+    protected List<int> grabbed;
+    public signal bool hotkey(uint keycode, ulong time, bool pressed);
+
+    public HotKeys()
+    {
+        Object(name: "input.hotkeys");
+
+        grabbed = new List<int>();
+    }
+
+    // Hotkey handlink takes inspiration from
+    // http://old.nabble.com/-PATCH--Initial-non-working-implementation-of-wxWindow::%28Un%29RegisterHotKey-on-wxGTK-td14557263.html
+    private static Gdk.FilterReturn on_hotkey(Gdk.XEvent* xevent, Gdk.Event? event, void* data)
+    {
+        // Global events don't get their data translated to gdk, as there is no
+        // GdkWindow to work with, therefore we need to use the xevent, because
+        // GdkEvent* is always GDK_NOTHING
+        X.Event* xev = (X.Event*)xevent;
+
+        switch (xev->type)
+        {
+            case X.EventType.KeyPress:
+                return zavai.input.hotkeys.my_on_hotkey(xev, true);
+            case X.EventType.KeyRelease:
+                return zavai.input.hotkeys.my_on_hotkey(xev, false);
+            default:
+                return Gdk.FilterReturn.CONTINUE;
+        }
+    }
+
+    //public Gdk.FilterReturn on_hotkey(Gdk.XEvent xevent, Gdk.Event? event)
+    private Gdk.FilterReturn my_on_hotkey(X.Event* xev, bool pressed)
+    {
+        // From http://tronche.com/gui/x/xlib/input/pointer-grabbing.html:
+        //
+        // A timestamp is a time value, expressed in milliseconds. It typically is the
+        // time since the last server reset. Timestamp values wrap around (after about
+        // 49.7 days). The server, given its current time is represented by timestamp
+        // T, always interprets timestamps from clients by treating half of the
+        // timestamp space as being later in time than T. One timestamp value, named
+        // CurrentTime, is never generated by the server. This value is reserved for
+        // use in requests to represent the current server time. 
+
+        if (grabbed.index((int)xev->xkey.keycode) == -1)
+            return Gdk.FilterReturn.CONTINUE;
+
+        if (hotkey(xev->xkey.keycode, xev->xkey.time, pressed))
+            return Gdk.FilterReturn.REMOVE;
+        return Gdk.FilterReturn.CONTINUE;
+    }
+
+    public void grab(int keycode, int modifiers, bool owner_events)
+    {
+        // We need to grab the keys we want to listen to
+        int res = Gdk.x11_get_default_xdisplay().grab_key(keycode, modifiers, Gdk.x11_get_default_root_xwindow(), owner_events, X.GrabMode.Async, X.GrabMode.Async);
+        if (res != 0)
+            stderr.printf("Grab result: %d\n", res); // We get BadRequest and don't know why
+        grabbed.append(keycode);
+    }
+
+    /// Start reading from the device
+    public override void start()
+    {
+        if (started) return;
+
+        //gdk_window_add_filter (NULL, _wxgtk_global_hotkey_callback, this);
+        ((Gdk.Window*)null)->add_filter((Gdk.FilterFunc)on_hotkey);
+
+        grab(160, 0, false);
+
+        base.start();
+    }
+
+    // Stop reading from the device
+    public override void stop()
+    {
+        if (!started) return;
+
+        //gdk_window_remove_filter(NULL, _wxgtk_global_hotkey_callback, this); 
+        ((Gdk.Window*)null)->remove_filter((Gdk.FilterFunc)on_hotkey);
+
+        base.stop();
+    }
 }
 
-public class PowerButton : DevInput
+public class PowerButton : zavai.Service
 {
-       public signal void power_button(Posix.timeval* time, bool pressed);
+    protected DevInput devinput;
+
+    public signal void power_button(Posix.timeval* time, bool pressed);
 
     public PowerButton()
     {
-        name = "input.power_button";
         // FIXME: change to event0 for the power button
         // FIXME: change to event4 for the aux button and headset button
-        //device = "/dev/input/event1";
-        device = "/dev/input/event0";
+        string inputdev = "/dev/input/event0";
+        if (Posix.access(inputdev, Posix.R_OK) == 0)
+        {
+            zavai.log.info("Handle power button via " + inputdev);
+            // Listen via input device
+            devinput = new DevInput("input.power_button", "/dev/input/event0");
+            devinput.event += on_event;
+            devinput.request("powerbutton");
+        } else {
+            zavai.log.info("Handle power button via XGrabKey on keycode " + zavai.config.power_button_keycode.to_string());
+            // Listen via X
+            hotkeys.hotkey += on_hotkey;
+            hotkeys.grab(zavai.config.power_button_keycode, 0, false);
+            hotkeys.request("powerbutton");
+        }
+    }
 
-               event += on_event;
+    protected bool on_event(LinuxInput.Event* ev)
+    {
+        if (ev->type == LinuxInput.Type.KEY && 
+            ev->code == LinuxInput.Key.POWER)
+        {
+            power_button(&(ev->time), ev->val == 0 ? false : true);
+        }
+        return true;
     }
 
-       protected bool on_event(LinuxInput.Event* ev)
-       {
-               if (ev->type == LinuxInput.Type.KEY && 
-                       ev->code == LinuxInput.Key.POWER)
-               {
-                       power_button(&(ev->time), ev->val == 0 ? false : true);
-               }
-               return true;
-       }
+    protected bool on_hotkey(uint keycode, ulong time, bool pressed)
+    {
+        if (keycode == zavai.config.power_button_keycode)
+        {
+            // Convert X time to a fake timeval
+            // TODO: handle wraparound
+            Posix.timeval tv = {
+                (time_t)(time / 1000),
+                (long)((time % 1000) * 1000)
+            };
+            power_button(&tv, pressed);
+            return true;
+        }
+        return false;
+    }
 }
 
+
 /*
 # TODO:
 #  - hook into the headset plugged/unplugged event
@@ -282,13 +412,13 @@ class Audio:
 */
 
 
+public HotKeys hotkeys = null;
 public PowerButton power_button = null;
 
 public void init()
 {
-       power_button = new PowerButton();
-
-       zavai.registry.register_service(power_button);
+    hotkeys = new HotKeys();
+    power_button = new PowerButton();
 }
 
 }