diff --git a/app/src/main/java/com/limelight/Game.java b/app/src/main/java/com/limelight/Game.java index f0014e69..0af7af54 100644 --- a/app/src/main/java/com/limelight/Game.java +++ b/app/src/main/java/com/limelight/Game.java @@ -4,10 +4,12 @@ package com.limelight; import com.limelight.binding.PlatformBinding; import com.limelight.binding.input.ControllerHandler; 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.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.virtual_controller.VirtualController; import com.limelight.binding.video.EnhancedDecoderRenderer; @@ -48,12 +50,10 @@ import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceHolder; -import android.view.SurfaceView; import android.view.View; import android.view.View.OnGenericMotionListener; import android.view.View.OnSystemUiVisibilityChangeListener; import android.view.View.OnTouchListener; -import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.FrameLayout; @@ -92,7 +92,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, private boolean connecting = false; private boolean connected = false; - private EvdevHandler evdevHandler; + private InputCaptureProvider inputCaptureProvider; private int modifierFlags = 0; private boolean grabbedInput = true; private boolean grabComboDown = false; @@ -284,11 +284,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, streamView); } - if (LimelightBuildProps.ROOT_BUILD) { - // Start watching for raw input - evdevHandler = new EvdevHandler(this, this); - evdevHandler.start(); - } + inputCaptureProvider = InputCaptureManager.getInputCaptureProvider(this, this); if (prefConfig.onscreenController) { // create virtual onscreen controller @@ -406,16 +402,10 @@ public class Game extends Activity implements SurfaceHolder.Callback, @Override public void run() { if (grabbedInput) { - NvMouseHelper.setCursorVisibility(Game.this, true); - if (evdevHandler != null) { - evdevHandler.ungrabAll(); - } + inputCaptureProvider.disableCapture(); } else { - NvMouseHelper.setCursorVisibility(Game.this, false); - if (evdevHandler != null) { - evdevHandler.regrabAll(); - } + inputCaptureProvider.enableCapture(); } grabbedInput = !grabbedInput; @@ -638,10 +628,10 @@ public class Game extends Activity implements SurfaceHolder.Callback, } // Get relative axis values if we can - if (NvMouseHelper.eventHasRelativeMouseAxes(event)) { + if (inputCaptureProvider.eventHasRelativeMouseAxes(event)) { // Send the deltas straight from the motion event - conn.sendMouseMove((short)NvMouseHelper.getRelativeAxisX(event), - (short)NvMouseHelper.getRelativeAxisY(event)); + conn.sendMouseMove((short) inputCaptureProvider.getRelativeAxisX(event), + (short) inputCaptureProvider.getRelativeAxisY(event)); // We have to also update the position Android thinks the cursor is at // 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(); } - // Close the Evdev reader to allow use of captured input devices - if (evdevHandler != null) { - evdevHandler.stop(); - evdevHandler = null; - } - // Enable cursor visibility again - NvMouseHelper.setCursorVisibility(this, true); + inputCaptureProvider.disableCapture(); + + // Destroy the capture provider + inputCaptureProvider.destroy(); } @Override @@ -871,7 +858,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, // Hide the mouse cursor now. Doing it before // dismissing the spinner seems to be undone // when the spinner gets displayed. - NvMouseHelper.setCursorVisibility(Game.this, false); + inputCaptureProvider.enableCapture(); } }); diff --git a/app/src/main/java/com/limelight/binding/input/capture/InputCaptureManager.java b/app/src/main/java/com/limelight/binding/input/capture/InputCaptureManager.java new file mode 100644 index 00000000..add31c98 --- /dev/null +++ b/app/src/main/java/com/limelight/binding/input/capture/InputCaptureManager.java @@ -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(); + } + } +} diff --git a/app/src/main/java/com/limelight/binding/input/capture/InputCaptureProvider.java b/app/src/main/java/com/limelight/binding/input/capture/InputCaptureProvider.java new file mode 100644 index 00000000..3de18c37 --- /dev/null +++ b/app/src/main/java/com/limelight/binding/input/capture/InputCaptureProvider.java @@ -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; + } +} diff --git a/app/src/main/java/com/limelight/binding/input/capture/NullCaptureProvider.java b/app/src/main/java/com/limelight/binding/input/capture/NullCaptureProvider.java new file mode 100644 index 00000000..553816c0 --- /dev/null +++ b/app/src/main/java/com/limelight/binding/input/capture/NullCaptureProvider.java @@ -0,0 +1,10 @@ +package com.limelight.binding.input.capture; + + +public class NullCaptureProvider extends InputCaptureProvider { + @Override + public void enableCapture() {} + + @Override + public void disableCapture() {} +} diff --git a/app/src/main/java/com/limelight/binding/input/NvMouseHelper.java b/app/src/main/java/com/limelight/binding/input/capture/ShieldCaptureProvider.java similarity index 72% rename from app/src/main/java/com/limelight/binding/input/NvMouseHelper.java rename to app/src/main/java/com/limelight/binding/input/capture/ShieldCaptureProvider.java index c709a44c..ae8ea046 100644 --- a/app/src/main/java/com/limelight/binding/input/NvMouseHelper.java +++ b/app/src/main/java/com/limelight/binding/input/capture/ShieldCaptureProvider.java @@ -1,4 +1,4 @@ -package com.limelight.binding.input; +package com.limelight.binding.input.capture; 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 -public class NvMouseHelper { +public class ShieldCaptureProvider extends InputCaptureProvider { private static boolean nvExtensionSupported; private static Method methodSetCursorVisibility; private static int AXIS_RELATIVE_X; private static int AXIS_RELATIVE_Y; + private Context context; + static { try { methodSetCursorVisibility = InputManager.class.getMethod("setCursorVisibility", boolean.class); @@ -36,16 +38,19 @@ public class NvMouseHelper { nvExtensionSupported = true; } catch (Exception e) { - LimeLog.info("NvMouseHelper not supported"); nvExtensionSupported = false; } } - public static boolean setCursorVisibility(Context context, boolean visible) { - if (!nvExtensionSupported) { - return false; - } + public ShieldCaptureProvider(Context context) { + this.context = context; + } + public static boolean isCaptureProviderSupported() { + return nvExtensionSupported; + } + + private boolean setCursorVisibility(boolean visible) { try { methodSetCursorVisibility.invoke(context.getSystemService(Context.INPUT_SERVICE), visible); return true; @@ -58,19 +63,29 @@ public class NvMouseHelper { return false; } - public static boolean eventHasRelativeMouseAxes(MotionEvent event) { - if (!nvExtensionSupported) { - return false; - } + @Override + public void enableCapture() { + setCursorVisibility(false); + } + + @Override + public void disableCapture() { + setCursorVisibility(true); + } + + @Override + public boolean eventHasRelativeMouseAxes(MotionEvent event) { return event.getAxisValue(AXIS_RELATIVE_X) != 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); } - public static float getRelativeAxisY(MotionEvent event) { + @Override + public float getRelativeAxisY(MotionEvent event) { return event.getAxisValue(AXIS_RELATIVE_Y); } } diff --git a/app/src/main/java/com/limelight/binding/input/evdev/EvdevHandler.java b/app/src/main/java/com/limelight/binding/input/evdev/EvdevCaptureProvider.java similarity index 87% rename from app/src/main/java/com/limelight/binding/input/evdev/EvdevHandler.java rename to app/src/main/java/com/limelight/binding/input/evdev/EvdevCaptureProvider.java index 4d9e2837..8a2a3a9e 100644 --- a/app/src/main/java/com/limelight/binding/input/evdev/EvdevHandler.java +++ b/app/src/main/java/com/limelight/binding/input/evdev/EvdevCaptureProvider.java @@ -4,6 +4,8 @@ import android.app.Activity; import android.widget.Toast; import com.limelight.LimeLog; +import com.limelight.LimelightBuildProps; +import com.limelight.binding.input.capture.InputCaptureProvider; import java.io.DataOutputStream; import java.io.File; @@ -13,7 +15,7 @@ import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; -public class EvdevHandler { +public class EvdevCaptureProvider extends InputCaptureProvider { private final EvdevListener listener; private final String libraryPath; @@ -25,6 +27,7 @@ public class EvdevHandler { private ServerSocket servSock; private Socket evdevSock; private Activity activity; + private boolean started = false; private static final byte UNGRAB_REQUEST = 1; 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.activity = activity; this.libraryPath = activity.getApplicationInfo().nativeLibraryDir; } - public void regrabAll() { - if (!shutdown && evdevOut != null) { - try { - evdevOut.write(REGRAB_REQUEST); - } catch (IOException e) { - e.printStackTrace(); + public static boolean isCaptureProviderSupported() { + return LimelightBuildProps.ROOT_BUILD; + } + + @Override + 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() { - if (!shutdown && evdevOut != null) { + @Override + public void disableCapture() { + if (started && !shutdown && evdevOut != null) { try { evdevOut.write(UNGRAB_REQUEST); } catch (IOException e) { @@ -189,15 +207,16 @@ public class EvdevHandler { } } - public void start() { - handlerThread.start(); - } - - public void stop() { + @Override + public void destroy() { // We need to stop the process in this context otherwise // we could get stuck waiting on output from the process // in order to terminate it. + if (!started) { + return; + } + shutdown = true; handlerThread.interrupt();