Merge pull request #85 from cgutman/master

Add multiple controller support and fix input device watcher issues
This commit is contained in:
Iwan Timmer
2015-02-20 14:09:28 +01:00
6 changed files with 73 additions and 14 deletions

View File

@@ -107,7 +107,7 @@
<target name="compile-common" depends="init"> <target name="compile-common" depends="init">
<!-- compile limelight --> <!-- compile limelight -->
<javac includeantruntime="false" destdir="${classes.dir}/common"> <javac includeantruntime="false" destdir="${classes.dir}/common">
<src path="${common.src.dir}"/>java <src path="${common.src.dir}"/>
<classpath> <classpath>
<fileset dir="${common.libs.dir}" includes="*.jar"/> <fileset dir="${common.libs.dir}" includes="*.jar"/>

View File

@@ -53,7 +53,9 @@ public class EvdevAbsolute {
return reverse?Short.MAX_VALUE:Short.MIN_VALUE; return reverse?Short.MAX_VALUE:Short.MIN_VALUE;
else { else {
value += value<avg?flat:-flat; value += value<avg?flat:-flat;
return (short) ((value-avg) * Short.MAX_VALUE / (reverse?flat-range:range-flat));
// Divide the value by the range before multiplying to avoid overflowing
return (short) (((value-avg) / (float)(reverse?flat-range:range-flat)) * 0x7FFE);
} }
} }

View File

@@ -1,5 +1,6 @@
package com.limelight.input; package com.limelight.input;
import com.limelight.LimeLog;
import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.NvConnection;
import com.limelight.nvstream.input.ControllerPacket; import com.limelight.nvstream.input.ControllerPacket;
import com.limelight.nvstream.input.KeyboardPacket; import com.limelight.nvstream.input.KeyboardPacket;
@@ -20,11 +21,16 @@ public class EvdevHandler extends EvdevReader {
/* GFE's prefix for every key code */ /* GFE's prefix for every key code */
public static final short KEY_PREFIX = (short) 0x80; public static final short KEY_PREFIX = (short) 0x80;
/* Global controller ID state */
private static int assignedControllerIds = 0;
private static Object controllerIdLock = new Object();
/* Gamepad state */ /* Gamepad state */
private short buttonFlags; private short buttonFlags;
private byte leftTrigger, rightTrigger; private byte leftTrigger, rightTrigger;
private short leftStickX, leftStickY, rightStickX, rightStickY; private short leftStickX, leftStickY, rightStickX, rightStickY;
private boolean gamepadSynced; private boolean gamepadSynced;
private short controllerId = -1;
private short mouseDeltaX, mouseDeltaY; private short mouseDeltaX, mouseDeltaY;
private byte mouseScroll; private byte mouseScroll;
@@ -41,9 +47,6 @@ public class EvdevHandler extends EvdevReader {
this.conn = conn; this.conn = conn;
this.mapping = mapping; this.mapping = mapping;
// We want limelight-common to scale the axis values to match Xinput values
ControllerPacket.enableAxisScaling = true;
absLX = new EvdevAbsolute(device, mapping.abs_x, mapping.reverse_x); absLX = new EvdevAbsolute(device, mapping.abs_x, mapping.reverse_x);
absLY = new EvdevAbsolute(device, mapping.abs_y, !mapping.reverse_y); absLY = new EvdevAbsolute(device, mapping.abs_y, !mapping.reverse_y);
absRX = new EvdevAbsolute(device, mapping.abs_rx, mapping.reverse_rx); absRX = new EvdevAbsolute(device, mapping.abs_rx, mapping.reverse_rx);
@@ -57,6 +60,30 @@ public class EvdevHandler extends EvdevReader {
gamepadSynced = true; gamepadSynced = true;
} }
private void assignNewControllerId() {
synchronized (controllerIdLock) {
for (short i = 0; i < 4; i++) {
if ((assignedControllerIds & (1 << i)) == 0) {
// Assign this unused value to this input device
assignedControllerIds |= (1 << i);
controllerId = i;
LimeLog.info("Assigning controller ID "+i);
return;
}
}
}
// All IDs have been assigned so use controller 0 for the rest
controllerId = 0;
}
private void releaseControllerId() {
LimeLog.info("Releasing controller ID "+controllerId);
synchronized (controllerIdLock) {
assignedControllerIds &= ~(1 << controllerId);
}
}
@Override @Override
protected void parseEvent(ByteBuffer buffer) { protected void parseEvent(ByteBuffer buffer) {
if (buffer.limit()==EvdevConstants.MAX_STRUCT_SIZE_BYTES) { if (buffer.limit()==EvdevConstants.MAX_STRUCT_SIZE_BYTES) {
@@ -73,7 +100,16 @@ public class EvdevHandler extends EvdevReader {
if (type==EvdevConstants.EV_SYN) { if (type==EvdevConstants.EV_SYN) {
if (!gamepadSynced) { if (!gamepadSynced) {
conn.sendControllerInput(buttonFlags, leftTrigger, rightTrigger, leftStickX, leftStickY, rightStickX, rightStickY); // Assign a controller ID if one hasn't been assigned yet.
// Note that we're only assigning IDs to things that actually send
// some form of controller input to avoid falsely reserving IDs for
// devices that aren't actually controllers or are inactive controllers.
if (controllerId < 0) {
assignNewControllerId();
}
conn.sendControllerInput(controllerId, buttonFlags, leftTrigger, rightTrigger,
leftStickX, leftStickY, rightStickX, rightStickY);
gamepadSynced = true; gamepadSynced = true;
} }
if (mouseDeltaX != 0 || mouseDeltaY != 0) { if (mouseDeltaX != 0 || mouseDeltaY != 0) {
@@ -222,6 +258,14 @@ public class EvdevHandler extends EvdevReader {
gamepadSynced &= !gamepadModified; gamepadSynced &= !gamepadModified;
} }
@Override
protected void deviceRemoved() {
// Release this device's controller ID (if it has one)
if (controllerId >= 0) {
releaseControllerId();
}
}
private short accountForDeadzone(short value) { private short accountForDeadzone(short value) {
return Math.abs(value) > mapping.abs_deadzone?value:0; return Math.abs(value) > mapping.abs_deadzone?value:0;
} }

View File

@@ -90,16 +90,22 @@ public class EvdevLoader implements Runnable {
WatchService watcher = evdev.getFileSystem().newWatchService(); WatchService watcher = evdev.getFileSystem().newWatchService();
evdev.register(watcher, StandardWatchEventKinds.ENTRY_CREATE); evdev.register(watcher, StandardWatchEventKinds.ENTRY_CREATE);
WatchKey watckKey = watcher.take(); for (;;) {
List<WatchEvent<?>> events = watckKey.pollEvents(); WatchKey watchKey = watcher.take();
for (WatchEvent event:events) { List<WatchEvent<?>> events = watchKey.pollEvents();
if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) { for (WatchEvent event:events) {
String name = event.context().toString(); if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
if (filter.accept(input, name)) { String name = event.context().toString();
LimeLog.info("Input " + name + " added"); if (filter.accept(input, name)) {
new EvdevHandler(conn, new File(input, name).getAbsolutePath(), mapping).start(); LimeLog.info("Input " + name + " added");
new EvdevHandler(conn, new File(input, name).getAbsolutePath(), mapping).start();
}
} }
} }
if (!watchKey.reset()) {
break;
}
} }
} catch (IOException | InterruptedException ex) { } catch (IOException | InterruptedException ex) {
LimeLog.severe(ex.getMessage()); LimeLog.severe(ex.getMessage());

View File

@@ -43,6 +43,7 @@ public abstract class EvdevReader implements Runnable {
} }
protected abstract void parseEvent(ByteBuffer buffer); protected abstract void parseEvent(ByteBuffer buffer);
protected abstract void deviceRemoved();
@Override @Override
public void run() { public void run() {
@@ -54,6 +55,7 @@ public abstract class EvdevReader implements Runnable {
} }
} catch (IOException e) { } catch (IOException e) {
LimeLog.warning("Input device removed"); LimeLog.warning("Input device removed");
deviceRemoved();
} }
} }

View File

@@ -75,6 +75,11 @@ public class GamepadMapper extends EvdevReader {
notify(); notify();
} }
} }
@Override
protected void deviceRemoved() {
// Nothing for us to do
}
public synchronized void readKey(String key, String name) throws InterruptedException { public synchronized void readKey(String key, String name) throws InterruptedException {
System.out.println(name); System.out.println(name);