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 InputCaptureProvider inputCaptureProvider;
|
||||||
private int modifierFlags = 0;
|
private int modifierFlags = 0;
|
||||||
private boolean grabbedInput = true;
|
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 StreamView streamView;
|
||||||
private long lastAbsTouchUpTime = 0;
|
private long lastAbsTouchUpTime = 0;
|
||||||
private long lastAbsTouchDownTime = 0;
|
private long lastAbsTouchDownTime = 0;
|
||||||
@ -1188,6 +1190,12 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
// Grab/ungrab the mouse cursor
|
// Grab/ungrab the mouse cursor
|
||||||
if (grab) {
|
if (grab) {
|
||||||
inputCaptureProvider.enableCapture();
|
inputCaptureProvider.enableCapture();
|
||||||
|
|
||||||
|
// Enabling capture may hide the cursor again, so
|
||||||
|
// we will need to show it again.
|
||||||
|
if (cursorVisible) {
|
||||||
|
inputCaptureProvider.showCursor();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
inputCaptureProvider.disableCapture();
|
inputCaptureProvider.disableCapture();
|
||||||
@ -1209,6 +1217,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
// Returns true if the key stroke was consumed
|
// Returns true if the key stroke was consumed
|
||||||
private boolean handleSpecialKeys(int androidKeyCode, boolean down) {
|
private boolean handleSpecialKeys(int androidKeyCode, boolean down) {
|
||||||
int modifierMask = 0;
|
int modifierMask = 0;
|
||||||
|
int nonModifierKeyCode = KeyEvent.KEYCODE_UNKNOWN;
|
||||||
|
|
||||||
if (androidKeyCode == KeyEvent.KEYCODE_CTRL_LEFT ||
|
if (androidKeyCode == KeyEvent.KEYCODE_CTRL_LEFT ||
|
||||||
androidKeyCode == KeyEvent.KEYCODE_CTRL_RIGHT) {
|
androidKeyCode == KeyEvent.KEYCODE_CTRL_RIGHT) {
|
||||||
@ -1226,6 +1235,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
androidKeyCode == KeyEvent.KEYCODE_META_RIGHT) {
|
androidKeyCode == KeyEvent.KEYCODE_META_RIGHT) {
|
||||||
modifierMask = KeyboardPacket.MODIFIER_META;
|
modifierMask = KeyboardPacket.MODIFIER_META;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
nonModifierKeyCode = androidKeyCode;
|
||||||
|
}
|
||||||
|
|
||||||
if (down) {
|
if (down) {
|
||||||
this.modifierFlags |= modifierMask;
|
this.modifierFlags |= modifierMask;
|
||||||
@ -1234,36 +1246,62 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
this.modifierFlags &= ~modifierMask;
|
this.modifierFlags &= ~modifierMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if Ctrl+Alt+Shift+Z is pressed
|
// Handle the special combos on the key up
|
||||||
if (androidKeyCode == KeyEvent.KEYCODE_Z &&
|
if (waitingForAllModifiersUp || specialKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
|
||||||
(modifierFlags & (KeyboardPacket.MODIFIER_CTRL | KeyboardPacket.MODIFIER_ALT | KeyboardPacket.MODIFIER_SHIFT)) ==
|
if (specialKeyCode == androidKeyCode) {
|
||||||
(KeyboardPacket.MODIFIER_CTRL | KeyboardPacket.MODIFIER_ALT | KeyboardPacket.MODIFIER_SHIFT))
|
// If this is a key up for the special key itself, eat that because the host never saw the original key down
|
||||||
{
|
return true;
|
||||||
if (down) {
|
}
|
||||||
// Now that we've pressed the magic combo
|
else if (modifierFlags != 0) {
|
||||||
// we'll wait for one of the keys to come up
|
// While we're waiting for modifiers to come up, eat all key downs and allow all key ups to pass
|
||||||
grabComboDown = true;
|
return down;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Toggle the grab if Z comes up
|
// When all modifiers are up, perform the special action
|
||||||
Handler h = getWindow().getDecorView().getHandler();
|
switch (specialKeyCode) {
|
||||||
if (h != null) {
|
// Toggle input grab
|
||||||
h.postDelayed(toggleGrab, 250);
|
case KeyEvent.KEYCODE_Z:
|
||||||
|
Handler h = getWindow().getDecorView().getHandler();
|
||||||
|
if (h != null) {
|
||||||
|
h.postDelayed(toggleGrab, 250);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
grabComboDown = false;
|
// Reset special key state
|
||||||
|
specialKeyCode = KeyEvent.KEYCODE_UNKNOWN;
|
||||||
|
waitingForAllModifiersUp = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
// Toggle the grab if control or shift comes up
|
// Check if Ctrl+Alt+Shift is down when a non-modifier key is pressed
|
||||||
else if (grabComboDown) {
|
else if ((modifierFlags & (KeyboardPacket.MODIFIER_CTRL | KeyboardPacket.MODIFIER_ALT | KeyboardPacket.MODIFIER_SHIFT)) ==
|
||||||
Handler h = getWindow().getDecorView().getHandler();
|
(KeyboardPacket.MODIFIER_CTRL | KeyboardPacket.MODIFIER_ALT | KeyboardPacket.MODIFIER_SHIFT) &&
|
||||||
if (h != null) {
|
(down && nonModifierKeyCode != KeyEvent.KEYCODE_UNKNOWN)) {
|
||||||
h.postDelayed(toggleGrab, 250);
|
// Remember that a special key combo was activated, so we can consume all key events until the modifiers come up
|
||||||
}
|
specialKeyCode = androidKeyCode;
|
||||||
|
waitingForAllModifiersUp = true;
|
||||||
grabComboDown = false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1762,7 +1800,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
// Returns true if the event was consumed
|
// Returns true if the event was consumed
|
||||||
// NB: View is only present if called from a view callback
|
// NB: View is only present if called from a view callback
|
||||||
private boolean handleMotionEvent(View view, MotionEvent event) {
|
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) {
|
if (!grabbedInput) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,8 @@ import android.view.View;
|
|||||||
// is unavailable on this system (ex: DeX, ChromeOS)
|
// is unavailable on this system (ex: DeX, ChromeOS)
|
||||||
@TargetApi(Build.VERSION_CODES.O)
|
@TargetApi(Build.VERSION_CODES.O)
|
||||||
public class AndroidNativePointerCaptureProvider extends AndroidPointerIconCaptureProvider implements InputManager.InputDeviceListener {
|
public class AndroidNativePointerCaptureProvider extends AndroidPointerIconCaptureProvider implements InputManager.InputDeviceListener {
|
||||||
private InputManager inputManager;
|
private final InputManager inputManager;
|
||||||
private View targetView;
|
private final View targetView;
|
||||||
|
|
||||||
public AndroidNativePointerCaptureProvider(Activity activity, View targetView) {
|
public AndroidNativePointerCaptureProvider(Activity activity, View targetView) {
|
||||||
super(activity, targetView);
|
super(activity, targetView);
|
||||||
@ -62,8 +62,16 @@ public class AndroidNativePointerCaptureProvider extends AndroidPointerIconCaptu
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enableCapture() {
|
public void showCursor() {
|
||||||
super.enableCapture();
|
super.showCursor();
|
||||||
|
|
||||||
|
inputManager.unregisterInputDeviceListener(this);
|
||||||
|
targetView.releasePointerCapture();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hideCursor() {
|
||||||
|
super.hideCursor();
|
||||||
|
|
||||||
// Listen for device events to enable/disable capture
|
// Listen for device events to enable/disable capture
|
||||||
inputManager.registerInputDeviceListener(this, null);
|
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
|
@Override
|
||||||
public void onWindowFocusChanged(boolean focusActive) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,14 +4,13 @@ import android.annotation.TargetApi;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.PointerIcon;
|
import android.view.PointerIcon;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.N)
|
@TargetApi(Build.VERSION_CODES.N)
|
||||||
public class AndroidPointerIconCaptureProvider extends InputCaptureProvider {
|
public class AndroidPointerIconCaptureProvider extends InputCaptureProvider {
|
||||||
private View targetView;
|
private final View targetView;
|
||||||
private Context context;
|
private final Context context;
|
||||||
|
|
||||||
public AndroidPointerIconCaptureProvider(Activity activity, View targetView) {
|
public AndroidPointerIconCaptureProvider(Activity activity, View targetView) {
|
||||||
this.context = activity;
|
this.context = activity;
|
||||||
@ -23,14 +22,14 @@ public class AndroidPointerIconCaptureProvider extends InputCaptureProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enableCapture() {
|
public void hideCursor() {
|
||||||
super.enableCapture();
|
super.hideCursor();
|
||||||
targetView.setPointerIcon(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_NULL));
|
targetView.setPointerIcon(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disableCapture() {
|
public void showCursor() {
|
||||||
super.disableCapture();
|
super.showCursor();
|
||||||
targetView.setPointerIcon(null);
|
targetView.setPointerIcon(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,15 @@ import android.view.MotionEvent;
|
|||||||
|
|
||||||
public abstract class InputCaptureProvider {
|
public abstract class InputCaptureProvider {
|
||||||
protected boolean isCapturing;
|
protected boolean isCapturing;
|
||||||
|
protected boolean isCursorVisible;
|
||||||
|
|
||||||
public void enableCapture() {
|
public void enableCapture() {
|
||||||
isCapturing = true;
|
isCapturing = true;
|
||||||
|
hideCursor();
|
||||||
}
|
}
|
||||||
public void disableCapture() {
|
public void disableCapture() {
|
||||||
isCapturing = false;
|
isCapturing = false;
|
||||||
|
showCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() {}
|
public void destroy() {}
|
||||||
@ -22,6 +25,14 @@ public abstract class InputCaptureProvider {
|
|||||||
return isCapturing;
|
return isCapturing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void showCursor() {
|
||||||
|
isCursorVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hideCursor() {
|
||||||
|
isCursorVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ public class ShieldCaptureProvider extends InputCaptureProvider {
|
|||||||
private static int AXIS_RELATIVE_X;
|
private static int AXIS_RELATIVE_X;
|
||||||
private static int AXIS_RELATIVE_Y;
|
private static int AXIS_RELATIVE_Y;
|
||||||
|
|
||||||
private Context context;
|
private final Context context;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
@ -62,14 +62,14 @@ public class ShieldCaptureProvider extends InputCaptureProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enableCapture() {
|
public void hideCursor() {
|
||||||
super.enableCapture();
|
super.hideCursor();
|
||||||
setCursorVisibility(false);
|
setCursorVisibility(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disableCapture() {
|
public void showCursor() {
|
||||||
super.disableCapture();
|
super.showCursor();
|
||||||
setCursorVisibility(true);
|
setCursorVisibility(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,6 +110,9 @@ public class EvdevCaptureProvider extends InputCaptureProvider {
|
|||||||
break;
|
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) {
|
switch (event.type) {
|
||||||
case EvdevEvent.EV_SYN:
|
case EvdevEvent.EV_SYN:
|
||||||
if (deltaX != 0 || deltaY != 0) {
|
if (deltaX != 0 || deltaY != 0) {
|
||||||
@ -231,35 +234,8 @@ public class EvdevCaptureProvider extends InputCaptureProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enableCapture() {
|
public void showCursor() {
|
||||||
super.enableCapture();
|
super.showCursor();
|
||||||
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();
|
|
||||||
// This may be called on the main thread
|
// This may be called on the main thread
|
||||||
runInNetworkSafeContextSynchronously(new Runnable() {
|
runInNetworkSafeContextSynchronously(new Runnable() {
|
||||||
@Override
|
@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
|
@Override
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
// We need to stop the process in this context otherwise
|
// We need to stop the process in this context otherwise
|
||||||
|
Loading…
x
Reference in New Issue
Block a user