mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-23 04:52:48 +00:00
Add an unified input capture interface
This commit is contained in:
parent
117b555fcd
commit
541ac44be4
@ -4,10 +4,12 @@ package com.limelight;
|
|||||||
import com.limelight.binding.PlatformBinding;
|
import com.limelight.binding.PlatformBinding;
|
||||||
import com.limelight.binding.input.ControllerHandler;
|
import com.limelight.binding.input.ControllerHandler;
|
||||||
import com.limelight.binding.input.KeyboardTranslator;
|
import com.limelight.binding.input.KeyboardTranslator;
|
||||||
import com.limelight.binding.input.NvMouseHelper;
|
import com.limelight.binding.input.capture.InputCaptureManager;
|
||||||
|
import com.limelight.binding.input.capture.InputCaptureProvider;
|
||||||
|
import com.limelight.binding.input.capture.ShieldCaptureProvider;
|
||||||
import com.limelight.binding.input.TouchContext;
|
import com.limelight.binding.input.TouchContext;
|
||||||
import com.limelight.binding.input.driver.UsbDriverService;
|
import com.limelight.binding.input.driver.UsbDriverService;
|
||||||
import com.limelight.binding.input.evdev.EvdevHandler;
|
import com.limelight.binding.input.evdev.EvdevCaptureProvider;
|
||||||
import com.limelight.binding.input.evdev.EvdevListener;
|
import com.limelight.binding.input.evdev.EvdevListener;
|
||||||
import com.limelight.binding.input.virtual_controller.VirtualController;
|
import com.limelight.binding.input.virtual_controller.VirtualController;
|
||||||
import com.limelight.binding.video.EnhancedDecoderRenderer;
|
import com.limelight.binding.video.EnhancedDecoderRenderer;
|
||||||
@ -48,12 +50,10 @@ import android.view.InputDevice;
|
|||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
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.OnSystemUiVisibilityChangeListener;
|
||||||
import android.view.View.OnTouchListener;
|
import android.view.View.OnTouchListener;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
@ -92,7 +92,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
private boolean connecting = false;
|
private boolean connecting = false;
|
||||||
private boolean connected = false;
|
private boolean connected = false;
|
||||||
|
|
||||||
private EvdevHandler evdevHandler;
|
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 grabComboDown = false;
|
||||||
@ -284,11 +284,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
streamView);
|
streamView);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LimelightBuildProps.ROOT_BUILD) {
|
inputCaptureProvider = InputCaptureManager.getInputCaptureProvider(this, this);
|
||||||
// Start watching for raw input
|
|
||||||
evdevHandler = new EvdevHandler(this, this);
|
|
||||||
evdevHandler.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prefConfig.onscreenController) {
|
if (prefConfig.onscreenController) {
|
||||||
// create virtual onscreen controller
|
// create virtual onscreen controller
|
||||||
@ -406,16 +402,10 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (grabbedInput) {
|
if (grabbedInput) {
|
||||||
NvMouseHelper.setCursorVisibility(Game.this, true);
|
inputCaptureProvider.disableCapture();
|
||||||
if (evdevHandler != null) {
|
|
||||||
evdevHandler.ungrabAll();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
NvMouseHelper.setCursorVisibility(Game.this, false);
|
inputCaptureProvider.enableCapture();
|
||||||
if (evdevHandler != null) {
|
|
||||||
evdevHandler.regrabAll();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
grabbedInput = !grabbedInput;
|
grabbedInput = !grabbedInput;
|
||||||
@ -638,10 +628,10 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get relative axis values if we can
|
// Get relative axis values if we can
|
||||||
if (NvMouseHelper.eventHasRelativeMouseAxes(event)) {
|
if (inputCaptureProvider.eventHasRelativeMouseAxes(event)) {
|
||||||
// Send the deltas straight from the motion event
|
// Send the deltas straight from the motion event
|
||||||
conn.sendMouseMove((short)NvMouseHelper.getRelativeAxisX(event),
|
conn.sendMouseMove((short) inputCaptureProvider.getRelativeAxisX(event),
|
||||||
(short)NvMouseHelper.getRelativeAxisY(event));
|
(short) inputCaptureProvider.getRelativeAxisY(event));
|
||||||
|
|
||||||
// We have to also update the position Android thinks the cursor is at
|
// We have to also update the position Android thinks the cursor is at
|
||||||
// in order to avoid jumping when we stop moving or click.
|
// in order to avoid jumping when we stop moving or click.
|
||||||
@ -818,14 +808,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
conn.stop();
|
conn.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the Evdev reader to allow use of captured input devices
|
|
||||||
if (evdevHandler != null) {
|
|
||||||
evdevHandler.stop();
|
|
||||||
evdevHandler = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable cursor visibility again
|
// Enable cursor visibility again
|
||||||
NvMouseHelper.setCursorVisibility(this, true);
|
inputCaptureProvider.disableCapture();
|
||||||
|
|
||||||
|
// Destroy the capture provider
|
||||||
|
inputCaptureProvider.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -871,7 +858,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
// Hide the mouse cursor now. Doing it before
|
// Hide the mouse cursor now. Doing it before
|
||||||
// dismissing the spinner seems to be undone
|
// dismissing the spinner seems to be undone
|
||||||
// when the spinner gets displayed.
|
// when the spinner gets displayed.
|
||||||
NvMouseHelper.setCursorVisibility(Game.this, false);
|
inputCaptureProvider.enableCapture();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.limelight.binding.input.capture;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import com.limelight.LimeLog;
|
||||||
|
import com.limelight.binding.input.evdev.EvdevCaptureProvider;
|
||||||
|
import com.limelight.binding.input.evdev.EvdevListener;
|
||||||
|
|
||||||
|
public class InputCaptureManager {
|
||||||
|
public static InputCaptureProvider getInputCaptureProvider(Activity activity, EvdevListener rootListener) {
|
||||||
|
/*if (AndroidCaptureProvider.isCaptureProviderSupported()) {
|
||||||
|
LimeLog.info("Using Android N+ native mouse capture");
|
||||||
|
return new AndroidCaptureProvider();
|
||||||
|
}
|
||||||
|
else*/ if (ShieldCaptureProvider.isCaptureProviderSupported()) {
|
||||||
|
LimeLog.info("Using NVIDIA mouse capture extension");
|
||||||
|
return new ShieldCaptureProvider(activity);
|
||||||
|
}
|
||||||
|
else if (EvdevCaptureProvider.isCaptureProviderSupported()) {
|
||||||
|
LimeLog.info("Using Evdev mouse capture");
|
||||||
|
return new EvdevCaptureProvider(activity, rootListener);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LimeLog.info("Mouse capture not available");
|
||||||
|
return new NullCaptureProvider();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.limelight.binding.input.capture;
|
||||||
|
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
|
public abstract class InputCaptureProvider {
|
||||||
|
public abstract void enableCapture();
|
||||||
|
public abstract void disableCapture();
|
||||||
|
public void destroy() {}
|
||||||
|
|
||||||
|
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getRelativeAxisX(MotionEvent event) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getRelativeAxisY(MotionEvent event) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.limelight.binding.input.capture;
|
||||||
|
|
||||||
|
|
||||||
|
public class NullCaptureProvider extends InputCaptureProvider {
|
||||||
|
@Override
|
||||||
|
public void enableCapture() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disableCapture() {}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.limelight.binding.input;
|
package com.limelight.binding.input.capture;
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -18,12 +18,14 @@ import java.lang.reflect.Method;
|
|||||||
//
|
//
|
||||||
// http://docs.nvidia.com/gameworks/index.html#technologies/mobile/game_controller_handling_mouse.htm
|
// http://docs.nvidia.com/gameworks/index.html#technologies/mobile/game_controller_handling_mouse.htm
|
||||||
|
|
||||||
public class NvMouseHelper {
|
public class ShieldCaptureProvider extends InputCaptureProvider {
|
||||||
private static boolean nvExtensionSupported;
|
private static boolean nvExtensionSupported;
|
||||||
private static Method methodSetCursorVisibility;
|
private static Method methodSetCursorVisibility;
|
||||||
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;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
methodSetCursorVisibility = InputManager.class.getMethod("setCursorVisibility", boolean.class);
|
methodSetCursorVisibility = InputManager.class.getMethod("setCursorVisibility", boolean.class);
|
||||||
@ -36,16 +38,19 @@ public class NvMouseHelper {
|
|||||||
|
|
||||||
nvExtensionSupported = true;
|
nvExtensionSupported = true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LimeLog.info("NvMouseHelper not supported");
|
|
||||||
nvExtensionSupported = false;
|
nvExtensionSupported = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean setCursorVisibility(Context context, boolean visible) {
|
public ShieldCaptureProvider(Context context) {
|
||||||
if (!nvExtensionSupported) {
|
this.context = context;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
public static boolean isCaptureProviderSupported() {
|
||||||
|
return nvExtensionSupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean setCursorVisibility(boolean visible) {
|
||||||
try {
|
try {
|
||||||
methodSetCursorVisibility.invoke(context.getSystemService(Context.INPUT_SERVICE), visible);
|
methodSetCursorVisibility.invoke(context.getSystemService(Context.INPUT_SERVICE), visible);
|
||||||
return true;
|
return true;
|
||||||
@ -58,19 +63,29 @@ public class NvMouseHelper {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
@Override
|
||||||
if (!nvExtensionSupported) {
|
public void enableCapture() {
|
||||||
return false;
|
setCursorVisibility(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disableCapture() {
|
||||||
|
setCursorVisibility(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
||||||
return event.getAxisValue(AXIS_RELATIVE_X) != 0 ||
|
return event.getAxisValue(AXIS_RELATIVE_X) != 0 ||
|
||||||
event.getAxisValue(AXIS_RELATIVE_Y) != 0;
|
event.getAxisValue(AXIS_RELATIVE_Y) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float getRelativeAxisX(MotionEvent event) {
|
@Override
|
||||||
|
public float getRelativeAxisX(MotionEvent event) {
|
||||||
return event.getAxisValue(AXIS_RELATIVE_X);
|
return event.getAxisValue(AXIS_RELATIVE_X);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float getRelativeAxisY(MotionEvent event) {
|
@Override
|
||||||
|
public float getRelativeAxisY(MotionEvent event) {
|
||||||
return event.getAxisValue(AXIS_RELATIVE_Y);
|
return event.getAxisValue(AXIS_RELATIVE_Y);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,6 +4,8 @@ import android.app.Activity;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.limelight.LimeLog;
|
import com.limelight.LimeLog;
|
||||||
|
import com.limelight.LimelightBuildProps;
|
||||||
|
import com.limelight.binding.input.capture.InputCaptureProvider;
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -13,7 +15,7 @@ import java.io.OutputStream;
|
|||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
|
||||||
public class EvdevHandler {
|
public class EvdevCaptureProvider extends InputCaptureProvider {
|
||||||
|
|
||||||
private final EvdevListener listener;
|
private final EvdevListener listener;
|
||||||
private final String libraryPath;
|
private final String libraryPath;
|
||||||
@ -25,6 +27,7 @@ public class EvdevHandler {
|
|||||||
private ServerSocket servSock;
|
private ServerSocket servSock;
|
||||||
private Socket evdevSock;
|
private Socket evdevSock;
|
||||||
private Activity activity;
|
private Activity activity;
|
||||||
|
private boolean started = false;
|
||||||
|
|
||||||
private static final byte UNGRAB_REQUEST = 1;
|
private static final byte UNGRAB_REQUEST = 1;
|
||||||
private static final byte REGRAB_REQUEST = 2;
|
private static final byte REGRAB_REQUEST = 2;
|
||||||
@ -163,24 +166,39 @@ public class EvdevHandler {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public EvdevHandler(Activity activity, EvdevListener listener) {
|
public EvdevCaptureProvider(Activity activity, EvdevListener listener) {
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
this.libraryPath = activity.getApplicationInfo().nativeLibraryDir;
|
this.libraryPath = activity.getApplicationInfo().nativeLibraryDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void regrabAll() {
|
public static boolean isCaptureProviderSupported() {
|
||||||
if (!shutdown && evdevOut != null) {
|
return LimelightBuildProps.ROOT_BUILD;
|
||||||
try {
|
}
|
||||||
evdevOut.write(REGRAB_REQUEST);
|
|
||||||
} catch (IOException e) {
|
@Override
|
||||||
e.printStackTrace();
|
public void enableCapture() {
|
||||||
|
if (!started) {
|
||||||
|
// Start the handler thread if it's our first time
|
||||||
|
// capturing
|
||||||
|
handlerThread.start();
|
||||||
|
started = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Send a request to regrab if we're already capturing
|
||||||
|
if (!shutdown && evdevOut != null) {
|
||||||
|
try {
|
||||||
|
evdevOut.write(REGRAB_REQUEST);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ungrabAll() {
|
@Override
|
||||||
if (!shutdown && evdevOut != null) {
|
public void disableCapture() {
|
||||||
|
if (started && !shutdown && evdevOut != null) {
|
||||||
try {
|
try {
|
||||||
evdevOut.write(UNGRAB_REQUEST);
|
evdevOut.write(UNGRAB_REQUEST);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -189,15 +207,16 @@ public class EvdevHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
@Override
|
||||||
handlerThread.start();
|
public void destroy() {
|
||||||
}
|
|
||||||
|
|
||||||
public void stop() {
|
|
||||||
// We need to stop the process in this context otherwise
|
// We need to stop the process in this context otherwise
|
||||||
// we could get stuck waiting on output from the process
|
// we could get stuck waiting on output from the process
|
||||||
// in order to terminate it.
|
// in order to terminate it.
|
||||||
|
|
||||||
|
if (!started) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
shutdown = true;
|
shutdown = true;
|
||||||
handlerThread.interrupt();
|
handlerThread.interrupt();
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user