From 3e017625a9076976a01ff714969dec862208de6a Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Tue, 2 Sep 2014 00:41:33 -0700 Subject: [PATCH] Raw mouse input is working --- .../binding/input/evdev/EvdevEvent.java | 2 +- .../binding/input/evdev/EvdevHandler.java | 134 +++++++++--------- .../binding/input/evdev/EvdevReader.java | 26 +++- .../binding/input/evdev/EvdevWatcher.java | 55 +++++-- 4 files changed, 136 insertions(+), 81 deletions(-) diff --git a/src/com/limelight/binding/input/evdev/EvdevEvent.java b/src/com/limelight/binding/input/evdev/EvdevEvent.java index a01bedcd..7cae1f7e 100644 --- a/src/com/limelight/binding/input/evdev/EvdevEvent.java +++ b/src/com/limelight/binding/input/evdev/EvdevEvent.java @@ -1,7 +1,7 @@ package com.limelight.binding.input.evdev; public class EvdevEvent { - public static final int EVDEV_MIN_EVENT_SIZE = 18; + public static final int EVDEV_MIN_EVENT_SIZE = 16; public static final int EVDEV_MAX_EVENT_SIZE = 24; /* Event types */ diff --git a/src/com/limelight/binding/input/evdev/EvdevHandler.java b/src/com/limelight/binding/input/evdev/EvdevHandler.java index 3e75f0cf..a7a291e2 100644 --- a/src/com/limelight/binding/input/evdev/EvdevHandler.java +++ b/src/com/limelight/binding/input/evdev/EvdevHandler.java @@ -1,6 +1,7 @@ package com.limelight.binding.input.evdev; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import com.limelight.LimeLog; @@ -17,91 +18,86 @@ public class EvdevHandler { // but it's important that we get this right to avoid causing // system-wide input problems. - // Modify permissions to allow us access - if (!EvdevReader.setPermissions(absolutePath, 0666)) { - LimeLog.warning("Unable to chmod "+absolutePath); + // Open the /dev/input/eventX file + int fd = EvdevReader.open(absolutePath); + if (fd == -1) { + LimeLog.warning("Unable to open "+absolutePath); return; } - + try { - // Open the /dev/input/eventX file - int fd = EvdevReader.open(absolutePath); - if (fd == -1) { - LimeLog.warning("Unable to open "+absolutePath); + // Check if it's a mouse + if (!EvdevReader.isMouse(fd)) { + // We only handle mice return; } - + + // Grab it for ourselves + if (!EvdevReader.grab(fd)) { + LimeLog.warning("Unable to grab "+absolutePath); + return; + } + + LimeLog.info("Grabbed device for raw mouse input: "+absolutePath); + + ByteBuffer buffer = ByteBuffer.allocate(EvdevEvent.EVDEV_MAX_EVENT_SIZE).order(ByteOrder.nativeOrder()); + try { - // Check if it's a mouse - if (!EvdevReader.isMouse(fd)) { - // We only handle mice - return; - } - - // Grab it for ourselves - if (!EvdevReader.grab(fd)) { - LimeLog.warning("Unable to grab "+absolutePath); - return; - } - - LimeLog.info("Grabbed device for raw mouse input: "+absolutePath); - - ByteBuffer buffer = ByteBuffer.allocate(EvdevEvent.EVDEV_MAX_EVENT_SIZE); - - try { - while (!isInterrupted() && !shutdown) { - EvdevEvent event = EvdevReader.read(fd, buffer); - if (event == null) { - return; + int deltaX = 0; + int deltaY = 0; + + while (!isInterrupted() && !shutdown) { + EvdevEvent event = EvdevReader.read(fd, buffer); + if (event == null) { + return; + } + + switch (event.type) + { + case EvdevEvent.EV_SYN: + if (deltaX != 0 || deltaY != 0) { + listener.mouseMove(deltaX, deltaY); + deltaX = deltaY = 0; } - - switch (event.type) + break; + + case EvdevEvent.EV_REL: + switch (event.code) { - case EvdevEvent.EV_SYN: - // Do nothing + case EvdevEvent.REL_X: + deltaX = event.value; break; - - case EvdevEvent.EV_REL: - switch (event.code) - { - case EvdevEvent.REL_X: - listener.mouseMove(event.value, 0); - break; - case EvdevEvent.REL_Y: - listener.mouseMove(0, event.value); - break; - } + case EvdevEvent.REL_Y: + deltaY = event.value; + break; + } + break; + + case EvdevEvent.EV_KEY: + switch (event.code) + { + case EvdevEvent.BTN_LEFT: + listener.mouseButtonEvent(EvdevListener.BUTTON_LEFT, + event.value != 0); + break; + case EvdevEvent.BTN_MIDDLE: + listener.mouseButtonEvent(EvdevListener.BUTTON_MIDDLE, + event.value != 0); + break; + case EvdevEvent.BTN_RIGHT: + listener.mouseButtonEvent(EvdevListener.BUTTON_RIGHT, + event.value != 0); break; - - case EvdevEvent.EV_KEY: - switch (event.code) - { - case EvdevEvent.BTN_LEFT: - listener.mouseButtonEvent(EvdevListener.BUTTON_LEFT, - event.value != 0); - break; - case EvdevEvent.BTN_MIDDLE: - listener.mouseButtonEvent(EvdevListener.BUTTON_MIDDLE, - event.value != 0); - break; - case EvdevEvent.BTN_RIGHT: - listener.mouseButtonEvent(EvdevListener.BUTTON_RIGHT, - event.value != 0); - break; - } } } - } finally { - // Release our grab - EvdevReader.ungrab(fd); } } finally { - // Close the file - EvdevReader.close(fd); + // Release our grab + EvdevReader.ungrab(fd); } } finally { - // Set permissions back - EvdevReader.setPermissions(absolutePath, 0066); + // Close the file + EvdevReader.close(fd); } } }; diff --git a/src/com/limelight/binding/input/evdev/EvdevReader.java b/src/com/limelight/binding/input/evdev/EvdevReader.java index 681818cf..2398ce52 100644 --- a/src/com/limelight/binding/input/evdev/EvdevReader.java +++ b/src/com/limelight/binding/input/evdev/EvdevReader.java @@ -1,5 +1,7 @@ package com.limelight.binding.input.evdev; +import java.io.IOException; +import java.io.OutputStream; import java.nio.ByteBuffer; import com.limelight.LimeLog; @@ -10,7 +12,29 @@ public class EvdevReader { } // Requires root to chmod /dev/input/eventX - public static native boolean setPermissions(String fileName, int octalPermissions); + public static boolean setPermissions(int octalPermissions) { + String chmodString = String.format("chmod %o /dev/input/*\n", octalPermissions); + + ProcessBuilder builder = new ProcessBuilder("su"); + + try { + Process p = builder.start(); + + OutputStream stdin = p.getOutputStream(); + stdin.write(chmodString.getBytes("UTF-8")); + stdin.write("exit\n".getBytes("UTF-8")); + stdin.flush(); + + p.waitFor(); + p.destroy(); + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + return false; + } // Returns the fd to be passed to other function or -1 on error public static native int open(String fileName); diff --git a/src/com/limelight/binding/input/evdev/EvdevWatcher.java b/src/com/limelight/binding/input/evdev/EvdevWatcher.java index 90c05d35..ea5ef701 100644 --- a/src/com/limelight/binding/input/evdev/EvdevWatcher.java +++ b/src/com/limelight/binding/input/evdev/EvdevWatcher.java @@ -13,7 +13,9 @@ public class EvdevWatcher { private HashMap handlers = new HashMap(); private boolean shutdown = false; + private boolean init = false; private EvdevListener listener; + private Thread startThread; private FileObserver observer = new FileObserver(PATH, FileObserver.CREATE | FileObserver.DELETE) { @Override @@ -34,6 +36,11 @@ public class EvdevWatcher { if ((event & FileObserver.CREATE) != 0) { LimeLog.info("Starting evdev handler for "+fileName); + if (!init) { + // If this a real new device, update permissions again so we can read it + EvdevReader.setPermissions(0666); + } + EvdevHandler handler = new EvdevHandler(PATH + "/" + fileName, listener); handler.start(); @@ -43,7 +50,7 @@ public class EvdevWatcher { if ((event & FileObserver.DELETE) != 0) { LimeLog.info("Halting evdev handler for "+fileName); - EvdevHandler handler = handlers.get(fileName); + EvdevHandler handler = handlers.remove(fileName); if (handler != null) { handler.notifyDeleted(); } @@ -57,18 +64,46 @@ public class EvdevWatcher { } public void start() { - // Start watching for new files - observer.startWatching(); - - // Rundown existing files and generate synthetic events - File devInputDir = new File(PATH); - File[] files = devInputDir.listFiles(); - for (File f : files) { - observer.onEvent(FileObserver.CREATE, f.getName()); - } + startThread = new Thread() { + @Override + public void run() { + // Get permissions to read the eventX files + EvdevReader.setPermissions(0666); + init = true; + + // Rundown existing files and generate synthetic events + File devInputDir = new File(PATH); + File[] files = devInputDir.listFiles(); + for (File f : files) { + observer.onEvent(FileObserver.CREATE, f.getName()); + } + + // Done with initial onEvent calls + init = false; + + // Start watching for new files + observer.startWatching(); + + synchronized (startThread) { + // Wait to be awoken again by shutdown() + try { + startThread.wait(); + } catch (InterruptedException e) {} + } + + // Giveup eventX permissions + EvdevReader.setPermissions(0066); + } + }; + startThread.start(); } public void shutdown() { + // Let start thread cleanup on it's own sweet time + synchronized (startThread) { + startThread.notify(); + } + // Stop the observer observer.stopWatching();