mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-03 08:15:33 +00:00
parent
978a879c43
commit
081cca48fb
@ -132,7 +132,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
private InputCaptureProvider inputCaptureProvider;
|
||||
private int modifierFlags = 0;
|
||||
private boolean grabbedInput = true;
|
||||
private boolean grabComboDown = false;
|
||||
private boolean cursorVisible = false;
|
||||
private boolean waitingForAllModifiersUp = false;
|
||||
private int specialKeyCode = KeyEvent.KEYCODE_UNKNOWN;
|
||||
private StreamView streamView;
|
||||
private long lastAbsTouchUpTime = 0;
|
||||
private long lastAbsTouchDownTime = 0;
|
||||
@ -1188,6 +1190,12 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
// Grab/ungrab the mouse cursor
|
||||
if (grab) {
|
||||
inputCaptureProvider.enableCapture();
|
||||
|
||||
// Enabling capture may hide the cursor again, so
|
||||
// we will need to show it again.
|
||||
if (cursorVisible) {
|
||||
inputCaptureProvider.showCursor();
|
||||
}
|
||||
}
|
||||
else {
|
||||
inputCaptureProvider.disableCapture();
|
||||
@ -1209,6 +1217,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
// Returns true if the key stroke was consumed
|
||||
private boolean handleSpecialKeys(int androidKeyCode, boolean down) {
|
||||
int modifierMask = 0;
|
||||
int nonModifierKeyCode = KeyEvent.KEYCODE_UNKNOWN;
|
||||
|
||||
if (androidKeyCode == KeyEvent.KEYCODE_CTRL_LEFT ||
|
||||
androidKeyCode == KeyEvent.KEYCODE_CTRL_RIGHT) {
|
||||
@ -1226,6 +1235,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
androidKeyCode == KeyEvent.KEYCODE_META_RIGHT) {
|
||||
modifierMask = KeyboardPacket.MODIFIER_META;
|
||||
}
|
||||
else {
|
||||
nonModifierKeyCode = androidKeyCode;
|
||||
}
|
||||
|
||||
if (down) {
|
||||
this.modifierFlags |= modifierMask;
|
||||
@ -1234,36 +1246,62 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
this.modifierFlags &= ~modifierMask;
|
||||
}
|
||||
|
||||
// Check if Ctrl+Alt+Shift+Z is pressed
|
||||
if (androidKeyCode == KeyEvent.KEYCODE_Z &&
|
||||
(modifierFlags & (KeyboardPacket.MODIFIER_CTRL | KeyboardPacket.MODIFIER_ALT | KeyboardPacket.MODIFIER_SHIFT)) ==
|
||||
(KeyboardPacket.MODIFIER_CTRL | KeyboardPacket.MODIFIER_ALT | KeyboardPacket.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;
|
||||
}
|
||||
|
||||
// Handle the special combos on the key up
|
||||
if (waitingForAllModifiersUp || specialKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
|
||||
if (specialKeyCode == androidKeyCode) {
|
||||
// If this is a key up for the special key itself, eat that because the host never saw the original key down
|
||||
return true;
|
||||
}
|
||||
// Toggle the grab if control or shift comes up
|
||||
else if (grabComboDown) {
|
||||
else if (modifierFlags != 0) {
|
||||
// While we're waiting for modifiers to come up, eat all key downs and allow all key ups to pass
|
||||
return down;
|
||||
}
|
||||
else {
|
||||
// When all modifiers are up, perform the special action
|
||||
switch (specialKeyCode) {
|
||||
// Toggle input grab
|
||||
case KeyEvent.KEYCODE_Z:
|
||||
Handler h = getWindow().getDecorView().getHandler();
|
||||
if (h != null) {
|
||||
h.postDelayed(toggleGrab, 250);
|
||||
}
|
||||
break;
|
||||
|
||||
grabComboDown = false;
|
||||
// Quit
|
||||
case KeyEvent.KEYCODE_Q:
|
||||
finish();
|
||||
break;
|
||||
|
||||
// Toggle cursor visibility
|
||||
case KeyEvent.KEYCODE_C:
|
||||
if (!grabbedInput) {
|
||||
inputCaptureProvider.enableCapture();
|
||||
grabbedInput = true;
|
||||
}
|
||||
cursorVisible = !cursorVisible;
|
||||
if (cursorVisible) {
|
||||
inputCaptureProvider.showCursor();
|
||||
} else {
|
||||
inputCaptureProvider.hideCursor();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Reset special key state
|
||||
specialKeyCode = KeyEvent.KEYCODE_UNKNOWN;
|
||||
waitingForAllModifiersUp = false;
|
||||
}
|
||||
}
|
||||
// Check if Ctrl+Alt+Shift is down when a non-modifier key is pressed
|
||||
else if ((modifierFlags & (KeyboardPacket.MODIFIER_CTRL | KeyboardPacket.MODIFIER_ALT | KeyboardPacket.MODIFIER_SHIFT)) ==
|
||||
(KeyboardPacket.MODIFIER_CTRL | KeyboardPacket.MODIFIER_ALT | KeyboardPacket.MODIFIER_SHIFT) &&
|
||||
(down && nonModifierKeyCode != KeyEvent.KEYCODE_UNKNOWN)) {
|
||||
// Remember that a special key combo was activated, so we can consume all key events until the modifiers come up
|
||||
specialKeyCode = androidKeyCode;
|
||||
waitingForAllModifiersUp = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1762,7 +1800,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
// Returns true if the event was consumed
|
||||
// NB: View is only present if called from a view callback
|
||||
private boolean handleMotionEvent(View view, MotionEvent event) {
|
||||
// Pass through keyboard input if we're not grabbing
|
||||
// Pass through mouse/touch/joystick input if we're not grabbing
|
||||
if (!grabbedInput) {
|
||||
return false;
|
||||
}
|
||||
|
@ -15,8 +15,8 @@ import android.view.View;
|
||||
// is unavailable on this system (ex: DeX, ChromeOS)
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
public class AndroidNativePointerCaptureProvider extends AndroidPointerIconCaptureProvider implements InputManager.InputDeviceListener {
|
||||
private InputManager inputManager;
|
||||
private View targetView;
|
||||
private final InputManager inputManager;
|
||||
private final View targetView;
|
||||
|
||||
public AndroidNativePointerCaptureProvider(Activity activity, View targetView) {
|
||||
super(activity, targetView);
|
||||
@ -62,8 +62,16 @@ public class AndroidNativePointerCaptureProvider extends AndroidPointerIconCaptu
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableCapture() {
|
||||
super.enableCapture();
|
||||
public void showCursor() {
|
||||
super.showCursor();
|
||||
|
||||
inputManager.unregisterInputDeviceListener(this);
|
||||
targetView.releasePointerCapture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideCursor() {
|
||||
super.hideCursor();
|
||||
|
||||
// Listen for device events to enable/disable capture
|
||||
inputManager.registerInputDeviceListener(this, null);
|
||||
@ -74,16 +82,12 @@ public class AndroidNativePointerCaptureProvider extends AndroidPointerIconCaptu
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableCapture() {
|
||||
super.disableCapture();
|
||||
inputManager.unregisterInputDeviceListener(this);
|
||||
targetView.releasePointerCapture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean focusActive) {
|
||||
if (!focusActive || !isCapturing) {
|
||||
// NB: We have to check cursor visibility here because Android pointer capture
|
||||
// doesn't support capturing the cursor while it's visible. Enabling pointer
|
||||
// capture implicitly hides the cursor.
|
||||
if (!focusActive || !isCapturing || isCursorVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -4,14 +4,13 @@ import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.PointerIcon;
|
||||
import android.view.View;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
public class AndroidPointerIconCaptureProvider extends InputCaptureProvider {
|
||||
private View targetView;
|
||||
private Context context;
|
||||
private final View targetView;
|
||||
private final Context context;
|
||||
|
||||
public AndroidPointerIconCaptureProvider(Activity activity, View targetView) {
|
||||
this.context = activity;
|
||||
@ -23,14 +22,14 @@ public class AndroidPointerIconCaptureProvider extends InputCaptureProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableCapture() {
|
||||
super.enableCapture();
|
||||
public void hideCursor() {
|
||||
super.hideCursor();
|
||||
targetView.setPointerIcon(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_NULL));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableCapture() {
|
||||
super.disableCapture();
|
||||
public void showCursor() {
|
||||
super.showCursor();
|
||||
targetView.setPointerIcon(null);
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,15 @@ import android.view.MotionEvent;
|
||||
|
||||
public abstract class InputCaptureProvider {
|
||||
protected boolean isCapturing;
|
||||
protected boolean isCursorVisible;
|
||||
|
||||
public void enableCapture() {
|
||||
isCapturing = true;
|
||||
hideCursor();
|
||||
}
|
||||
public void disableCapture() {
|
||||
isCapturing = false;
|
||||
showCursor();
|
||||
}
|
||||
|
||||
public void destroy() {}
|
||||
@ -22,6 +25,14 @@ public abstract class InputCaptureProvider {
|
||||
return isCapturing;
|
||||
}
|
||||
|
||||
public void showCursor() {
|
||||
isCursorVisible = true;
|
||||
}
|
||||
|
||||
public void hideCursor() {
|
||||
isCursorVisible = false;
|
||||
}
|
||||
|
||||
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ public class ShieldCaptureProvider extends InputCaptureProvider {
|
||||
private static int AXIS_RELATIVE_X;
|
||||
private static int AXIS_RELATIVE_Y;
|
||||
|
||||
private Context context;
|
||||
private final Context context;
|
||||
|
||||
static {
|
||||
try {
|
||||
@ -62,14 +62,14 @@ public class ShieldCaptureProvider extends InputCaptureProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableCapture() {
|
||||
super.enableCapture();
|
||||
public void hideCursor() {
|
||||
super.hideCursor();
|
||||
setCursorVisibility(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableCapture() {
|
||||
super.disableCapture();
|
||||
public void showCursor() {
|
||||
super.showCursor();
|
||||
setCursorVisibility(true);
|
||||
}
|
||||
|
||||
|
@ -110,6 +110,9 @@ public class EvdevCaptureProvider extends InputCaptureProvider {
|
||||
break;
|
||||
}
|
||||
|
||||
// Note: The EvdevReader process already filters input events when grabbing
|
||||
// is not enabled, so we don't need to that here.
|
||||
|
||||
switch (event.type) {
|
||||
case EvdevEvent.EV_SYN:
|
||||
if (deltaX != 0 || deltaY != 0) {
|
||||
@ -231,35 +234,8 @@ public class EvdevCaptureProvider extends InputCaptureProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableCapture() {
|
||||
super.enableCapture();
|
||||
if (!started) {
|
||||
// Start the handler thread if it's our first time
|
||||
// capturing
|
||||
handlerThread.start();
|
||||
started = true;
|
||||
}
|
||||
else {
|
||||
// This may be called on the main thread
|
||||
runInNetworkSafeContextSynchronously(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Send a request to regrab if we're already capturing
|
||||
if (!shutdown && evdevOut != null) {
|
||||
try {
|
||||
evdevOut.write(REGRAB_REQUEST);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableCapture() {
|
||||
super.disableCapture();
|
||||
public void showCursor() {
|
||||
super.showCursor();
|
||||
// This may be called on the main thread
|
||||
runInNetworkSafeContextSynchronously(new Runnable() {
|
||||
@Override
|
||||
@ -275,6 +251,39 @@ public class EvdevCaptureProvider extends InputCaptureProvider {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideCursor() {
|
||||
super.hideCursor();
|
||||
// This may be called on the main thread
|
||||
runInNetworkSafeContextSynchronously(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Send a request to regrab if we're already capturing
|
||||
if (started && !shutdown && evdevOut != null) {
|
||||
try {
|
||||
evdevOut.write(REGRAB_REQUEST);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableCapture() {
|
||||
if (!started) {
|
||||
// Start the handler thread if it's our first time
|
||||
// capturing
|
||||
handlerThread.start();
|
||||
started = true;
|
||||
}
|
||||
|
||||
// Call the superclass only after we've started the handler thread.
|
||||
// It will invoke hideCursor() when we call it.
|
||||
super.enableCapture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// We need to stop the process in this context otherwise
|
||||
|
Loading…
x
Reference in New Issue
Block a user