mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2026-04-06 07:56:07 +00:00
Tabs -> Spaces
This commit is contained in:
@@ -17,23 +17,23 @@ import com.limelight.utils.Vector2d;
|
||||
|
||||
public class ControllerHandler implements InputManager.InputDeviceListener {
|
||||
|
||||
private static final int MAXIMUM_BUMPER_UP_DELAY_MS = 100;
|
||||
private static final int MAXIMUM_BUMPER_UP_DELAY_MS = 100;
|
||||
|
||||
private static final int START_DOWN_TIME_KEYB_MS = 750;
|
||||
|
||||
private static final int MINIMUM_BUTTON_DOWN_TIME_MS = 25;
|
||||
|
||||
private static final int EMULATING_SPECIAL = 0x1;
|
||||
private static final int EMULATING_SELECT = 0x2;
|
||||
|
||||
private static final int EMULATED_SPECIAL_UP_DELAY_MS = 100;
|
||||
private static final int EMULATED_SELECT_UP_DELAY_MS = 30;
|
||||
|
||||
private final Vector2d inputVector = new Vector2d();
|
||||
|
||||
private final HashMap<String, ControllerContext> contexts = new HashMap<String, ControllerContext>();
|
||||
|
||||
private final NvConnection conn;
|
||||
|
||||
private static final int MINIMUM_BUTTON_DOWN_TIME_MS = 25;
|
||||
|
||||
private static final int EMULATING_SPECIAL = 0x1;
|
||||
private static final int EMULATING_SELECT = 0x2;
|
||||
|
||||
private static final int EMULATED_SPECIAL_UP_DELAY_MS = 100;
|
||||
private static final int EMULATED_SELECT_UP_DELAY_MS = 30;
|
||||
|
||||
private final Vector2d inputVector = new Vector2d();
|
||||
|
||||
private final HashMap<String, ControllerContext> contexts = new HashMap<String, ControllerContext>();
|
||||
|
||||
private final NvConnection conn;
|
||||
private final double stickDeadzone;
|
||||
private final ControllerContext defaultContext = new ControllerContext();
|
||||
private final GameGestures gestures;
|
||||
@@ -41,9 +41,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
|
||||
|
||||
private final boolean multiControllerEnabled;
|
||||
private short currentControllers;
|
||||
|
||||
public ControllerHandler(NvConnection conn, GameGestures gestures, boolean multiControllerEnabled, int deadzonePercentage) {
|
||||
this.conn = conn;
|
||||
|
||||
public ControllerHandler(NvConnection conn, GameGestures gestures, boolean multiControllerEnabled, int deadzonePercentage) {
|
||||
this.conn = conn;
|
||||
this.gestures = gestures;
|
||||
this.multiControllerEnabled = multiControllerEnabled;
|
||||
|
||||
@@ -82,20 +82,20 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
|
||||
defaultContext.leftTriggerAxis = MotionEvent.AXIS_BRAKE;
|
||||
defaultContext.rightTriggerAxis = MotionEvent.AXIS_GAS;
|
||||
defaultContext.controllerNumber = (short) 0;
|
||||
}
|
||||
|
||||
private static InputDevice.MotionRange getMotionRangeForJoystickAxis(InputDevice dev, int axis) {
|
||||
InputDevice.MotionRange range;
|
||||
|
||||
// First get the axis for SOURCE_JOYSTICK
|
||||
range = dev.getMotionRange(axis, InputDevice.SOURCE_JOYSTICK);
|
||||
if (range == null) {
|
||||
// Now try the axis for SOURCE_GAMEPAD
|
||||
range = dev.getMotionRange(axis, InputDevice.SOURCE_GAMEPAD);
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
}
|
||||
|
||||
private static InputDevice.MotionRange getMotionRangeForJoystickAxis(InputDevice dev, int axis) {
|
||||
InputDevice.MotionRange range;
|
||||
|
||||
// First get the axis for SOURCE_JOYSTICK
|
||||
range = dev.getMotionRange(axis, InputDevice.SOURCE_JOYSTICK);
|
||||
if (range == null) {
|
||||
// Now try the axis for SOURCE_GAMEPAD
|
||||
range = dev.getMotionRange(axis, InputDevice.SOURCE_GAMEPAD);
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
private short assignNewControllerNumber() {
|
||||
for (short i = 0; i < 4; i++) {
|
||||
@@ -137,9 +137,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
|
||||
LimeLog.info("Controller number "+controllerNumber+" is now available");
|
||||
currentControllers &= ~(1 << controllerNumber);
|
||||
}
|
||||
|
||||
private ControllerContext createContextForDevice(InputDevice dev) {
|
||||
ControllerContext context = new ControllerContext();
|
||||
|
||||
private ControllerContext createContextForDevice(InputDevice dev) {
|
||||
ControllerContext context = new ControllerContext();
|
||||
String devName = dev.getName();
|
||||
|
||||
LimeLog.info("Creating controller context for device: "+devName);
|
||||
@@ -148,91 +148,91 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
|
||||
context.id = dev.getId();
|
||||
|
||||
context.leftStickXAxis = MotionEvent.AXIS_X;
|
||||
context.leftStickYAxis = MotionEvent.AXIS_Y;
|
||||
context.leftStickYAxis = MotionEvent.AXIS_Y;
|
||||
if (getMotionRangeForJoystickAxis(dev, context.leftStickXAxis) != null &&
|
||||
getMotionRangeForJoystickAxis(dev, context.leftStickYAxis) != null) {
|
||||
// This is a gamepad
|
||||
hasGameController = true;
|
||||
context.hasJoystickAxes = true;
|
||||
}
|
||||
|
||||
InputDevice.MotionRange leftTriggerRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_LTRIGGER);
|
||||
InputDevice.MotionRange rightTriggerRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RTRIGGER);
|
||||
InputDevice.MotionRange brakeRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_BRAKE);
|
||||
InputDevice.MotionRange gasRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_GAS);
|
||||
if (leftTriggerRange != null && rightTriggerRange != null)
|
||||
{
|
||||
// Some controllers use LTRIGGER and RTRIGGER (like Ouya)
|
||||
context.leftTriggerAxis = MotionEvent.AXIS_LTRIGGER;
|
||||
context.rightTriggerAxis = MotionEvent.AXIS_RTRIGGER;
|
||||
}
|
||||
else if (brakeRange != null && gasRange != null)
|
||||
{
|
||||
// Others use GAS and BRAKE (like Moga)
|
||||
context.leftTriggerAxis = MotionEvent.AXIS_BRAKE;
|
||||
context.rightTriggerAxis = MotionEvent.AXIS_GAS;
|
||||
}
|
||||
else
|
||||
{
|
||||
InputDevice.MotionRange rxRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RX);
|
||||
InputDevice.MotionRange ryRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RY);
|
||||
if (rxRange != null && ryRange != null && devName != null) {
|
||||
if (devName.contains("Xbox") || devName.contains("XBox") || devName.contains("X-Box")) {
|
||||
// Xbox controllers use RX and RY for right stick
|
||||
context.rightStickXAxis = MotionEvent.AXIS_RX;
|
||||
context.rightStickYAxis = MotionEvent.AXIS_RY;
|
||||
|
||||
// Xbox controllers use Z and RZ for triggers
|
||||
context.leftTriggerAxis = MotionEvent.AXIS_Z;
|
||||
context.rightTriggerAxis = MotionEvent.AXIS_RZ;
|
||||
context.triggersIdleNegative = true;
|
||||
context.isXboxController = true;
|
||||
}
|
||||
else {
|
||||
// DS4 controller uses RX and RY for triggers
|
||||
context.leftTriggerAxis = MotionEvent.AXIS_RX;
|
||||
context.rightTriggerAxis = MotionEvent.AXIS_RY;
|
||||
context.triggersIdleNegative = true;
|
||||
|
||||
context.isDualShock4 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (context.rightStickXAxis == -1 && context.rightStickYAxis == -1) {
|
||||
InputDevice.MotionRange zRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_Z);
|
||||
InputDevice.MotionRange rzRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RZ);
|
||||
|
||||
// Most other controllers use Z and RZ for the right stick
|
||||
if (zRange != null && rzRange != null) {
|
||||
context.rightStickXAxis = MotionEvent.AXIS_Z;
|
||||
context.rightStickYAxis = MotionEvent.AXIS_RZ;
|
||||
}
|
||||
else {
|
||||
InputDevice.MotionRange rxRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RX);
|
||||
InputDevice.MotionRange ryRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RY);
|
||||
|
||||
// Try RX and RY now
|
||||
if (rxRange != null && ryRange != null) {
|
||||
context.rightStickXAxis = MotionEvent.AXIS_RX;
|
||||
context.rightStickYAxis = MotionEvent.AXIS_RY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Some devices have "hats" for d-pads
|
||||
InputDevice.MotionRange hatXRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_HAT_X);
|
||||
InputDevice.MotionRange hatYRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_HAT_Y);
|
||||
if (hatXRange != null && hatYRange != null) {
|
||||
context.hatXAxis = MotionEvent.AXIS_HAT_X;
|
||||
context.hatYAxis = MotionEvent.AXIS_HAT_Y;
|
||||
}
|
||||
|
||||
if (context.leftStickXAxis != -1 && context.leftStickYAxis != -1) {
|
||||
|
||||
InputDevice.MotionRange leftTriggerRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_LTRIGGER);
|
||||
InputDevice.MotionRange rightTriggerRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RTRIGGER);
|
||||
InputDevice.MotionRange brakeRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_BRAKE);
|
||||
InputDevice.MotionRange gasRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_GAS);
|
||||
if (leftTriggerRange != null && rightTriggerRange != null)
|
||||
{
|
||||
// Some controllers use LTRIGGER and RTRIGGER (like Ouya)
|
||||
context.leftTriggerAxis = MotionEvent.AXIS_LTRIGGER;
|
||||
context.rightTriggerAxis = MotionEvent.AXIS_RTRIGGER;
|
||||
}
|
||||
else if (brakeRange != null && gasRange != null)
|
||||
{
|
||||
// Others use GAS and BRAKE (like Moga)
|
||||
context.leftTriggerAxis = MotionEvent.AXIS_BRAKE;
|
||||
context.rightTriggerAxis = MotionEvent.AXIS_GAS;
|
||||
}
|
||||
else
|
||||
{
|
||||
InputDevice.MotionRange rxRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RX);
|
||||
InputDevice.MotionRange ryRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RY);
|
||||
if (rxRange != null && ryRange != null && devName != null) {
|
||||
if (devName.contains("Xbox") || devName.contains("XBox") || devName.contains("X-Box")) {
|
||||
// Xbox controllers use RX and RY for right stick
|
||||
context.rightStickXAxis = MotionEvent.AXIS_RX;
|
||||
context.rightStickYAxis = MotionEvent.AXIS_RY;
|
||||
|
||||
// Xbox controllers use Z and RZ for triggers
|
||||
context.leftTriggerAxis = MotionEvent.AXIS_Z;
|
||||
context.rightTriggerAxis = MotionEvent.AXIS_RZ;
|
||||
context.triggersIdleNegative = true;
|
||||
context.isXboxController = true;
|
||||
}
|
||||
else {
|
||||
// DS4 controller uses RX and RY for triggers
|
||||
context.leftTriggerAxis = MotionEvent.AXIS_RX;
|
||||
context.rightTriggerAxis = MotionEvent.AXIS_RY;
|
||||
context.triggersIdleNegative = true;
|
||||
|
||||
context.isDualShock4 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (context.rightStickXAxis == -1 && context.rightStickYAxis == -1) {
|
||||
InputDevice.MotionRange zRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_Z);
|
||||
InputDevice.MotionRange rzRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RZ);
|
||||
|
||||
// Most other controllers use Z and RZ for the right stick
|
||||
if (zRange != null && rzRange != null) {
|
||||
context.rightStickXAxis = MotionEvent.AXIS_Z;
|
||||
context.rightStickYAxis = MotionEvent.AXIS_RZ;
|
||||
}
|
||||
else {
|
||||
InputDevice.MotionRange rxRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RX);
|
||||
InputDevice.MotionRange ryRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RY);
|
||||
|
||||
// Try RX and RY now
|
||||
if (rxRange != null && ryRange != null) {
|
||||
context.rightStickXAxis = MotionEvent.AXIS_RX;
|
||||
context.rightStickYAxis = MotionEvent.AXIS_RY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Some devices have "hats" for d-pads
|
||||
InputDevice.MotionRange hatXRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_HAT_X);
|
||||
InputDevice.MotionRange hatYRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_HAT_Y);
|
||||
if (hatXRange != null && hatYRange != null) {
|
||||
context.hatXAxis = MotionEvent.AXIS_HAT_X;
|
||||
context.hatYAxis = MotionEvent.AXIS_HAT_Y;
|
||||
}
|
||||
|
||||
if (context.leftStickXAxis != -1 && context.leftStickYAxis != -1) {
|
||||
context.leftStickDeadzoneRadius = (float) stickDeadzone;
|
||||
}
|
||||
|
||||
if (context.rightStickXAxis != -1 && context.rightStickYAxis != -1) {
|
||||
}
|
||||
|
||||
if (context.rightStickXAxis != -1 && context.rightStickYAxis != -1) {
|
||||
context.rightStickDeadzoneRadius = (float) stickDeadzone;
|
||||
}
|
||||
|
||||
@@ -300,114 +300,114 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
|
||||
LimeLog.info("Assigned as controller "+context.controllerNumber);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private ControllerContext getContextForDevice(InputDevice dev) {
|
||||
// Unknown devices use the default context
|
||||
if (dev == null) {
|
||||
return defaultContext;
|
||||
}
|
||||
|
||||
String descriptor = dev.getDescriptor();
|
||||
|
||||
// Return the existing context if it exists
|
||||
ControllerContext context = contexts.get(descriptor);
|
||||
if (context != null) {
|
||||
return context;
|
||||
}
|
||||
|
||||
// Otherwise create a new context
|
||||
}
|
||||
|
||||
private ControllerContext getContextForDevice(InputDevice dev) {
|
||||
// Unknown devices use the default context
|
||||
if (dev == null) {
|
||||
return defaultContext;
|
||||
}
|
||||
|
||||
String descriptor = dev.getDescriptor();
|
||||
|
||||
// Return the existing context if it exists
|
||||
ControllerContext context = contexts.get(descriptor);
|
||||
if (context != null) {
|
||||
return context;
|
||||
}
|
||||
|
||||
// Otherwise create a new context
|
||||
context = createContextForDevice(dev);
|
||||
contexts.put(descriptor, context);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private void sendControllerInputPacket(ControllerContext context) {
|
||||
conn.sendControllerInput(context.controllerNumber, context.inputMap,
|
||||
contexts.put(descriptor, context);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private void sendControllerInputPacket(ControllerContext context) {
|
||||
conn.sendControllerInput(context.controllerNumber, context.inputMap,
|
||||
context.leftTrigger, context.rightTrigger,
|
||||
context.leftStickX, context.leftStickY,
|
||||
context.rightStickX, context.rightStickY);
|
||||
}
|
||||
}
|
||||
|
||||
// Return a valid keycode, 0 to consume, or -1 to not consume the event
|
||||
// Device MAY BE NULL
|
||||
private int handleRemapping(ControllerContext context, KeyEvent event) {
|
||||
private int handleRemapping(ControllerContext context, KeyEvent event) {
|
||||
// For remotes, don't capture the back button
|
||||
if (context.isRemote) {
|
||||
if (context.isRemote) {
|
||||
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (context.isDualShock4) {
|
||||
switch (event.getKeyCode()) {
|
||||
case KeyEvent.KEYCODE_BUTTON_Y:
|
||||
return KeyEvent.KEYCODE_BUTTON_L1;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_Z:
|
||||
return KeyEvent.KEYCODE_BUTTON_R1;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_C:
|
||||
return KeyEvent.KEYCODE_BUTTON_B;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_X:
|
||||
return KeyEvent.KEYCODE_BUTTON_Y;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_B:
|
||||
return KeyEvent.KEYCODE_BUTTON_A;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_A:
|
||||
return KeyEvent.KEYCODE_BUTTON_X;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_SELECT:
|
||||
return KeyEvent.KEYCODE_BUTTON_THUMBL;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_START:
|
||||
return KeyEvent.KEYCODE_BUTTON_THUMBR;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_L2:
|
||||
return KeyEvent.KEYCODE_BUTTON_SELECT;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_R2:
|
||||
return KeyEvent.KEYCODE_BUTTON_START;
|
||||
|
||||
// These are duplicate trigger events
|
||||
case KeyEvent.KEYCODE_BUTTON_R1:
|
||||
case KeyEvent.KEYCODE_BUTTON_L1:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (context.hatXAxis != -1 && context.hatYAxis != -1) {
|
||||
switch (event.getKeyCode()) {
|
||||
// These are duplicate dpad events for hat input
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||
case KeyEvent.KEYCODE_DPAD_CENTER:
|
||||
case KeyEvent.KEYCODE_DPAD_UP:
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (context.hatXAxis == -1 &&
|
||||
context.hatYAxis == -1 &&
|
||||
context.isXboxController &&
|
||||
event.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN) {
|
||||
// If there's not a proper Xbox controller mapping, we'll translate the raw d-pad
|
||||
// scan codes into proper key codes
|
||||
switch (event.getScanCode())
|
||||
{
|
||||
case 704:
|
||||
return KeyEvent.KEYCODE_DPAD_LEFT;
|
||||
case 705:
|
||||
return KeyEvent.KEYCODE_DPAD_RIGHT;
|
||||
case 706:
|
||||
return KeyEvent.KEYCODE_DPAD_UP;
|
||||
case 707:
|
||||
return KeyEvent.KEYCODE_DPAD_DOWN;
|
||||
}
|
||||
}
|
||||
switch (event.getKeyCode()) {
|
||||
case KeyEvent.KEYCODE_BUTTON_Y:
|
||||
return KeyEvent.KEYCODE_BUTTON_L1;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_Z:
|
||||
return KeyEvent.KEYCODE_BUTTON_R1;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_C:
|
||||
return KeyEvent.KEYCODE_BUTTON_B;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_X:
|
||||
return KeyEvent.KEYCODE_BUTTON_Y;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_B:
|
||||
return KeyEvent.KEYCODE_BUTTON_A;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_A:
|
||||
return KeyEvent.KEYCODE_BUTTON_X;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_SELECT:
|
||||
return KeyEvent.KEYCODE_BUTTON_THUMBL;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_START:
|
||||
return KeyEvent.KEYCODE_BUTTON_THUMBR;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_L2:
|
||||
return KeyEvent.KEYCODE_BUTTON_SELECT;
|
||||
|
||||
case KeyEvent.KEYCODE_BUTTON_R2:
|
||||
return KeyEvent.KEYCODE_BUTTON_START;
|
||||
|
||||
// These are duplicate trigger events
|
||||
case KeyEvent.KEYCODE_BUTTON_R1:
|
||||
case KeyEvent.KEYCODE_BUTTON_L1:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (context.hatXAxis != -1 && context.hatYAxis != -1) {
|
||||
switch (event.getKeyCode()) {
|
||||
// These are duplicate dpad events for hat input
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||
case KeyEvent.KEYCODE_DPAD_CENTER:
|
||||
case KeyEvent.KEYCODE_DPAD_UP:
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (context.hatXAxis == -1 &&
|
||||
context.hatYAxis == -1 &&
|
||||
context.isXboxController &&
|
||||
event.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN) {
|
||||
// If there's not a proper Xbox controller mapping, we'll translate the raw d-pad
|
||||
// scan codes into proper key codes
|
||||
switch (event.getScanCode())
|
||||
{
|
||||
case 704:
|
||||
return KeyEvent.KEYCODE_DPAD_LEFT;
|
||||
case 705:
|
||||
return KeyEvent.KEYCODE_DPAD_RIGHT;
|
||||
case 706:
|
||||
return KeyEvent.KEYCODE_DPAD_UP;
|
||||
case 707:
|
||||
return KeyEvent.KEYCODE_DPAD_DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
// Past here we can fixup the keycode and potentially trigger
|
||||
// another special case so we need to remember what keycode we're using
|
||||
@@ -439,21 +439,21 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
|
||||
// Emulate the select button with mode
|
||||
return KeyEvent.KEYCODE_BUTTON_SELECT;
|
||||
}
|
||||
|
||||
return keyCode;
|
||||
}
|
||||
|
||||
return keyCode;
|
||||
}
|
||||
|
||||
private Vector2d populateCachedVector(float x, float y) {
|
||||
// Reinitialize our cached Vector2d object
|
||||
inputVector.initialize(x, y);
|
||||
return inputVector;
|
||||
}
|
||||
|
||||
private void handleDeadZone(Vector2d stickVector, float deadzoneRadius) {
|
||||
if (stickVector.getMagnitude() <= deadzoneRadius) {
|
||||
// Deadzone
|
||||
|
||||
private void handleDeadZone(Vector2d stickVector, float deadzoneRadius) {
|
||||
if (stickVector.getMagnitude() <= deadzoneRadius) {
|
||||
// Deadzone
|
||||
stickVector.initialize(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// We're not normalizing here because we let the computer handle the deadzones.
|
||||
// Normalizing can make the deadzones larger than they should be after the computer also
|
||||
@@ -518,9 +518,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
|
||||
|
||||
sendControllerInputPacket(context);
|
||||
}
|
||||
|
||||
public boolean handleMotionEvent(MotionEvent event) {
|
||||
ControllerContext context = getContextForDevice(event.getDevice());
|
||||
|
||||
public boolean handleMotionEvent(MotionEvent event) {
|
||||
ControllerContext context = getContextForDevice(event.getDevice());
|
||||
float lsX = 0, lsY = 0, rsX = 0, rsY = 0, rt = 0, lt = 0, hatX = 0, hatY = 0;
|
||||
|
||||
// We purposefully ignore the historical values in the motion event as it makes
|
||||
@@ -548,255 +548,255 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
|
||||
|
||||
handleAxisSet(context, lsX, lsY, rsX, rsY, lt, rt, hatX, hatY);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean handleButtonUp(KeyEvent event) {
|
||||
ControllerContext context = getContextForDevice(event.getDevice());
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean handleButtonUp(KeyEvent event) {
|
||||
ControllerContext context = getContextForDevice(event.getDevice());
|
||||
|
||||
int keyCode = handleRemapping(context, event);
|
||||
if (keyCode == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the button hasn't been down long enough, sleep for a bit before sending the up event
|
||||
// This allows "instant" button presses (like OUYA's virtual menu button) to work. This
|
||||
// path should not be triggered during normal usage.
|
||||
if (SystemClock.uptimeMillis() - event.getDownTime() < ControllerHandler.MINIMUM_BUTTON_DOWN_TIME_MS)
|
||||
{
|
||||
// Since our sleep time is so short (10 ms), it shouldn't cause a problem doing this in the
|
||||
// UI thread.
|
||||
try {
|
||||
Thread.sleep(ControllerHandler.MINIMUM_BUTTON_DOWN_TIME_MS);
|
||||
} catch (InterruptedException ignored) {}
|
||||
}
|
||||
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_BUTTON_MODE:
|
||||
if (keyCode == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the button hasn't been down long enough, sleep for a bit before sending the up event
|
||||
// This allows "instant" button presses (like OUYA's virtual menu button) to work. This
|
||||
// path should not be triggered during normal usage.
|
||||
if (SystemClock.uptimeMillis() - event.getDownTime() < ControllerHandler.MINIMUM_BUTTON_DOWN_TIME_MS)
|
||||
{
|
||||
// Since our sleep time is so short (10 ms), it shouldn't cause a problem doing this in the
|
||||
// UI thread.
|
||||
try {
|
||||
Thread.sleep(ControllerHandler.MINIMUM_BUTTON_DOWN_TIME_MS);
|
||||
} catch (InterruptedException ignored) {}
|
||||
}
|
||||
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_BUTTON_MODE:
|
||||
context.inputMap &= ~ControllerPacket.SPECIAL_BUTTON_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_START:
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_START:
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
if (SystemClock.uptimeMillis() - context.startDownTime > ControllerHandler.START_DOWN_TIME_KEYB_MS) {
|
||||
gestures.showKeyboard();
|
||||
}
|
||||
context.inputMap &= ~ControllerPacket.PLAY_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
case KeyEvent.KEYCODE_BUTTON_SELECT:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
case KeyEvent.KEYCODE_BUTTON_SELECT:
|
||||
context.inputMap &= ~ControllerPacket.BACK_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||
context.inputMap &= ~ControllerPacket.LEFT_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||
context.inputMap &= ~ControllerPacket.RIGHT_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_UP:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_UP:
|
||||
context.inputMap &= ~ControllerPacket.UP_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||
context.inputMap &= ~ControllerPacket.DOWN_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_B:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_B:
|
||||
context.inputMap &= ~ControllerPacket.B_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_CENTER:
|
||||
case KeyEvent.KEYCODE_BUTTON_A:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_CENTER:
|
||||
case KeyEvent.KEYCODE_BUTTON_A:
|
||||
context.inputMap &= ~ControllerPacket.A_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_X:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_X:
|
||||
context.inputMap &= ~ControllerPacket.X_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_Y:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_Y:
|
||||
context.inputMap &= ~ControllerPacket.Y_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_L1:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_L1:
|
||||
context.inputMap &= ~ControllerPacket.LB_FLAG;
|
||||
context.lastLbUpTime = SystemClock.uptimeMillis();
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_R1:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_R1:
|
||||
context.inputMap &= ~ControllerPacket.RB_FLAG;
|
||||
context.lastRbUpTime = SystemClock.uptimeMillis();
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBL:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBL:
|
||||
context.inputMap &= ~ControllerPacket.LS_CLK_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBR:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBR:
|
||||
context.inputMap &= ~ControllerPacket.RS_CLK_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_L2:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_L2:
|
||||
context.leftTrigger = 0;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_R2:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_R2:
|
||||
context.rightTrigger = 0;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we're emulating the select button
|
||||
if ((context.emulatingButtonFlags & ControllerHandler.EMULATING_SELECT) != 0)
|
||||
{
|
||||
// If either start or LB is up, select comes up too
|
||||
if ((context.inputMap & ControllerPacket.PLAY_FLAG) == 0 ||
|
||||
(context.inputMap & ControllerPacket.LB_FLAG) == 0)
|
||||
{
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we're emulating the select button
|
||||
if ((context.emulatingButtonFlags & ControllerHandler.EMULATING_SELECT) != 0)
|
||||
{
|
||||
// If either start or LB is up, select comes up too
|
||||
if ((context.inputMap & ControllerPacket.PLAY_FLAG) == 0 ||
|
||||
(context.inputMap & ControllerPacket.LB_FLAG) == 0)
|
||||
{
|
||||
context.inputMap &= ~ControllerPacket.BACK_FLAG;
|
||||
|
||||
context.emulatingButtonFlags &= ~ControllerHandler.EMULATING_SELECT;
|
||||
|
||||
try {
|
||||
Thread.sleep(EMULATED_SELECT_UP_DELAY_MS);
|
||||
} catch (InterruptedException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we're emulating the special button
|
||||
if ((context.emulatingButtonFlags & ControllerHandler.EMULATING_SPECIAL) != 0)
|
||||
{
|
||||
// If either start or select and RB is up, the special button comes up too
|
||||
if ((context.inputMap & ControllerPacket.PLAY_FLAG) == 0 ||
|
||||
((context.inputMap & ControllerPacket.BACK_FLAG) == 0 &&
|
||||
(context.inputMap & ControllerPacket.RB_FLAG) == 0))
|
||||
{
|
||||
|
||||
try {
|
||||
Thread.sleep(EMULATED_SELECT_UP_DELAY_MS);
|
||||
} catch (InterruptedException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we're emulating the special button
|
||||
if ((context.emulatingButtonFlags & ControllerHandler.EMULATING_SPECIAL) != 0)
|
||||
{
|
||||
// If either start or select and RB is up, the special button comes up too
|
||||
if ((context.inputMap & ControllerPacket.PLAY_FLAG) == 0 ||
|
||||
((context.inputMap & ControllerPacket.BACK_FLAG) == 0 &&
|
||||
(context.inputMap & ControllerPacket.RB_FLAG) == 0))
|
||||
{
|
||||
context.inputMap &= ~ControllerPacket.SPECIAL_BUTTON_FLAG;
|
||||
|
||||
context.emulatingButtonFlags &= ~ControllerHandler.EMULATING_SPECIAL;
|
||||
|
||||
try {
|
||||
Thread.sleep(EMULATED_SPECIAL_UP_DELAY_MS);
|
||||
} catch (InterruptedException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
sendControllerInputPacket(context);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean handleButtonDown(KeyEvent event) {
|
||||
ControllerContext context = getContextForDevice(event.getDevice());
|
||||
|
||||
try {
|
||||
Thread.sleep(EMULATED_SPECIAL_UP_DELAY_MS);
|
||||
} catch (InterruptedException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
sendControllerInputPacket(context);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean handleButtonDown(KeyEvent event) {
|
||||
ControllerContext context = getContextForDevice(event.getDevice());
|
||||
|
||||
int keyCode = handleRemapping(context, event);
|
||||
if (keyCode == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_BUTTON_MODE:
|
||||
if (keyCode == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_BUTTON_MODE:
|
||||
context.inputMap |= ControllerPacket.SPECIAL_BUTTON_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_START:
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_START:
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
if (event.getRepeatCount() == 0) {
|
||||
context.startDownTime = SystemClock.uptimeMillis();
|
||||
}
|
||||
context.inputMap |= ControllerPacket.PLAY_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
case KeyEvent.KEYCODE_BUTTON_SELECT:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
case KeyEvent.KEYCODE_BUTTON_SELECT:
|
||||
context.inputMap |= ControllerPacket.BACK_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||
context.inputMap |= ControllerPacket.LEFT_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||
context.inputMap |= ControllerPacket.RIGHT_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_UP:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_UP:
|
||||
context.inputMap |= ControllerPacket.UP_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||
context.inputMap |= ControllerPacket.DOWN_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_B:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_B:
|
||||
context.inputMap |= ControllerPacket.B_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_CENTER:
|
||||
case KeyEvent.KEYCODE_BUTTON_A:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_CENTER:
|
||||
case KeyEvent.KEYCODE_BUTTON_A:
|
||||
context.inputMap |= ControllerPacket.A_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_X:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_X:
|
||||
context.inputMap |= ControllerPacket.X_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_Y:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_Y:
|
||||
context.inputMap |= ControllerPacket.Y_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_L1:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_L1:
|
||||
context.inputMap |= ControllerPacket.LB_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_R1:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_R1:
|
||||
context.inputMap |= ControllerPacket.RB_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBL:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBL:
|
||||
context.inputMap |= ControllerPacket.LS_CLK_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBR:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBR:
|
||||
context.inputMap |= ControllerPacket.RS_CLK_FLAG;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_L2:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_L2:
|
||||
context.leftTrigger = (byte)0xFF;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_R2:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_R2:
|
||||
context.rightTrigger = (byte)0xFF;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start+LB acts like select for controllers with one button
|
||||
if ((context.inputMap & ControllerPacket.PLAY_FLAG) != 0 &&
|
||||
((context.inputMap & ControllerPacket.LB_FLAG) != 0 ||
|
||||
SystemClock.uptimeMillis() - context.lastLbUpTime <= MAXIMUM_BUMPER_UP_DELAY_MS))
|
||||
{
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start+LB acts like select for controllers with one button
|
||||
if ((context.inputMap & ControllerPacket.PLAY_FLAG) != 0 &&
|
||||
((context.inputMap & ControllerPacket.LB_FLAG) != 0 ||
|
||||
SystemClock.uptimeMillis() - context.lastLbUpTime <= MAXIMUM_BUMPER_UP_DELAY_MS))
|
||||
{
|
||||
context.inputMap &= ~(ControllerPacket.PLAY_FLAG | ControllerPacket.LB_FLAG);
|
||||
context.inputMap |= ControllerPacket.BACK_FLAG;
|
||||
|
||||
context.emulatingButtonFlags |= ControllerHandler.EMULATING_SELECT;
|
||||
}
|
||||
|
||||
// We detect select+start or start+RB as the special button combo
|
||||
if (((context.inputMap & ControllerPacket.RB_FLAG) != 0 ||
|
||||
(SystemClock.uptimeMillis() - context.lastRbUpTime <= MAXIMUM_BUMPER_UP_DELAY_MS) ||
|
||||
(context.inputMap & ControllerPacket.BACK_FLAG) != 0) &&
|
||||
(context.inputMap & ControllerPacket.PLAY_FLAG) != 0)
|
||||
{
|
||||
}
|
||||
|
||||
// We detect select+start or start+RB as the special button combo
|
||||
if (((context.inputMap & ControllerPacket.RB_FLAG) != 0 ||
|
||||
(SystemClock.uptimeMillis() - context.lastRbUpTime <= MAXIMUM_BUMPER_UP_DELAY_MS) ||
|
||||
(context.inputMap & ControllerPacket.BACK_FLAG) != 0) &&
|
||||
(context.inputMap & ControllerPacket.PLAY_FLAG) != 0)
|
||||
{
|
||||
context.inputMap &= ~(ControllerPacket.BACK_FLAG | ControllerPacket.PLAY_FLAG | ControllerPacket.RB_FLAG);
|
||||
context.inputMap |= ControllerPacket.SPECIAL_BUTTON_FLAG;
|
||||
|
||||
context.emulatingButtonFlags |= ControllerHandler.EMULATING_SPECIAL;
|
||||
}
|
||||
}
|
||||
|
||||
// Send a new input packet if this is the first instance of a button down event
|
||||
// or anytime if we're emulating a button
|
||||
if (event.getRepeatCount() == 0 || context.emulatingButtonFlags != 0) {
|
||||
sendControllerInputPacket(context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class ControllerContext {
|
||||
public String name;
|
||||
public int id;
|
||||
|
||||
public int leftStickXAxis = -1;
|
||||
public int leftStickYAxis = -1;
|
||||
public float leftStickDeadzoneRadius;
|
||||
public int leftStickXAxis = -1;
|
||||
public int leftStickYAxis = -1;
|
||||
public float leftStickDeadzoneRadius;
|
||||
|
||||
public int rightStickXAxis = -1;
|
||||
public int rightStickYAxis = -1;
|
||||
public float rightStickDeadzoneRadius;
|
||||
|
||||
public int leftTriggerAxis = -1;
|
||||
public int rightTriggerAxis = -1;
|
||||
public boolean triggersIdleNegative;
|
||||
public int rightStickXAxis = -1;
|
||||
public int rightStickYAxis = -1;
|
||||
public float rightStickDeadzoneRadius;
|
||||
|
||||
public int leftTriggerAxis = -1;
|
||||
public int rightTriggerAxis = -1;
|
||||
public boolean triggersIdleNegative;
|
||||
public float triggerDeadzone;
|
||||
|
||||
public int hatXAxis = -1;
|
||||
public int hatYAxis = -1;
|
||||
|
||||
public boolean isDualShock4;
|
||||
public boolean isXboxController;
|
||||
|
||||
public int hatXAxis = -1;
|
||||
public int hatYAxis = -1;
|
||||
|
||||
public boolean isDualShock4;
|
||||
public boolean isXboxController;
|
||||
public boolean backIsStart;
|
||||
public boolean modeIsSelect;
|
||||
public boolean isRemote;
|
||||
@@ -822,5 +822,5 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
|
||||
public long lastRbUpTime = 0;
|
||||
|
||||
public long startDownTime = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ public class KeyboardTranslator extends KeycodeTranslator {
|
||||
public static final int VK_Z = 90;
|
||||
public static final int VK_ALT = 18;
|
||||
public static final int VK_NUMPAD0 = 96;
|
||||
public static final int VK_BACK_SLASH = 92;
|
||||
public static final int VK_CAPS_LOCK = 20;
|
||||
public static final int VK_BACK_SLASH = 92;
|
||||
public static final int VK_CAPS_LOCK = 20;
|
||||
public static final int VK_CLEAR = 12;
|
||||
public static final int VK_COMMA = 44;
|
||||
public static final int VK_CONTROL = 17;
|
||||
|
||||
@@ -4,95 +4,95 @@ import com.limelight.nvstream.NvConnection;
|
||||
import com.limelight.nvstream.input.MouseButtonPacket;
|
||||
|
||||
public class TouchContext {
|
||||
private int lastTouchX = 0;
|
||||
private int lastTouchY = 0;
|
||||
private int originalTouchX = 0;
|
||||
private int originalTouchY = 0;
|
||||
private long originalTouchTime = 0;
|
||||
private int lastTouchX = 0;
|
||||
private int lastTouchY = 0;
|
||||
private int originalTouchX = 0;
|
||||
private int originalTouchY = 0;
|
||||
private long originalTouchTime = 0;
|
||||
private boolean cancelled;
|
||||
|
||||
private final NvConnection conn;
|
||||
private final int actionIndex;
|
||||
|
||||
private final NvConnection conn;
|
||||
private final int actionIndex;
|
||||
private final double xFactor;
|
||||
private final double yFactor;
|
||||
|
||||
private static final int TAP_MOVEMENT_THRESHOLD = 10;
|
||||
private static final int TAP_TIME_THRESHOLD = 250;
|
||||
|
||||
public TouchContext(NvConnection conn, int actionIndex, double xFactor, double yFactor)
|
||||
{
|
||||
this.conn = conn;
|
||||
this.actionIndex = actionIndex;
|
||||
|
||||
private static final int TAP_MOVEMENT_THRESHOLD = 10;
|
||||
private static final int TAP_TIME_THRESHOLD = 250;
|
||||
|
||||
public TouchContext(NvConnection conn, int actionIndex, double xFactor, double yFactor)
|
||||
{
|
||||
this.conn = conn;
|
||||
this.actionIndex = actionIndex;
|
||||
this.xFactor = xFactor;
|
||||
this.yFactor = yFactor;
|
||||
}
|
||||
}
|
||||
|
||||
public int getActionIndex()
|
||||
{
|
||||
return actionIndex;
|
||||
}
|
||||
|
||||
private boolean isTap()
|
||||
{
|
||||
int xDelta = Math.abs(lastTouchX - originalTouchX);
|
||||
int yDelta = Math.abs(lastTouchY - originalTouchY);
|
||||
long timeDelta = System.currentTimeMillis() - originalTouchTime;
|
||||
|
||||
return xDelta <= TAP_MOVEMENT_THRESHOLD &&
|
||||
yDelta <= TAP_MOVEMENT_THRESHOLD &&
|
||||
timeDelta <= TAP_TIME_THRESHOLD;
|
||||
}
|
||||
|
||||
private byte getMouseButtonIndex()
|
||||
{
|
||||
if (actionIndex == 1) {
|
||||
return MouseButtonPacket.BUTTON_RIGHT;
|
||||
}
|
||||
else {
|
||||
return MouseButtonPacket.BUTTON_LEFT;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean touchDownEvent(int eventX, int eventY)
|
||||
{
|
||||
originalTouchX = lastTouchX = eventX;
|
||||
originalTouchY = lastTouchY = eventY;
|
||||
originalTouchTime = System.currentTimeMillis();
|
||||
|
||||
private boolean isTap()
|
||||
{
|
||||
int xDelta = Math.abs(lastTouchX - originalTouchX);
|
||||
int yDelta = Math.abs(lastTouchY - originalTouchY);
|
||||
long timeDelta = System.currentTimeMillis() - originalTouchTime;
|
||||
|
||||
return xDelta <= TAP_MOVEMENT_THRESHOLD &&
|
||||
yDelta <= TAP_MOVEMENT_THRESHOLD &&
|
||||
timeDelta <= TAP_TIME_THRESHOLD;
|
||||
}
|
||||
|
||||
private byte getMouseButtonIndex()
|
||||
{
|
||||
if (actionIndex == 1) {
|
||||
return MouseButtonPacket.BUTTON_RIGHT;
|
||||
}
|
||||
else {
|
||||
return MouseButtonPacket.BUTTON_LEFT;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean touchDownEvent(int eventX, int eventY)
|
||||
{
|
||||
originalTouchX = lastTouchX = eventX;
|
||||
originalTouchY = lastTouchY = eventY;
|
||||
originalTouchTime = System.currentTimeMillis();
|
||||
cancelled = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void touchUpEvent(int eventX, int eventY)
|
||||
{
|
||||
}
|
||||
|
||||
public void touchUpEvent(int eventX, int eventY)
|
||||
{
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isTap())
|
||||
{
|
||||
byte buttonIndex = getMouseButtonIndex();
|
||||
|
||||
// Lower the mouse button
|
||||
conn.sendMouseButtonDown(buttonIndex);
|
||||
|
||||
// We need to sleep a bit here because some games
|
||||
// do input detection by polling
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ignored) {}
|
||||
|
||||
// Raise the mouse button
|
||||
conn.sendMouseButtonUp(buttonIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean touchMoveEvent(int eventX, int eventY)
|
||||
if (isTap())
|
||||
{
|
||||
byte buttonIndex = getMouseButtonIndex();
|
||||
|
||||
// Lower the mouse button
|
||||
conn.sendMouseButtonDown(buttonIndex);
|
||||
|
||||
// We need to sleep a bit here because some games
|
||||
// do input detection by polling
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ignored) {}
|
||||
|
||||
// Raise the mouse button
|
||||
conn.sendMouseButtonUp(buttonIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean touchMoveEvent(int eventX, int eventY)
|
||||
{
|
||||
if (eventX != lastTouchX || eventY != lastTouchY)
|
||||
{
|
||||
// We only send moves for the primary touch point
|
||||
if (actionIndex == 0) {
|
||||
{
|
||||
// We only send moves for the primary touch point
|
||||
if (actionIndex == 0) {
|
||||
int deltaX = eventX - lastTouchX;
|
||||
int deltaY = eventY - lastTouchY;
|
||||
|
||||
@@ -100,15 +100,15 @@ public class TouchContext {
|
||||
deltaX = (int)Math.round((double)deltaX * xFactor);
|
||||
deltaY = (int)Math.round((double)deltaY * yFactor);
|
||||
|
||||
conn.sendMouseMove((short)deltaX, (short)deltaY);
|
||||
}
|
||||
|
||||
lastTouchX = eventX;
|
||||
lastTouchY = eventY;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
conn.sendMouseMove((short)deltaX, (short)deltaY);
|
||||
}
|
||||
|
||||
lastTouchX = eventX;
|
||||
lastTouchY = eventY;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void cancelTouch() {
|
||||
cancelled = true;
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
package com.limelight.binding.input.evdev;
|
||||
|
||||
public class EvdevEvent {
|
||||
public static final int EVDEV_MIN_EVENT_SIZE = 16;
|
||||
public static final int EVDEV_MAX_EVENT_SIZE = 24;
|
||||
|
||||
/* Event types */
|
||||
public static final short EV_SYN = 0x00;
|
||||
public static final short EV_KEY = 0x01;
|
||||
public static final short EV_REL = 0x02;
|
||||
public static final short EV_MSC = 0x04;
|
||||
|
||||
/* Relative axes */
|
||||
public static final short REL_X = 0x00;
|
||||
public static final short REL_Y = 0x01;
|
||||
public static final short REL_WHEEL = 0x08;
|
||||
|
||||
/* Buttons */
|
||||
public static final short BTN_LEFT = 0x110;
|
||||
public static final short BTN_RIGHT = 0x111;
|
||||
public static final short BTN_MIDDLE = 0x112;
|
||||
public static final short BTN_SIDE = 0x113;
|
||||
public static final short BTN_EXTRA = 0x114;
|
||||
public static final short BTN_FORWARD = 0x115;
|
||||
public static final short BTN_BACK = 0x116;
|
||||
public static final short BTN_TASK = 0x117;
|
||||
public static final short BTN_GAMEPAD = 0x130;
|
||||
|
||||
/* Keys */
|
||||
public static final short KEY_Q = 16;
|
||||
|
||||
public final short type;
|
||||
public final short code;
|
||||
public final int value;
|
||||
|
||||
public EvdevEvent(short type, short code, int value) {
|
||||
this.type = type;
|
||||
this.code = code;
|
||||
this.value = value;
|
||||
}
|
||||
public static final int EVDEV_MIN_EVENT_SIZE = 16;
|
||||
public static final int EVDEV_MAX_EVENT_SIZE = 24;
|
||||
|
||||
/* Event types */
|
||||
public static final short EV_SYN = 0x00;
|
||||
public static final short EV_KEY = 0x01;
|
||||
public static final short EV_REL = 0x02;
|
||||
public static final short EV_MSC = 0x04;
|
||||
|
||||
/* Relative axes */
|
||||
public static final short REL_X = 0x00;
|
||||
public static final short REL_Y = 0x01;
|
||||
public static final short REL_WHEEL = 0x08;
|
||||
|
||||
/* Buttons */
|
||||
public static final short BTN_LEFT = 0x110;
|
||||
public static final short BTN_RIGHT = 0x111;
|
||||
public static final short BTN_MIDDLE = 0x112;
|
||||
public static final short BTN_SIDE = 0x113;
|
||||
public static final short BTN_EXTRA = 0x114;
|
||||
public static final short BTN_FORWARD = 0x115;
|
||||
public static final short BTN_BACK = 0x116;
|
||||
public static final short BTN_TASK = 0x117;
|
||||
public static final short BTN_GAMEPAD = 0x130;
|
||||
|
||||
/* Keys */
|
||||
public static final short KEY_Q = 16;
|
||||
|
||||
public final short type;
|
||||
public final short code;
|
||||
public final int value;
|
||||
|
||||
public EvdevEvent(short type, short code, int value) {
|
||||
this.type = type;
|
||||
this.code = code;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,161 +7,161 @@ import com.limelight.LimeLog;
|
||||
|
||||
public class EvdevHandler {
|
||||
|
||||
private final String absolutePath;
|
||||
private final EvdevListener listener;
|
||||
private boolean shutdown = false;
|
||||
private int fd = -1;
|
||||
|
||||
private final Thread handlerThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
// All the finally blocks here make this code look like a mess
|
||||
// but it's important that we get this right to avoid causing
|
||||
// system-wide input problems.
|
||||
|
||||
// Open the /dev/input/eventX file
|
||||
fd = EvdevReader.open(absolutePath);
|
||||
if (fd == -1) {
|
||||
LimeLog.warning("Unable to open "+absolutePath);
|
||||
return;
|
||||
}
|
||||
private final String absolutePath;
|
||||
private final EvdevListener listener;
|
||||
private boolean shutdown = false;
|
||||
private int fd = -1;
|
||||
|
||||
try {
|
||||
// Check if it's a mouse or keyboard, but not a gamepad
|
||||
if ((!EvdevReader.isMouse(fd) && !EvdevReader.isAlphaKeyboard(fd)) ||
|
||||
EvdevReader.isGamepad(fd)) {
|
||||
// We only handle keyboards and mice
|
||||
return;
|
||||
}
|
||||
private final Thread handlerThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
// All the finally blocks here make this code look like a mess
|
||||
// but it's important that we get this right to avoid causing
|
||||
// system-wide input problems.
|
||||
|
||||
// Grab it for ourselves
|
||||
if (!EvdevReader.grab(fd)) {
|
||||
LimeLog.warning("Unable to grab "+absolutePath);
|
||||
return;
|
||||
}
|
||||
// Open the /dev/input/eventX file
|
||||
fd = EvdevReader.open(absolutePath);
|
||||
if (fd == -1) {
|
||||
LimeLog.warning("Unable to open "+absolutePath);
|
||||
return;
|
||||
}
|
||||
|
||||
LimeLog.info("Grabbed device for raw keyboard/mouse input: "+absolutePath);
|
||||
try {
|
||||
// Check if it's a mouse or keyboard, but not a gamepad
|
||||
if ((!EvdevReader.isMouse(fd) && !EvdevReader.isAlphaKeyboard(fd)) ||
|
||||
EvdevReader.isGamepad(fd)) {
|
||||
// We only handle keyboards and mice
|
||||
return;
|
||||
}
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocate(EvdevEvent.EVDEV_MAX_EVENT_SIZE).order(ByteOrder.nativeOrder());
|
||||
// Grab it for ourselves
|
||||
if (!EvdevReader.grab(fd)) {
|
||||
LimeLog.warning("Unable to grab "+absolutePath);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int deltaX = 0;
|
||||
int deltaY = 0;
|
||||
byte deltaScroll = 0;
|
||||
LimeLog.info("Grabbed device for raw keyboard/mouse input: "+absolutePath);
|
||||
|
||||
while (!isInterrupted() && !shutdown) {
|
||||
EvdevEvent event = EvdevReader.read(fd, buffer);
|
||||
if (event == null) {
|
||||
return;
|
||||
}
|
||||
ByteBuffer buffer = ByteBuffer.allocate(EvdevEvent.EVDEV_MAX_EVENT_SIZE).order(ByteOrder.nativeOrder());
|
||||
|
||||
switch (event.type)
|
||||
{
|
||||
case EvdevEvent.EV_SYN:
|
||||
if (deltaX != 0 || deltaY != 0) {
|
||||
listener.mouseMove(deltaX, deltaY);
|
||||
deltaX = deltaY = 0;
|
||||
}
|
||||
if (deltaScroll != 0) {
|
||||
listener.mouseScroll(deltaScroll);
|
||||
deltaScroll = 0;
|
||||
}
|
||||
break;
|
||||
try {
|
||||
int deltaX = 0;
|
||||
int deltaY = 0;
|
||||
byte deltaScroll = 0;
|
||||
|
||||
case EvdevEvent.EV_REL:
|
||||
switch (event.code)
|
||||
{
|
||||
case EvdevEvent.REL_X:
|
||||
deltaX = event.value;
|
||||
break;
|
||||
case EvdevEvent.REL_Y:
|
||||
deltaY = event.value;
|
||||
break;
|
||||
case EvdevEvent.REL_WHEEL:
|
||||
deltaScroll = (byte) event.value;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
while (!isInterrupted() && !shutdown) {
|
||||
EvdevEvent event = EvdevReader.read(fd, buffer);
|
||||
if (event == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
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.BTN_SIDE:
|
||||
case EvdevEvent.BTN_EXTRA:
|
||||
case EvdevEvent.BTN_FORWARD:
|
||||
case EvdevEvent.BTN_BACK:
|
||||
case EvdevEvent.BTN_TASK:
|
||||
// Other unhandled mouse buttons
|
||||
break;
|
||||
|
||||
default:
|
||||
// We got some unrecognized button. This means
|
||||
// someone is trying to use the other device in this
|
||||
// "combination" input device. We'll try to handle
|
||||
// it via keyboard, but we're not going to disconnect
|
||||
// if we can't
|
||||
short keyCode = EvdevTranslator.translateEvdevKeyCode(event.code);
|
||||
if (keyCode != 0) {
|
||||
listener.keyboardEvent(event.value != 0, keyCode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EvdevEvent.EV_MSC:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// Release our grab
|
||||
EvdevReader.ungrab(fd);
|
||||
}
|
||||
} finally {
|
||||
// Close the file
|
||||
EvdevReader.close(fd);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public EvdevHandler(String absolutePath, EvdevListener listener) {
|
||||
this.absolutePath = absolutePath;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
handlerThread.start();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
// Close the fd. It doesn't matter if this races
|
||||
// with the handler thread. We'll close this out from
|
||||
// under the thread to wake it up
|
||||
if (fd != -1) {
|
||||
EvdevReader.close(fd);
|
||||
}
|
||||
|
||||
shutdown = true;
|
||||
handlerThread.interrupt();
|
||||
|
||||
try {
|
||||
handlerThread.join();
|
||||
} catch (InterruptedException ignored) {}
|
||||
}
|
||||
|
||||
public void notifyDeleted() {
|
||||
stop();
|
||||
}
|
||||
switch (event.type)
|
||||
{
|
||||
case EvdevEvent.EV_SYN:
|
||||
if (deltaX != 0 || deltaY != 0) {
|
||||
listener.mouseMove(deltaX, deltaY);
|
||||
deltaX = deltaY = 0;
|
||||
}
|
||||
if (deltaScroll != 0) {
|
||||
listener.mouseScroll(deltaScroll);
|
||||
deltaScroll = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case EvdevEvent.EV_REL:
|
||||
switch (event.code)
|
||||
{
|
||||
case EvdevEvent.REL_X:
|
||||
deltaX = event.value;
|
||||
break;
|
||||
case EvdevEvent.REL_Y:
|
||||
deltaY = event.value;
|
||||
break;
|
||||
case EvdevEvent.REL_WHEEL:
|
||||
deltaScroll = (byte) 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.BTN_SIDE:
|
||||
case EvdevEvent.BTN_EXTRA:
|
||||
case EvdevEvent.BTN_FORWARD:
|
||||
case EvdevEvent.BTN_BACK:
|
||||
case EvdevEvent.BTN_TASK:
|
||||
// Other unhandled mouse buttons
|
||||
break;
|
||||
|
||||
default:
|
||||
// We got some unrecognized button. This means
|
||||
// someone is trying to use the other device in this
|
||||
// "combination" input device. We'll try to handle
|
||||
// it via keyboard, but we're not going to disconnect
|
||||
// if we can't
|
||||
short keyCode = EvdevTranslator.translateEvdevKeyCode(event.code);
|
||||
if (keyCode != 0) {
|
||||
listener.keyboardEvent(event.value != 0, keyCode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EvdevEvent.EV_MSC:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// Release our grab
|
||||
EvdevReader.ungrab(fd);
|
||||
}
|
||||
} finally {
|
||||
// Close the file
|
||||
EvdevReader.close(fd);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public EvdevHandler(String absolutePath, EvdevListener listener) {
|
||||
this.absolutePath = absolutePath;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
handlerThread.start();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
// Close the fd. It doesn't matter if this races
|
||||
// with the handler thread. We'll close this out from
|
||||
// under the thread to wake it up
|
||||
if (fd != -1) {
|
||||
EvdevReader.close(fd);
|
||||
}
|
||||
|
||||
shutdown = true;
|
||||
handlerThread.interrupt();
|
||||
|
||||
try {
|
||||
handlerThread.join();
|
||||
} catch (InterruptedException ignored) {}
|
||||
}
|
||||
|
||||
public void notifyDeleted() {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.limelight.binding.input.evdev;
|
||||
|
||||
public interface EvdevListener {
|
||||
public static final int BUTTON_LEFT = 1;
|
||||
public static final int BUTTON_MIDDLE = 2;
|
||||
public static final int BUTTON_RIGHT = 3;
|
||||
|
||||
public void mouseMove(int deltaX, int deltaY);
|
||||
public void mouseButtonEvent(int buttonId, boolean down);
|
||||
public void mouseScroll(byte amount);
|
||||
public void keyboardEvent(boolean buttonDown, short keyCode);
|
||||
public static final int BUTTON_LEFT = 1;
|
||||
public static final int BUTTON_MIDDLE = 2;
|
||||
public static final int BUTTON_RIGHT = 3;
|
||||
|
||||
public void mouseMove(int deltaX, int deltaY);
|
||||
public void mouseButtonEvent(int buttonId, boolean down);
|
||||
public void mouseScroll(byte amount);
|
||||
public void keyboardEvent(boolean buttonDown, short keyCode);
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ import java.util.Locale;
|
||||
import com.limelight.LimeLog;
|
||||
|
||||
public class EvdevReader {
|
||||
static {
|
||||
System.loadLibrary("evdev_reader");
|
||||
}
|
||||
static {
|
||||
System.loadLibrary("evdev_reader");
|
||||
}
|
||||
|
||||
public static void patchSeLinuxPolicies() {
|
||||
//
|
||||
@@ -30,76 +30,76 @@ public class EvdevReader {
|
||||
"\"allow untrusted_app input_device chr_file { open read write ioctl }\"");
|
||||
}
|
||||
}
|
||||
|
||||
// Requires root to chmod /dev/input/eventX
|
||||
public static void setPermissions(String[] files, int octalPermissions) {
|
||||
|
||||
// Requires root to chmod /dev/input/eventX
|
||||
public static void setPermissions(String[] files, int octalPermissions) {
|
||||
EvdevShell shell = EvdevShell.getInstance();
|
||||
|
||||
for (String file : files) {
|
||||
shell.runCommand(String.format((Locale)null, "chmod %o %s", octalPermissions, file));
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the fd to be passed to other function or -1 on error
|
||||
public static native int open(String fileName);
|
||||
|
||||
// Prevent other apps (including Android itself) from using the device while "grabbed"
|
||||
public static native boolean grab(int fd);
|
||||
public static native boolean ungrab(int fd);
|
||||
|
||||
// Used for checking device capabilities
|
||||
public static native boolean hasRelAxis(int fd, short axis);
|
||||
public static native boolean hasAbsAxis(int fd, short axis);
|
||||
public static native boolean hasKey(int fd, short key);
|
||||
|
||||
public static boolean isMouse(int fd) {
|
||||
// This is the same check that Android does in EventHub.cpp
|
||||
return hasRelAxis(fd, EvdevEvent.REL_X) &&
|
||||
hasRelAxis(fd, EvdevEvent.REL_Y) &&
|
||||
hasKey(fd, EvdevEvent.BTN_LEFT);
|
||||
}
|
||||
|
||||
public static boolean isAlphaKeyboard(int fd) {
|
||||
// This is the same check that Android does in EventHub.cpp
|
||||
return hasKey(fd, EvdevEvent.KEY_Q);
|
||||
}
|
||||
|
||||
public static boolean isGamepad(int fd) {
|
||||
return hasKey(fd, EvdevEvent.BTN_GAMEPAD);
|
||||
}
|
||||
|
||||
// Returns the bytes read or -1 on error
|
||||
private static native int read(int fd, byte[] buffer);
|
||||
|
||||
// Takes a byte buffer to use to read the output into.
|
||||
// This buffer MUST be in native byte order and at least
|
||||
// EVDEV_MAX_EVENT_SIZE bytes long.
|
||||
public static EvdevEvent read(int fd, ByteBuffer buffer) {
|
||||
int bytesRead = read(fd, buffer.array());
|
||||
if (bytesRead < 0) {
|
||||
LimeLog.warning("Failed to read: "+bytesRead);
|
||||
return null;
|
||||
}
|
||||
else if (bytesRead < EvdevEvent.EVDEV_MIN_EVENT_SIZE) {
|
||||
LimeLog.warning("Short read: "+bytesRead);
|
||||
return null;
|
||||
}
|
||||
|
||||
buffer.limit(bytesRead);
|
||||
buffer.rewind();
|
||||
|
||||
// Throw away the time stamp
|
||||
if (bytesRead == EvdevEvent.EVDEV_MAX_EVENT_SIZE) {
|
||||
buffer.getLong();
|
||||
buffer.getLong();
|
||||
} else {
|
||||
buffer.getInt();
|
||||
buffer.getInt();
|
||||
}
|
||||
|
||||
return new EvdevEvent(buffer.getShort(), buffer.getShort(), buffer.getInt());
|
||||
}
|
||||
|
||||
// Closes the fd from open()
|
||||
public static native int close(int fd);
|
||||
}
|
||||
|
||||
// Returns the fd to be passed to other function or -1 on error
|
||||
public static native int open(String fileName);
|
||||
|
||||
// Prevent other apps (including Android itself) from using the device while "grabbed"
|
||||
public static native boolean grab(int fd);
|
||||
public static native boolean ungrab(int fd);
|
||||
|
||||
// Used for checking device capabilities
|
||||
public static native boolean hasRelAxis(int fd, short axis);
|
||||
public static native boolean hasAbsAxis(int fd, short axis);
|
||||
public static native boolean hasKey(int fd, short key);
|
||||
|
||||
public static boolean isMouse(int fd) {
|
||||
// This is the same check that Android does in EventHub.cpp
|
||||
return hasRelAxis(fd, EvdevEvent.REL_X) &&
|
||||
hasRelAxis(fd, EvdevEvent.REL_Y) &&
|
||||
hasKey(fd, EvdevEvent.BTN_LEFT);
|
||||
}
|
||||
|
||||
public static boolean isAlphaKeyboard(int fd) {
|
||||
// This is the same check that Android does in EventHub.cpp
|
||||
return hasKey(fd, EvdevEvent.KEY_Q);
|
||||
}
|
||||
|
||||
public static boolean isGamepad(int fd) {
|
||||
return hasKey(fd, EvdevEvent.BTN_GAMEPAD);
|
||||
}
|
||||
|
||||
// Returns the bytes read or -1 on error
|
||||
private static native int read(int fd, byte[] buffer);
|
||||
|
||||
// Takes a byte buffer to use to read the output into.
|
||||
// This buffer MUST be in native byte order and at least
|
||||
// EVDEV_MAX_EVENT_SIZE bytes long.
|
||||
public static EvdevEvent read(int fd, ByteBuffer buffer) {
|
||||
int bytesRead = read(fd, buffer.array());
|
||||
if (bytesRead < 0) {
|
||||
LimeLog.warning("Failed to read: "+bytesRead);
|
||||
return null;
|
||||
}
|
||||
else if (bytesRead < EvdevEvent.EVDEV_MIN_EVENT_SIZE) {
|
||||
LimeLog.warning("Short read: "+bytesRead);
|
||||
return null;
|
||||
}
|
||||
|
||||
buffer.limit(bytesRead);
|
||||
buffer.rewind();
|
||||
|
||||
// Throw away the time stamp
|
||||
if (bytesRead == EvdevEvent.EVDEV_MAX_EVENT_SIZE) {
|
||||
buffer.getLong();
|
||||
buffer.getLong();
|
||||
} else {
|
||||
buffer.getInt();
|
||||
buffer.getInt();
|
||||
}
|
||||
|
||||
return new EvdevEvent(buffer.getShort(), buffer.getShort(), buffer.getInt());
|
||||
}
|
||||
|
||||
// Closes the fd from open()
|
||||
public static native int close(int fd);
|
||||
}
|
||||
|
||||
@@ -4,136 +4,136 @@ import android.view.KeyEvent;
|
||||
|
||||
public class EvdevTranslator {
|
||||
|
||||
private static final short[] EVDEV_KEY_CODES = {
|
||||
0, //KeyEvent.VK_RESERVED
|
||||
KeyEvent.KEYCODE_ESCAPE,
|
||||
KeyEvent.KEYCODE_1,
|
||||
KeyEvent.KEYCODE_2,
|
||||
KeyEvent.KEYCODE_3,
|
||||
KeyEvent.KEYCODE_4,
|
||||
KeyEvent.KEYCODE_5,
|
||||
KeyEvent.KEYCODE_6,
|
||||
KeyEvent.KEYCODE_7,
|
||||
KeyEvent.KEYCODE_8,
|
||||
KeyEvent.KEYCODE_9,
|
||||
KeyEvent.KEYCODE_0,
|
||||
KeyEvent.KEYCODE_MINUS,
|
||||
KeyEvent.KEYCODE_EQUALS,
|
||||
KeyEvent.KEYCODE_DEL,
|
||||
KeyEvent.KEYCODE_TAB,
|
||||
KeyEvent.KEYCODE_Q,
|
||||
KeyEvent.KEYCODE_W,
|
||||
KeyEvent.KEYCODE_E,
|
||||
KeyEvent.KEYCODE_R,
|
||||
KeyEvent.KEYCODE_T,
|
||||
KeyEvent.KEYCODE_Y,
|
||||
KeyEvent.KEYCODE_U,
|
||||
KeyEvent.KEYCODE_I,
|
||||
KeyEvent.KEYCODE_O,
|
||||
KeyEvent.KEYCODE_P,
|
||||
KeyEvent.KEYCODE_LEFT_BRACKET,
|
||||
KeyEvent.KEYCODE_RIGHT_BRACKET,
|
||||
KeyEvent.KEYCODE_ENTER,
|
||||
KeyEvent.KEYCODE_CTRL_LEFT,
|
||||
KeyEvent.KEYCODE_A,
|
||||
KeyEvent.KEYCODE_S,
|
||||
KeyEvent.KEYCODE_D,
|
||||
KeyEvent.KEYCODE_F,
|
||||
KeyEvent.KEYCODE_G,
|
||||
KeyEvent.KEYCODE_H,
|
||||
KeyEvent.KEYCODE_J,
|
||||
KeyEvent.KEYCODE_K,
|
||||
KeyEvent.KEYCODE_L,
|
||||
KeyEvent.KEYCODE_SEMICOLON,
|
||||
KeyEvent.KEYCODE_APOSTROPHE,
|
||||
KeyEvent.KEYCODE_GRAVE,
|
||||
KeyEvent.KEYCODE_SHIFT_LEFT,
|
||||
KeyEvent.KEYCODE_BACKSLASH,
|
||||
KeyEvent.KEYCODE_Z,
|
||||
KeyEvent.KEYCODE_X,
|
||||
KeyEvent.KEYCODE_C,
|
||||
KeyEvent.KEYCODE_V,
|
||||
KeyEvent.KEYCODE_B,
|
||||
KeyEvent.KEYCODE_N,
|
||||
KeyEvent.KEYCODE_M,
|
||||
KeyEvent.KEYCODE_COMMA,
|
||||
KeyEvent.KEYCODE_PERIOD,
|
||||
KeyEvent.KEYCODE_SLASH,
|
||||
KeyEvent.KEYCODE_SHIFT_RIGHT,
|
||||
KeyEvent.KEYCODE_NUMPAD_MULTIPLY,
|
||||
KeyEvent.KEYCODE_ALT_LEFT,
|
||||
KeyEvent.KEYCODE_SPACE,
|
||||
KeyEvent.KEYCODE_CAPS_LOCK,
|
||||
KeyEvent.KEYCODE_F1,
|
||||
KeyEvent.KEYCODE_F2,
|
||||
KeyEvent.KEYCODE_F3,
|
||||
KeyEvent.KEYCODE_F4,
|
||||
KeyEvent.KEYCODE_F5,
|
||||
KeyEvent.KEYCODE_F6,
|
||||
KeyEvent.KEYCODE_F7,
|
||||
KeyEvent.KEYCODE_F8,
|
||||
KeyEvent.KEYCODE_F9,
|
||||
KeyEvent.KEYCODE_F10,
|
||||
KeyEvent.KEYCODE_NUM_LOCK,
|
||||
KeyEvent.KEYCODE_SCROLL_LOCK,
|
||||
KeyEvent.KEYCODE_NUMPAD_7,
|
||||
KeyEvent.KEYCODE_NUMPAD_8,
|
||||
KeyEvent.KEYCODE_NUMPAD_9,
|
||||
KeyEvent.KEYCODE_NUMPAD_SUBTRACT,
|
||||
KeyEvent.KEYCODE_NUMPAD_4,
|
||||
KeyEvent.KEYCODE_NUMPAD_5,
|
||||
KeyEvent.KEYCODE_NUMPAD_6,
|
||||
KeyEvent.KEYCODE_NUMPAD_ADD,
|
||||
KeyEvent.KEYCODE_NUMPAD_1,
|
||||
KeyEvent.KEYCODE_NUMPAD_2,
|
||||
KeyEvent.KEYCODE_NUMPAD_3,
|
||||
KeyEvent.KEYCODE_NUMPAD_0,
|
||||
KeyEvent.KEYCODE_NUMPAD_DOT,
|
||||
0,
|
||||
0, //KeyEvent.VK_ZENKAKUHANKAKU,
|
||||
0, //KeyEvent.VK_102ND,
|
||||
KeyEvent.KEYCODE_F11,
|
||||
KeyEvent.KEYCODE_F12,
|
||||
0, //KeyEvent.VK_RO,
|
||||
0, //KeyEvent.VK_KATAKANA,
|
||||
0, //KeyEvent.VK_HIRAGANA,
|
||||
0, //KeyEvent.VK_HENKAN,
|
||||
0, //KeyEvent.VK_KATAKANAHIRAGANA,
|
||||
0, //KeyEvent.VK_MUHENKAN,
|
||||
0, //KeyEvent.VK_KPJPCOMMA,
|
||||
KeyEvent.KEYCODE_NUMPAD_ENTER,
|
||||
KeyEvent.KEYCODE_CTRL_RIGHT,
|
||||
KeyEvent.KEYCODE_NUMPAD_DIVIDE,
|
||||
KeyEvent.KEYCODE_SYSRQ,
|
||||
KeyEvent.KEYCODE_ALT_RIGHT,
|
||||
0, //KeyEvent.VK_LINEFEED,
|
||||
KeyEvent.KEYCODE_HOME,
|
||||
KeyEvent.KEYCODE_DPAD_UP,
|
||||
KeyEvent.KEYCODE_PAGE_UP,
|
||||
KeyEvent.KEYCODE_DPAD_LEFT,
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT,
|
||||
KeyEvent.KEYCODE_MOVE_END,
|
||||
KeyEvent.KEYCODE_DPAD_DOWN,
|
||||
KeyEvent.KEYCODE_PAGE_DOWN,
|
||||
KeyEvent.KEYCODE_INSERT,
|
||||
KeyEvent.KEYCODE_FORWARD_DEL,
|
||||
0, //KeyEvent.VK_MACRO,
|
||||
0, //KeyEvent.VK_MUTE,
|
||||
0, //KeyEvent.VK_VOLUMEDOWN,
|
||||
0, //KeyEvent.VK_VOLUMEUP,
|
||||
0, //KeyEvent.VK_POWER, /* SC System Power Down */
|
||||
KeyEvent.KEYCODE_NUMPAD_EQUALS,
|
||||
0, //KeyEvent.VK_KPPLUSMINUS,
|
||||
KeyEvent.KEYCODE_BREAK,
|
||||
0, //KeyEvent.VK_SCALE, /* AL Compiz Scale (Expose) */
|
||||
};
|
||||
|
||||
public static short translateEvdevKeyCode(short evdevKeyCode) {
|
||||
if (evdevKeyCode < EVDEV_KEY_CODES.length) {
|
||||
return EVDEV_KEY_CODES[evdevKeyCode];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
private static final short[] EVDEV_KEY_CODES = {
|
||||
0, //KeyEvent.VK_RESERVED
|
||||
KeyEvent.KEYCODE_ESCAPE,
|
||||
KeyEvent.KEYCODE_1,
|
||||
KeyEvent.KEYCODE_2,
|
||||
KeyEvent.KEYCODE_3,
|
||||
KeyEvent.KEYCODE_4,
|
||||
KeyEvent.KEYCODE_5,
|
||||
KeyEvent.KEYCODE_6,
|
||||
KeyEvent.KEYCODE_7,
|
||||
KeyEvent.KEYCODE_8,
|
||||
KeyEvent.KEYCODE_9,
|
||||
KeyEvent.KEYCODE_0,
|
||||
KeyEvent.KEYCODE_MINUS,
|
||||
KeyEvent.KEYCODE_EQUALS,
|
||||
KeyEvent.KEYCODE_DEL,
|
||||
KeyEvent.KEYCODE_TAB,
|
||||
KeyEvent.KEYCODE_Q,
|
||||
KeyEvent.KEYCODE_W,
|
||||
KeyEvent.KEYCODE_E,
|
||||
KeyEvent.KEYCODE_R,
|
||||
KeyEvent.KEYCODE_T,
|
||||
KeyEvent.KEYCODE_Y,
|
||||
KeyEvent.KEYCODE_U,
|
||||
KeyEvent.KEYCODE_I,
|
||||
KeyEvent.KEYCODE_O,
|
||||
KeyEvent.KEYCODE_P,
|
||||
KeyEvent.KEYCODE_LEFT_BRACKET,
|
||||
KeyEvent.KEYCODE_RIGHT_BRACKET,
|
||||
KeyEvent.KEYCODE_ENTER,
|
||||
KeyEvent.KEYCODE_CTRL_LEFT,
|
||||
KeyEvent.KEYCODE_A,
|
||||
KeyEvent.KEYCODE_S,
|
||||
KeyEvent.KEYCODE_D,
|
||||
KeyEvent.KEYCODE_F,
|
||||
KeyEvent.KEYCODE_G,
|
||||
KeyEvent.KEYCODE_H,
|
||||
KeyEvent.KEYCODE_J,
|
||||
KeyEvent.KEYCODE_K,
|
||||
KeyEvent.KEYCODE_L,
|
||||
KeyEvent.KEYCODE_SEMICOLON,
|
||||
KeyEvent.KEYCODE_APOSTROPHE,
|
||||
KeyEvent.KEYCODE_GRAVE,
|
||||
KeyEvent.KEYCODE_SHIFT_LEFT,
|
||||
KeyEvent.KEYCODE_BACKSLASH,
|
||||
KeyEvent.KEYCODE_Z,
|
||||
KeyEvent.KEYCODE_X,
|
||||
KeyEvent.KEYCODE_C,
|
||||
KeyEvent.KEYCODE_V,
|
||||
KeyEvent.KEYCODE_B,
|
||||
KeyEvent.KEYCODE_N,
|
||||
KeyEvent.KEYCODE_M,
|
||||
KeyEvent.KEYCODE_COMMA,
|
||||
KeyEvent.KEYCODE_PERIOD,
|
||||
KeyEvent.KEYCODE_SLASH,
|
||||
KeyEvent.KEYCODE_SHIFT_RIGHT,
|
||||
KeyEvent.KEYCODE_NUMPAD_MULTIPLY,
|
||||
KeyEvent.KEYCODE_ALT_LEFT,
|
||||
KeyEvent.KEYCODE_SPACE,
|
||||
KeyEvent.KEYCODE_CAPS_LOCK,
|
||||
KeyEvent.KEYCODE_F1,
|
||||
KeyEvent.KEYCODE_F2,
|
||||
KeyEvent.KEYCODE_F3,
|
||||
KeyEvent.KEYCODE_F4,
|
||||
KeyEvent.KEYCODE_F5,
|
||||
KeyEvent.KEYCODE_F6,
|
||||
KeyEvent.KEYCODE_F7,
|
||||
KeyEvent.KEYCODE_F8,
|
||||
KeyEvent.KEYCODE_F9,
|
||||
KeyEvent.KEYCODE_F10,
|
||||
KeyEvent.KEYCODE_NUM_LOCK,
|
||||
KeyEvent.KEYCODE_SCROLL_LOCK,
|
||||
KeyEvent.KEYCODE_NUMPAD_7,
|
||||
KeyEvent.KEYCODE_NUMPAD_8,
|
||||
KeyEvent.KEYCODE_NUMPAD_9,
|
||||
KeyEvent.KEYCODE_NUMPAD_SUBTRACT,
|
||||
KeyEvent.KEYCODE_NUMPAD_4,
|
||||
KeyEvent.KEYCODE_NUMPAD_5,
|
||||
KeyEvent.KEYCODE_NUMPAD_6,
|
||||
KeyEvent.KEYCODE_NUMPAD_ADD,
|
||||
KeyEvent.KEYCODE_NUMPAD_1,
|
||||
KeyEvent.KEYCODE_NUMPAD_2,
|
||||
KeyEvent.KEYCODE_NUMPAD_3,
|
||||
KeyEvent.KEYCODE_NUMPAD_0,
|
||||
KeyEvent.KEYCODE_NUMPAD_DOT,
|
||||
0,
|
||||
0, //KeyEvent.VK_ZENKAKUHANKAKU,
|
||||
0, //KeyEvent.VK_102ND,
|
||||
KeyEvent.KEYCODE_F11,
|
||||
KeyEvent.KEYCODE_F12,
|
||||
0, //KeyEvent.VK_RO,
|
||||
0, //KeyEvent.VK_KATAKANA,
|
||||
0, //KeyEvent.VK_HIRAGANA,
|
||||
0, //KeyEvent.VK_HENKAN,
|
||||
0, //KeyEvent.VK_KATAKANAHIRAGANA,
|
||||
0, //KeyEvent.VK_MUHENKAN,
|
||||
0, //KeyEvent.VK_KPJPCOMMA,
|
||||
KeyEvent.KEYCODE_NUMPAD_ENTER,
|
||||
KeyEvent.KEYCODE_CTRL_RIGHT,
|
||||
KeyEvent.KEYCODE_NUMPAD_DIVIDE,
|
||||
KeyEvent.KEYCODE_SYSRQ,
|
||||
KeyEvent.KEYCODE_ALT_RIGHT,
|
||||
0, //KeyEvent.VK_LINEFEED,
|
||||
KeyEvent.KEYCODE_HOME,
|
||||
KeyEvent.KEYCODE_DPAD_UP,
|
||||
KeyEvent.KEYCODE_PAGE_UP,
|
||||
KeyEvent.KEYCODE_DPAD_LEFT,
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT,
|
||||
KeyEvent.KEYCODE_MOVE_END,
|
||||
KeyEvent.KEYCODE_DPAD_DOWN,
|
||||
KeyEvent.KEYCODE_PAGE_DOWN,
|
||||
KeyEvent.KEYCODE_INSERT,
|
||||
KeyEvent.KEYCODE_FORWARD_DEL,
|
||||
0, //KeyEvent.VK_MACRO,
|
||||
0, //KeyEvent.VK_MUTE,
|
||||
0, //KeyEvent.VK_VOLUMEDOWN,
|
||||
0, //KeyEvent.VK_VOLUMEUP,
|
||||
0, //KeyEvent.VK_POWER, /* SC System Power Down */
|
||||
KeyEvent.KEYCODE_NUMPAD_EQUALS,
|
||||
0, //KeyEvent.VK_KPPLUSMINUS,
|
||||
KeyEvent.KEYCODE_BREAK,
|
||||
0, //KeyEvent.VK_SCALE, /* AL Compiz Scale (Expose) */
|
||||
};
|
||||
|
||||
public static short translateEvdevKeyCode(short evdevKeyCode) {
|
||||
if (evdevKeyCode < EVDEV_KEY_CODES.length) {
|
||||
return EVDEV_KEY_CODES[evdevKeyCode];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,115 +10,115 @@ import android.os.FileObserver;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
public class EvdevWatcher {
|
||||
private static final String PATH = "/dev/input";
|
||||
private static final String REQUIRED_FILE_PREFIX = "event";
|
||||
|
||||
private final HashMap<String, EvdevHandler> handlers = new HashMap<String, EvdevHandler>();
|
||||
private boolean shutdown = false;
|
||||
private boolean init = false;
|
||||
private boolean ungrabbed = false;
|
||||
private EvdevListener listener;
|
||||
private Thread startThread;
|
||||
private static final String PATH = "/dev/input";
|
||||
private static final String REQUIRED_FILE_PREFIX = "event";
|
||||
|
||||
private final HashMap<String, EvdevHandler> handlers = new HashMap<String, EvdevHandler>();
|
||||
private boolean shutdown = false;
|
||||
private boolean init = false;
|
||||
private boolean ungrabbed = false;
|
||||
private EvdevListener listener;
|
||||
private Thread startThread;
|
||||
|
||||
private static boolean patchedSeLinuxPolicies = false;
|
||||
|
||||
private FileObserver observer = new FileObserver(PATH, FileObserver.CREATE | FileObserver.DELETE) {
|
||||
@Override
|
||||
public void onEvent(int event, String fileName) {
|
||||
if (fileName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fileName.startsWith(REQUIRED_FILE_PREFIX)) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (handlers) {
|
||||
if (shutdown) {
|
||||
return;
|
||||
}
|
||||
|
||||
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(new String[]{PATH + "/" + fileName}, 0666);
|
||||
}
|
||||
|
||||
EvdevHandler handler = new EvdevHandler(PATH + "/" + fileName, listener);
|
||||
|
||||
// If we're ungrabbed now, don't start the handler
|
||||
if (!ungrabbed) {
|
||||
handler.start();
|
||||
}
|
||||
|
||||
handlers.put(fileName, handler);
|
||||
}
|
||||
|
||||
if ((event & FileObserver.DELETE) != 0) {
|
||||
LimeLog.info("Halting evdev handler for "+fileName);
|
||||
|
||||
EvdevHandler handler = handlers.remove(fileName);
|
||||
if (handler != null) {
|
||||
handler.notifyDeleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public EvdevWatcher(EvdevListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private File[] rundownWithPermissionsChange(int newPermissions) {
|
||||
// Rundown existing files
|
||||
File devInputDir = new File(PATH);
|
||||
File[] files = devInputDir.listFiles();
|
||||
if (files == null) {
|
||||
return new File[0];
|
||||
}
|
||||
|
||||
// Set desired permissions
|
||||
String[] filePaths = new String[files.length];
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
filePaths[i] = files[i].getAbsolutePath();
|
||||
}
|
||||
EvdevReader.setPermissions(filePaths, newPermissions);
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
public void ungrabAll() {
|
||||
synchronized (handlers) {
|
||||
// Note that we're ungrabbed for now
|
||||
ungrabbed = true;
|
||||
|
||||
// Stop all handlers
|
||||
for (EvdevHandler handler : handlers.values()) {
|
||||
handler.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void regrabAll() {
|
||||
synchronized (handlers) {
|
||||
// We're regrabbing everything now
|
||||
ungrabbed = false;
|
||||
|
||||
for (Map.Entry<String, EvdevHandler> entry : handlers.entrySet()) {
|
||||
// We need to recreate each entry since we can't reuse a stopped one
|
||||
entry.setValue(new EvdevHandler(PATH + "/" + entry.getKey(), listener));
|
||||
entry.getValue().start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
startThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
private FileObserver observer = new FileObserver(PATH, FileObserver.CREATE | FileObserver.DELETE) {
|
||||
@Override
|
||||
public void onEvent(int event, String fileName) {
|
||||
if (fileName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fileName.startsWith(REQUIRED_FILE_PREFIX)) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (handlers) {
|
||||
if (shutdown) {
|
||||
return;
|
||||
}
|
||||
|
||||
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(new String[]{PATH + "/" + fileName}, 0666);
|
||||
}
|
||||
|
||||
EvdevHandler handler = new EvdevHandler(PATH + "/" + fileName, listener);
|
||||
|
||||
// If we're ungrabbed now, don't start the handler
|
||||
if (!ungrabbed) {
|
||||
handler.start();
|
||||
}
|
||||
|
||||
handlers.put(fileName, handler);
|
||||
}
|
||||
|
||||
if ((event & FileObserver.DELETE) != 0) {
|
||||
LimeLog.info("Halting evdev handler for "+fileName);
|
||||
|
||||
EvdevHandler handler = handlers.remove(fileName);
|
||||
if (handler != null) {
|
||||
handler.notifyDeleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public EvdevWatcher(EvdevListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private File[] rundownWithPermissionsChange(int newPermissions) {
|
||||
// Rundown existing files
|
||||
File devInputDir = new File(PATH);
|
||||
File[] files = devInputDir.listFiles();
|
||||
if (files == null) {
|
||||
return new File[0];
|
||||
}
|
||||
|
||||
// Set desired permissions
|
||||
String[] filePaths = new String[files.length];
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
filePaths[i] = files[i].getAbsolutePath();
|
||||
}
|
||||
EvdevReader.setPermissions(filePaths, newPermissions);
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
public void ungrabAll() {
|
||||
synchronized (handlers) {
|
||||
// Note that we're ungrabbed for now
|
||||
ungrabbed = true;
|
||||
|
||||
// Stop all handlers
|
||||
for (EvdevHandler handler : handlers.values()) {
|
||||
handler.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void regrabAll() {
|
||||
synchronized (handlers) {
|
||||
// We're regrabbing everything now
|
||||
ungrabbed = false;
|
||||
|
||||
for (Map.Entry<String, EvdevHandler> entry : handlers.entrySet()) {
|
||||
// We need to recreate each entry since we can't reuse a stopped one
|
||||
entry.setValue(new EvdevHandler(PATH + "/" + entry.getKey(), listener));
|
||||
entry.getValue().start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
startThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Initialize the root shell
|
||||
EvdevShell.getInstance().startShell();
|
||||
|
||||
@@ -128,61 +128,61 @@ public class EvdevWatcher {
|
||||
patchedSeLinuxPolicies = true;
|
||||
}
|
||||
|
||||
// List all files and allow us access
|
||||
File[] files = rundownWithPermissionsChange(0666);
|
||||
|
||||
init = true;
|
||||
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
|
||||
rundownWithPermissionsChange(0660);
|
||||
// List all files and allow us access
|
||||
File[] files = rundownWithPermissionsChange(0666);
|
||||
|
||||
init = true;
|
||||
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
|
||||
rundownWithPermissionsChange(0660);
|
||||
|
||||
// Kill the root shell
|
||||
try {
|
||||
EvdevShell.getInstance().stopShell();
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
};
|
||||
startThread.start();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
// Let start thread cleanup on it's own sweet time
|
||||
synchronized (startThread) {
|
||||
startThread.notify();
|
||||
}
|
||||
|
||||
// Stop the observer
|
||||
observer.stopWatching();
|
||||
|
||||
synchronized (handlers) {
|
||||
// Stop creating new handlers
|
||||
shutdown = true;
|
||||
|
||||
// If we've already ungrabbed, there's nothing else to do
|
||||
if (ungrabbed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop all handlers
|
||||
for (EvdevHandler handler : handlers.values()) {
|
||||
handler.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
startThread.start();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
// Let start thread cleanup on it's own sweet time
|
||||
synchronized (startThread) {
|
||||
startThread.notify();
|
||||
}
|
||||
|
||||
// Stop the observer
|
||||
observer.stopWatching();
|
||||
|
||||
synchronized (handlers) {
|
||||
// Stop creating new handlers
|
||||
shutdown = true;
|
||||
|
||||
// If we've already ungrabbed, there's nothing else to do
|
||||
if (ungrabbed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop all handlers
|
||||
for (EvdevHandler handler : handlers.values()) {
|
||||
handler.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user