mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-20 19:42:45 +00:00
Raw mouse input is working
This commit is contained in:
parent
124037ce27
commit
3e017625a9
@ -1,7 +1,7 @@
|
|||||||
package com.limelight.binding.input.evdev;
|
package com.limelight.binding.input.evdev;
|
||||||
|
|
||||||
public class EvdevEvent {
|
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;
|
public static final int EVDEV_MAX_EVENT_SIZE = 24;
|
||||||
|
|
||||||
/* Event types */
|
/* Event types */
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.limelight.binding.input.evdev;
|
package com.limelight.binding.input.evdev;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
import com.limelight.LimeLog;
|
import com.limelight.LimeLog;
|
||||||
|
|
||||||
@ -17,91 +18,86 @@ public class EvdevHandler {
|
|||||||
// but it's important that we get this right to avoid causing
|
// but it's important that we get this right to avoid causing
|
||||||
// system-wide input problems.
|
// system-wide input problems.
|
||||||
|
|
||||||
// Modify permissions to allow us access
|
// Open the /dev/input/eventX file
|
||||||
if (!EvdevReader.setPermissions(absolutePath, 0666)) {
|
int fd = EvdevReader.open(absolutePath);
|
||||||
LimeLog.warning("Unable to chmod "+absolutePath);
|
if (fd == -1) {
|
||||||
|
LimeLog.warning("Unable to open "+absolutePath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Open the /dev/input/eventX file
|
// Check if it's a mouse
|
||||||
int fd = EvdevReader.open(absolutePath);
|
if (!EvdevReader.isMouse(fd)) {
|
||||||
if (fd == -1) {
|
// We only handle mice
|
||||||
LimeLog.warning("Unable to open "+absolutePath);
|
|
||||||
return;
|
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 {
|
try {
|
||||||
// Check if it's a mouse
|
int deltaX = 0;
|
||||||
if (!EvdevReader.isMouse(fd)) {
|
int deltaY = 0;
|
||||||
// We only handle mice
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grab it for ourselves
|
while (!isInterrupted() && !shutdown) {
|
||||||
if (!EvdevReader.grab(fd)) {
|
EvdevEvent event = EvdevReader.read(fd, buffer);
|
||||||
LimeLog.warning("Unable to grab "+absolutePath);
|
if (event == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LimeLog.info("Grabbed device for raw mouse input: "+absolutePath);
|
switch (event.type)
|
||||||
|
{
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(EvdevEvent.EVDEV_MAX_EVENT_SIZE);
|
case EvdevEvent.EV_SYN:
|
||||||
|
if (deltaX != 0 || deltaY != 0) {
|
||||||
try {
|
listener.mouseMove(deltaX, deltaY);
|
||||||
while (!isInterrupted() && !shutdown) {
|
deltaX = deltaY = 0;
|
||||||
EvdevEvent event = EvdevReader.read(fd, buffer);
|
|
||||||
if (event == null) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
switch (event.type)
|
case EvdevEvent.EV_REL:
|
||||||
|
switch (event.code)
|
||||||
{
|
{
|
||||||
case EvdevEvent.EV_SYN:
|
case EvdevEvent.REL_X:
|
||||||
// Do nothing
|
deltaX = event.value;
|
||||||
break;
|
break;
|
||||||
|
case EvdevEvent.REL_Y:
|
||||||
case EvdevEvent.EV_REL:
|
deltaY = event.value;
|
||||||
switch (event.code)
|
|
||||||
{
|
|
||||||
case EvdevEvent.REL_X:
|
|
||||||
listener.mouseMove(event.value, 0);
|
|
||||||
break;
|
|
||||||
case EvdevEvent.REL_Y:
|
|
||||||
listener.mouseMove(0, event.value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case EvdevEvent.EV_KEY:
|
case EvdevEvent.EV_KEY:
|
||||||
switch (event.code)
|
switch (event.code)
|
||||||
{
|
{
|
||||||
case EvdevEvent.BTN_LEFT:
|
case EvdevEvent.BTN_LEFT:
|
||||||
listener.mouseButtonEvent(EvdevListener.BUTTON_LEFT,
|
listener.mouseButtonEvent(EvdevListener.BUTTON_LEFT,
|
||||||
event.value != 0);
|
event.value != 0);
|
||||||
break;
|
break;
|
||||||
case EvdevEvent.BTN_MIDDLE:
|
case EvdevEvent.BTN_MIDDLE:
|
||||||
listener.mouseButtonEvent(EvdevListener.BUTTON_MIDDLE,
|
listener.mouseButtonEvent(EvdevListener.BUTTON_MIDDLE,
|
||||||
event.value != 0);
|
event.value != 0);
|
||||||
break;
|
break;
|
||||||
case EvdevEvent.BTN_RIGHT:
|
case EvdevEvent.BTN_RIGHT:
|
||||||
listener.mouseButtonEvent(EvdevListener.BUTTON_RIGHT,
|
listener.mouseButtonEvent(EvdevListener.BUTTON_RIGHT,
|
||||||
event.value != 0);
|
event.value != 0);
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
// Release our grab
|
|
||||||
EvdevReader.ungrab(fd);
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
// Close the file
|
// Release our grab
|
||||||
EvdevReader.close(fd);
|
EvdevReader.ungrab(fd);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
// Set permissions back
|
// Close the file
|
||||||
EvdevReader.setPermissions(absolutePath, 0066);
|
EvdevReader.close(fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.limelight.binding.input.evdev;
|
package com.limelight.binding.input.evdev;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import com.limelight.LimeLog;
|
import com.limelight.LimeLog;
|
||||||
@ -10,7 +12,29 @@ public class EvdevReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Requires root to chmod /dev/input/eventX
|
// 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
|
// Returns the fd to be passed to other function or -1 on error
|
||||||
public static native int open(String fileName);
|
public static native int open(String fileName);
|
||||||
|
@ -13,7 +13,9 @@ public class EvdevWatcher {
|
|||||||
|
|
||||||
private HashMap<String, EvdevHandler> handlers = new HashMap<String, EvdevHandler>();
|
private HashMap<String, EvdevHandler> handlers = new HashMap<String, EvdevHandler>();
|
||||||
private boolean shutdown = false;
|
private boolean shutdown = false;
|
||||||
|
private boolean init = false;
|
||||||
private EvdevListener listener;
|
private EvdevListener listener;
|
||||||
|
private Thread startThread;
|
||||||
|
|
||||||
private FileObserver observer = new FileObserver(PATH, FileObserver.CREATE | FileObserver.DELETE) {
|
private FileObserver observer = new FileObserver(PATH, FileObserver.CREATE | FileObserver.DELETE) {
|
||||||
@Override
|
@Override
|
||||||
@ -34,6 +36,11 @@ public class EvdevWatcher {
|
|||||||
if ((event & FileObserver.CREATE) != 0) {
|
if ((event & FileObserver.CREATE) != 0) {
|
||||||
LimeLog.info("Starting evdev handler for "+fileName);
|
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);
|
EvdevHandler handler = new EvdevHandler(PATH + "/" + fileName, listener);
|
||||||
handler.start();
|
handler.start();
|
||||||
|
|
||||||
@ -43,7 +50,7 @@ public class EvdevWatcher {
|
|||||||
if ((event & FileObserver.DELETE) != 0) {
|
if ((event & FileObserver.DELETE) != 0) {
|
||||||
LimeLog.info("Halting evdev handler for "+fileName);
|
LimeLog.info("Halting evdev handler for "+fileName);
|
||||||
|
|
||||||
EvdevHandler handler = handlers.get(fileName);
|
EvdevHandler handler = handlers.remove(fileName);
|
||||||
if (handler != null) {
|
if (handler != null) {
|
||||||
handler.notifyDeleted();
|
handler.notifyDeleted();
|
||||||
}
|
}
|
||||||
@ -57,18 +64,46 @@ public class EvdevWatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
// Start watching for new files
|
startThread = new Thread() {
|
||||||
observer.startWatching();
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Get permissions to read the eventX files
|
||||||
|
EvdevReader.setPermissions(0666);
|
||||||
|
init = true;
|
||||||
|
|
||||||
// Rundown existing files and generate synthetic events
|
// Rundown existing files and generate synthetic events
|
||||||
File devInputDir = new File(PATH);
|
File devInputDir = new File(PATH);
|
||||||
File[] files = devInputDir.listFiles();
|
File[] files = devInputDir.listFiles();
|
||||||
for (File f : files) {
|
for (File f : files) {
|
||||||
observer.onEvent(FileObserver.CREATE, f.getName());
|
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() {
|
public void shutdown() {
|
||||||
|
// Let start thread cleanup on it's own sweet time
|
||||||
|
synchronized (startThread) {
|
||||||
|
startThread.notify();
|
||||||
|
}
|
||||||
|
|
||||||
// Stop the observer
|
// Stop the observer
|
||||||
observer.stopWatching();
|
observer.stopWatching();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user