Commit a515853569d2ca38235bec75f3ec9f0d6aa084cb

Sam Lantinga 2018-06-18T13:14:02

Added support for external mouse in Samsung DeX mode relative mode doesn't work, but absolute coordinates are functional

diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
index 3cf84c6..e04d1c8 100644
--- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
+++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
@@ -670,6 +670,17 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
      */
     public static boolean supportsRelativeMouse()
     {
+        // ChromeOS doesn't provide relative mouse motion via the Android 7 APIs
+        if (isChromebook()) {
+            return false;
+        }
+
+        // Samsung DeX mode doesn't support relative mice properly under Android 7 APIs,
+        // and simply returns no data under Android 8 APIs.
+        if (isDeXMode()) {
+            return false;
+        }
+
         return SDLActivity.getMotionListener().supportsRelativeMouse();
     }
 
@@ -678,6 +689,10 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
      */
     public static boolean setRelativeMouseEnabled(boolean enabled)
     {
+        if (enabled && !supportsRelativeMouse()) {
+            return false;
+        }
+
         return SDLActivity.getMotionListener().setRelativeMouseEnabled(enabled);
     }
 
@@ -716,6 +731,23 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
     /**
      * This method is called by SDL using JNI.
      */
+    public static boolean isDeXMode() {
+        if (Build.VERSION.SDK_INT < 24) {
+            return false;
+        }
+        try {
+            final Configuration config = getContext().getResources().getConfiguration();
+            final Class configClass = config.getClass();
+            return configClass.getField("SEM_DESKTOP_MODE_ENABLED").getInt(configClass)
+                    == configClass.getField("semDesktopModeEnabled").getInt(config);
+        } catch(Exception ignored) {
+            return false;
+        }
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
     public static DisplayMetrics getDisplayDPI() {
         return getContext().getResources().getDisplayMetrics();
     }
@@ -1313,7 +1345,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
             setOnGenericMotionListener(SDLActivity.getMotionListener());
         }
 
-        if (Build.VERSION.SDK_INT >= 26) {
+        if ((Build.VERSION.SDK_INT >= 26) && !SDLActivity.isDeXMode()) {
             setOnCapturedPointerListener(new SDLCapturedPointerListener_API26());
         }
 
@@ -1544,7 +1576,8 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
         float x,y,p;
 
         // !!! FIXME: dump this SDK check after 2.0.4 ships and require API14.
-        if (event.getSource() == InputDevice.SOURCE_MOUSE && SDLActivity.mSeparateMouseAndTouch) {
+        // 12290 = Samsung DeX mode desktop mouse
+        if ((event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == 12290) && SDLActivity.mSeparateMouseAndTouch) {
             if (Build.VERSION.SDK_INT < 14) {
                 mouseButton = 1; // all mouse buttons are the left button
             } else {
diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java b/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java
index f97dd20..bd6a2ad 100644
--- a/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java
+++ b/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java
@@ -692,6 +692,7 @@ class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
                 return SDLControllerManager.handleJoystickMotionEvent(event);
                 
             case InputDevice.SOURCE_MOUSE:
+            case 12290: // DeX desktop mouse cursor is a separate non-standard input type.
                 if (!SDLActivity.mSeparateMouseAndTouch) {
                     break;
                 }
diff --git a/include/SDL_system.h b/include/SDL_system.h
index 6d6832d..06fce35 100644
--- a/include/SDL_system.h
+++ b/include/SDL_system.h
@@ -131,6 +131,11 @@ extern DECLSPEC SDL_bool SDLCALL SDL_IsAndroidTV(void);
 extern DECLSPEC SDL_bool SDLCALL SDL_IsChromebook(void);
 
 /**
+  \brief Return true is the application is running on a Samsung DeX docking station
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_IsDeXMode(void);
+
+/**
    See the official Android developer guide for more information:
    http://developer.android.com/guide/topics/data/data-storage.html
 */
diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c
index 29a55a9..f5f5d13 100644
--- a/src/core/android/SDL_android.c
+++ b/src/core/android/SDL_android.c
@@ -215,6 +215,7 @@ static jmethodID midSetOrientation;
 static jmethodID midGetContext;
 static jmethodID midIsAndroidTV;
 static jmethodID midIsChromebook;
+static jmethodID midIsDeXMode;
 static jmethodID midInputGetInputDeviceIds;
 static jmethodID midSendMessage;
 static jmethodID midShowTextInput;
@@ -320,6 +321,8 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass c
                                 "isAndroidTV","()Z");
     midIsChromebook = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
                                 "isChromebook", "()Z");
+    midIsDeXMode = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "isDeXMode", "()Z");
     midInputGetInputDeviceIds = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
                                 "inputGetInputDeviceIds", "(I)[I");
     midSendMessage = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
@@ -354,7 +357,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass c
        !midClipboardSetText || !midClipboardGetText || !midClipboardHasText ||
        !midOpenAPKExpansionInputStream || !midGetManifestEnvironmentVariables || !midGetDisplayDPI ||
        !midCreateCustomCursor || !midSetCustomCursor || !midSetSystemCursor || !midSupportsRelativeMouse || !midSetRelativeMouseEnabled ||
-       !midIsChromebook) {
+       !midIsChromebook || !midIsDeXMode) {
         __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?");
     }
 
@@ -2036,6 +2039,12 @@ SDL_bool SDL_IsChromebook(void)
     return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsChromebook);
 }
 
+SDL_bool SDL_IsDeXMode(void)
+{
+    JNIEnv *env = Android_JNI_GetEnv();
+    return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsDeXMode);
+}
+
 const char * SDL_AndroidGetInternalStoragePath(void)
 {
     static char *s_AndroidInternalFilesPath = NULL;
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 8d32232..84730f4 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -673,3 +673,4 @@
 #define SDL_LinuxSetThreadPriority SDL_LinuxSetThreadPriority_REAL
 #define SDL_HasAVX512F SDL_HasAVX512F_REAL
 #define SDL_IsChromebook SDL_IsChromebook_REAL
+#define SDL_IsDeXMode SDL_IsDeXMode_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 3d2a1a3..a95428a 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -714,4 +714,5 @@ SDL_DYNAPI_PROC(int,SDL_LinuxSetThreadPriority,(Sint64 a, int b),(a,b),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_HasAVX512F,(void),(),return)
 #ifdef __ANDROID__
 SDL_DYNAPI_PROC(SDL_bool,SDL_IsChromebook,(void),(),return)
+SDL_DYNAPI_PROC(SDL_bool,SDL_IsDeXMode,(void),(),return)
 #endif
diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c
index 135292e..55d248e 100644
--- a/src/events/SDL_mouse.c
+++ b/src/events/SDL_mouse.c
@@ -721,6 +721,9 @@ SDL_SetRelativeMouseMode(SDL_bool enabled)
     } else if (mouse->SetRelativeMouseMode(enabled) < 0) {
         if (enabled) {
             /* Fall back to warp mode if native relative mode failed */
+            if (!mouse->WarpMouse) {
+                return SDL_SetError("No relative mode implementation available");
+            }
             mouse->relative_mode_warp = SDL_TRUE;
         }
     }