Add an ungrab key combo (Ctrl+Shift+Z). Ignore repeat key down events. Fix some mishandling of input events that could cause crashes.

This commit is contained in:
Cameron Gutman 2014-10-03 22:18:36 -07:00
parent 23fcaa1bab
commit a726ba8ea7
2 changed files with 210 additions and 44 deletions

View File

@ -69,6 +69,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
private boolean toastsDisabled;
private EvdevWatcher evdevWatcher;
private int modifierFlags = 0;
private boolean grabbedInput = true;
private boolean grabComboDown = false;
private static final int MODIFIER_CTRL = 0x1;
private static final int MODIFIER_SHIFT = 0x2;
private ConfigurableDecoderRenderer decoderRenderer;
@ -315,6 +320,80 @@ public class Game extends Activity implements SurfaceHolder.Callback,
wifiLock.release();
}
private Runnable toggleGrab = new Runnable() {
@Override
public void run() {
if (evdevWatcher != null) {
if (grabbedInput) {
evdevWatcher.ungrabAll();
}
else {
evdevWatcher.regrabAll();
}
}
grabbedInput = !grabbedInput;
}
};
// Returns true if the key stroke was consumed
private boolean handleMagicKeyCombos(short translatedKey, boolean down) {
int modifierMask = 0;
// Mask off the high byte
translatedKey &= 0xff;
if (translatedKey == KeyboardTranslator.VK_CONTROL) {
modifierMask = MODIFIER_CTRL;
}
else if (translatedKey == KeyboardTranslator.VK_SHIFT) {
modifierMask = MODIFIER_SHIFT;
}
if (down) {
this.modifierFlags |= modifierMask;
}
else {
this.modifierFlags &= ~modifierMask;
}
// Check if Ctrl+Shift+Z is pressed
if (translatedKey == KeyboardTranslator.VK_Z &&
(modifierFlags & (MODIFIER_CTRL|MODIFIER_SHIFT)) == (MODIFIER_CTRL|MODIFIER_SHIFT))
{
if (down) {
// Now that we've pressed the magic combo
// we'll wait for one of the keys to come up
grabComboDown = true;
}
else {
// Toggle the grab if Z comes up
Handler h = getWindow().getDecorView().getHandler();
if (h != null) {
h.postDelayed(toggleGrab, 250);
}
grabComboDown = false;
}
return true;
}
// Toggle the grab if control or shift comes up
else if (grabComboDown) {
Handler h = getWindow().getDecorView().getHandler();
if (h != null) {
h.postDelayed(toggleGrab, 250);
}
grabComboDown = false;
return true;
}
// Not a special combo
return false;
}
private static byte getModifierState(KeyEvent event) {
byte modifier = 0;
if (event.isShiftPressed()) {
@ -350,6 +429,21 @@ public class Game extends Activity implements SurfaceHolder.Callback,
return super.onKeyDown(keyCode, event);
}
// Let this method take duplicate key down events
if (handleMagicKeyCombos(translated, true)) {
return true;
}
// Eat repeat down events
if (event.getRepeatCount() > 0) {
return true;
}
// Pass through keyboard input if we're not grabbing
if (!grabbedInput) {
return super.onKeyDown(keyCode, event);
}
keybTranslator.sendKeyDown(translated,
getModifierState(event));
}
@ -388,6 +482,15 @@ public class Game extends Activity implements SurfaceHolder.Callback,
return super.onKeyUp(keyCode, event);
}
if (handleMagicKeyCombos(translated, false)) {
return true;
}
// Pass through keyboard input if we're not grabbing
if (!grabbedInput) {
return super.onKeyUp(keyCode, event);
}
keybTranslator.sendKeyUp(translated,
getModifierState(event));
}
@ -405,13 +508,23 @@ public class Game extends Activity implements SurfaceHolder.Callback,
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0)
// Returns true if the event was consumed
private boolean handleMotionEvent(MotionEvent event) {
// Pass through keyboard input if we're not grabbing
if (!grabbedInput) {
return false;
}
if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
if (controllerHandler.handleMotionEvent(event)) {
return true;
}
}
else if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0)
{
// This case is for touch-based input devices
if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN ||
event.getSource() == InputDevice.SOURCE_STYLUS)
event.getSource() == InputDevice.SOURCE_STYLUS)
{
int actionIndex = event.getActionIndex();
@ -420,7 +533,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
TouchContext context = getTouchContext(actionIndex);
if (context == null) {
return super.onTouchEvent(event);
return false;
}
switch (event.getActionMasked())
@ -444,8 +557,17 @@ public class Game extends Activity implements SurfaceHolder.Callback,
touchContextMap[i].touchMoveEvent(eventX, eventY);
}
break;
case MotionEvent.ACTION_HOVER_MOVE:
// Send a mouse move update (if neccessary)
updateMousePosition((int)event.getX(), (int)event.getY());
break;
case MotionEvent.ACTION_SCROLL:
// Send the vertical scroll packet
byte vScrollClicks = (byte) event.getAxisValue(MotionEvent.AXIS_VSCROLL);
conn.sendMouseScroll(vScrollClicks);
break;
default:
return super.onTouchEvent(event);
return false;
}
}
// This case is for mice
@ -486,40 +608,34 @@ public class Game extends Activity implements SurfaceHolder.Callback,
}
else
{
return super.onTouchEvent(event);
// Unknown source
return false;
}
// Handled a known source
return true;
}
return super.onTouchEvent(event);
// Unknown class
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!handleMotionEvent(event)) {
return super.onTouchEvent(event);
}
return true;
}
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
if (controllerHandler.handleMotionEvent(event)) {
return true;
}
}
else if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0)
{
switch (event.getActionMasked())
{
case MotionEvent.ACTION_HOVER_MOVE:
// Send a mouse move update (if neccessary)
updateMousePosition((int)event.getX(), (int)event.getY());
break;
case MotionEvent.ACTION_SCROLL:
// Send the vertical scroll packet
byte vScrollClicks = (byte) event.getAxisValue(MotionEvent.AXIS_VSCROLL);
conn.sendMouseScroll(vScrollClicks);
break;
}
return true;
if (!handleMotionEvent(event)) {
return super.onGenericMotionEvent(event);
}
return super.onGenericMotionEvent(event);
return true;
}
private void updateMousePosition(int eventX, int eventY) {
@ -547,15 +663,13 @@ public class Game extends Activity implements SurfaceHolder.Callback,
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
// Send it to the activity's motion event handler
return onGenericMotionEvent(event);
return handleMotionEvent(event);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent event) {
// Send it to the activity's touch event handler
return onTouchEvent(event);
return handleMotionEvent(event);
}
@Override
@ -706,4 +820,20 @@ public class Game extends Activity implements SurfaceHolder.Callback,
public void mouseScroll(byte amount) {
conn.sendMouseScroll(amount);
}
public void keyboardEvent(boolean buttonDown, short keyCode) {
short keyMap = keybTranslator.translate(keyCode);
if (keyMap != 0) {
if (handleMagicKeyCombos(keyMap, buttonDown)) {
return;
}
if (buttonDown) {
keybTranslator.sendKeyDown(keyMap, (byte) 0);
}
else {
keybTranslator.sendKeyUp(keyMap, (byte) 0);
}
}
}
}

View File

@ -2,6 +2,7 @@ package com.limelight.binding.input.evdev;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import com.limelight.LimeLog;
@ -14,6 +15,7 @@ public class EvdevWatcher {
private 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;
@ -42,7 +44,11 @@ public class EvdevWatcher {
}
EvdevHandler handler = new EvdevHandler(PATH + "/" + fileName, listener);
handler.start();
// If we're ungrabbed now, don't start the handler
if (!ungrabbed) {
handler.start();
}
handlers.put(fileName, handler);
}
@ -78,6 +84,31 @@ public class EvdevWatcher {
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
@ -123,6 +154,11 @@ public class EvdevWatcher {
// 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();