Merge branch 'master' into root

Conflicts:
	AndroidManifest.xml
	libs/limelight-common.jar
	src/com/limelight/Game.java
This commit is contained in:
Cameron Gutman 2014-10-03 23:05:06 -07:00
commit cd4bf9a28b
5 changed files with 256 additions and 63 deletions

View File

@ -2,7 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.limelight.root" package="com.limelight.root"
android:versionCode="35" android:versionCode="35"
android:versionName="2.5.5.1" > android:versionName="2.5.6" >
<uses-sdk <uses-sdk
android:minSdkVersion="16" android:minSdkVersion="16"

Binary file not shown.

View File

@ -36,6 +36,7 @@ import android.view.SurfaceHolder;
import android.view.SurfaceView; import android.view.SurfaceView;
import android.view.View; import android.view.View;
import android.view.View.OnGenericMotionListener; import android.view.View.OnGenericMotionListener;
import android.view.View.OnSystemUiVisibilityChangeListener;
import android.view.View.OnTouchListener; import android.view.View.OnTouchListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.Window; import android.view.Window;
@ -44,7 +45,8 @@ import android.widget.Toast;
public class Game extends Activity implements SurfaceHolder.Callback, public class Game extends Activity implements SurfaceHolder.Callback,
OnGenericMotionListener, OnTouchListener, NvConnectionListener, EvdevListener OnGenericMotionListener, OnTouchListener, NvConnectionListener, EvdevListener,
OnSystemUiVisibilityChangeListener
{ {
private int lastMouseX = Integer.MIN_VALUE; private int lastMouseX = Integer.MIN_VALUE;
private int lastMouseY = Integer.MIN_VALUE; private int lastMouseY = Integer.MIN_VALUE;
@ -70,6 +72,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
private boolean toastsDisabled; private boolean toastsDisabled;
private EvdevWatcher evdevWatcher; 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; private ConfigurableDecoderRenderer decoderRenderer;
@ -134,6 +141,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN); getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
} }
// Listen for UI visibility events
getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this);
// Change volume button behavior // Change volume button behavior
setVolumeControlStream(AudioManager.STREAM_MUSIC); setVolumeControlStream(AudioManager.STREAM_MUSIC);
@ -271,11 +281,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
} }
}; };
private void hideSystemUi() { private void hideSystemUi(int delay) {
Handler h = getWindow().getDecorView().getHandler(); Handler h = getWindow().getDecorView().getHandler();
if (h != null) { if (h != null) {
h.removeCallbacks(hideSystemUi); h.removeCallbacks(hideSystemUi);
h.postDelayed(hideSystemUi, 1000); h.postDelayed(hideSystemUi, delay);
} }
} }
@ -316,6 +326,80 @@ public class Game extends Activity implements SurfaceHolder.Callback,
wifiLock.release(); 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) { private static byte getModifierState(KeyEvent event) {
byte modifier = 0; byte modifier = 0;
if (event.isShiftPressed()) { if (event.isShiftPressed()) {
@ -351,6 +435,21 @@ public class Game extends Activity implements SurfaceHolder.Callback,
return super.onKeyDown(keyCode, event); 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, keybTranslator.sendKeyDown(translated,
getModifierState(event)); getModifierState(event));
} }
@ -360,16 +459,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
@Override @Override
public boolean onKeyUp(int keyCode, KeyEvent event) { public boolean onKeyUp(int keyCode, KeyEvent event) {
// Pressing a volume button drops the immersive flag so the UI shows up again and doesn't
// go away. I'm not sure if that's a bug or a feature, but we're working around it here
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
Handler h = getWindow().getDecorView().getHandler();
if (h != null) {
h.removeCallbacks(hideSystemUi);
h.postDelayed(hideSystemUi, 2000);
}
}
InputDevice dev = event.getDevice(); InputDevice dev = event.getDevice();
if (dev == null) { if (dev == null) {
return super.onKeyUp(keyCode, event); return super.onKeyUp(keyCode, event);
@ -389,6 +478,15 @@ public class Game extends Activity implements SurfaceHolder.Callback,
return super.onKeyUp(keyCode, event); 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, keybTranslator.sendKeyUp(translated,
getModifierState(event)); getModifierState(event));
} }
@ -406,9 +504,19 @@ public class Game extends Activity implements SurfaceHolder.Callback,
} }
} }
@Override // Returns true if the event was consumed
public boolean onTouchEvent(MotionEvent event) { private boolean handleMotionEvent(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) // 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 // This case is for touch-based input devices
if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN || if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN ||
@ -421,7 +529,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
TouchContext context = getTouchContext(actionIndex); TouchContext context = getTouchContext(actionIndex);
if (context == null) { if (context == null) {
return super.onTouchEvent(event); return false;
} }
switch (event.getActionMasked()) switch (event.getActionMasked())
@ -445,8 +553,17 @@ public class Game extends Activity implements SurfaceHolder.Callback,
touchContextMap[i].touchMoveEvent(eventX, eventY); touchContextMap[i].touchMoveEvent(eventX, eventY);
} }
break; 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: default:
return super.onTouchEvent(event); return false;
} }
} }
// This case is for mice // This case is for mice
@ -487,40 +604,34 @@ public class Game extends Activity implements SurfaceHolder.Callback,
} }
else else
{ {
// Unknown source
return false;
}
// Handled a known source
return true;
}
// Unknown class
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!handleMotionEvent(event)) {
return super.onTouchEvent(event); return super.onTouchEvent(event);
} }
return true; return true;
} }
return super.onTouchEvent(event);
}
@Override @Override
public boolean onGenericMotionEvent(MotionEvent event) { public boolean onGenericMotionEvent(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { if (!handleMotionEvent(event)) {
if (controllerHandler.handleMotionEvent(event)) { return super.onGenericMotionEvent(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;
} }
return super.onGenericMotionEvent(event); return true;
} }
private void updateMousePosition(int eventX, int eventY) { private void updateMousePosition(int eventX, int eventY) {
@ -548,15 +659,13 @@ public class Game extends Activity implements SurfaceHolder.Callback,
@Override @Override
public boolean onGenericMotion(View v, MotionEvent event) { public boolean onGenericMotion(View v, MotionEvent event) {
// Send it to the activity's motion event handler return handleMotionEvent(event);
return onGenericMotionEvent(event);
} }
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
@Override @Override
public boolean onTouch(View v, MotionEvent event) { public boolean onTouch(View v, MotionEvent event) {
// Send it to the activity's touch event handler return handleMotionEvent(event);
return onTouchEvent(event);
} }
@Override @Override
@ -592,8 +701,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
if (!displayedFailureDialog) { if (!displayedFailureDialog) {
displayedFailureDialog = true; displayedFailureDialog = true;
Dialog.displayDialog(this, "Connection Error", "Starting "+stage.getName()+" failed", true);
stopConnection(); stopConnection();
Dialog.displayDialog(this, "Connection Error", "Starting "+stage.getName()+" failed", true);
} }
} }
@ -603,8 +712,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
displayedFailureDialog = true; displayedFailureDialog = true;
e.printStackTrace(); e.printStackTrace();
Dialog.displayDialog(this, "Connection Terminated", "The connection failed unexpectedly", true);
stopConnection(); stopConnection();
Dialog.displayDialog(this, "Connection Terminated", "The connection failed unexpectedly", true);
} }
} }
@ -618,7 +727,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
connecting = false; connecting = false;
connected = true; connected = true;
hideSystemUi(); hideSystemUi(1000);
} }
@Override @Override
@ -712,6 +821,10 @@ public class Game extends Activity implements SurfaceHolder.Callback,
public void keyboardEvent(boolean buttonDown, short keyCode) { public void keyboardEvent(boolean buttonDown, short keyCode) {
short keyMap = keybTranslator.translate(keyCode); short keyMap = keybTranslator.translate(keyCode);
if (keyMap != 0) { if (keyMap != 0) {
if (handleMagicKeyCombos(keyMap, buttonDown)) {
return;
}
if (buttonDown) { if (buttonDown) {
keybTranslator.sendKeyDown(keyMap, (byte) 0); keybTranslator.sendKeyDown(keyMap, (byte) 0);
} }
@ -720,4 +833,27 @@ public class Game extends Activity implements SurfaceHolder.Callback,
} }
} }
} }
@Override
public void onSystemUiVisibilityChange(int visibility) {
// Don't do anything if we're not connected
if (!connected) {
return;
}
// This flag is set for all devices
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
hideSystemUi(2000);
}
// This flag is only set on 4.4+
else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT &&
(visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
hideSystemUi(2000);
}
// This flag is only set before 4.4+
else if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT &&
(visibility & View.SYSTEM_UI_FLAG_LOW_PROFILE) == 0) {
hideSystemUi(2000);
}
}
} }

View File

@ -47,6 +47,9 @@ public class ControllerHandler {
public ControllerHandler(NvConnection conn) { public ControllerHandler(NvConnection conn) {
this.conn = conn; this.conn = conn;
// We want limelight-common to scale the axis values to match Xinput values
ControllerPacket.enableAxisScaling = true;
} }
private ControllerMapping createMappingForDevice(InputDevice dev) { private ControllerMapping createMappingForDevice(InputDevice dev) {
@ -135,6 +138,15 @@ public class ControllerHandler {
InputDevice.MotionRange lsYRange = dev.getMotionRange(mapping.leftStickYAxis); InputDevice.MotionRange lsYRange = dev.getMotionRange(mapping.leftStickYAxis);
if (lsXRange != null && lsYRange != null) { if (lsXRange != null && lsYRange != null) {
mapping.leftStickDeadzoneRadius = Math.max(lsXRange.getFlat(), lsYRange.getFlat()); mapping.leftStickDeadzoneRadius = Math.max(lsXRange.getFlat(), lsYRange.getFlat());
// If there isn't a (reasonable) deadzone at all, use 20%
if (mapping.leftStickDeadzoneRadius < 0.02f) {
mapping.leftStickDeadzoneRadius = 0.20f;
}
// Check that the deadzone is 12% at minimum
else if (mapping.leftStickDeadzoneRadius < 0.12f) {
mapping.leftStickDeadzoneRadius = 0.12f;
}
} }
} }
@ -143,6 +155,15 @@ public class ControllerHandler {
InputDevice.MotionRange rsYRange = dev.getMotionRange(mapping.rightStickYAxis); InputDevice.MotionRange rsYRange = dev.getMotionRange(mapping.rightStickYAxis);
if (rsXRange != null && rsYRange != null) { if (rsXRange != null && rsYRange != null) {
mapping.rightStickDeadzoneRadius = Math.max(rsXRange.getFlat(), rsYRange.getFlat()); mapping.rightStickDeadzoneRadius = Math.max(rsXRange.getFlat(), rsYRange.getFlat());
// If there isn't a (reasonable) deadzone at all, use 20%
if (mapping.rightStickDeadzoneRadius < 0.02f) {
mapping.rightStickDeadzoneRadius = 0.20f;
}
// Check that the deadzone is 12% at minimum
else if (mapping.rightStickDeadzoneRadius < 0.12f) {
mapping.rightStickDeadzoneRadius = 0.12f;
}
} }
} }

View File

@ -2,6 +2,7 @@ package com.limelight.binding.input.evdev;
import java.io.File; import java.io.File;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
import com.limelight.LimeLog; import com.limelight.LimeLog;
@ -14,6 +15,7 @@ public class EvdevWatcher {
private HashMap<String, EvdevHandler> handlers = new HashMap<String, EvdevHandler>(); private HashMap<String, EvdevHandler> handlers = new HashMap<String, EvdevHandler>();
private boolean shutdown = false; private boolean shutdown = false;
private boolean init = false; private boolean init = false;
private boolean ungrabbed = false;
private EvdevListener listener; private EvdevListener listener;
private Thread startThread; private Thread startThread;
@ -42,7 +44,11 @@ public class EvdevWatcher {
} }
EvdevHandler handler = new EvdevHandler(PATH + "/" + fileName, listener); EvdevHandler handler = new EvdevHandler(PATH + "/" + fileName, listener);
// If we're ungrabbed now, don't start the handler
if (!ungrabbed) {
handler.start(); handler.start();
}
handlers.put(fileName, handler); handlers.put(fileName, handler);
} }
@ -81,6 +87,31 @@ public class EvdevWatcher {
return files; 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() { public void start() {
startThread = new Thread() { startThread = new Thread() {
@Override @Override
@ -126,6 +157,11 @@ public class EvdevWatcher {
// Stop creating new handlers // Stop creating new handlers
shutdown = true; shutdown = true;
// If we've already ungrabbed, there's nothing else to do
if (ungrabbed) {
return;
}
// Stop all handlers // Stop all handlers
for (EvdevHandler handler : handlers.values()) { for (EvdevHandler handler : handlers.values()) {
handler.stop(); handler.stop();