Просмотр исходного кода

Android: decouple JNI setup from unused subsystems

Query the natively compiled-in subsystems at runtime so the Java side
only registers and initializes the managers that exist, fixing
UnsatisfiedLinkError when SDL is built with a subsystem disabled
(e.g. -DSDL_AUDIO_DISABLED).

- Add SDL.setupJNI(int subsystems) plus SDL_INIT_* Java constants;
  SDLActivity passes an overridable getInitSubsystems() mask so custom
  activities can skip Java-side setup for unused subsystems.
- Distinguish compiled from requested subsystems: JNI registration
  follows nativeGetCompiledSubsystems(), while manager initialization
  and surface/layout creation follow the requested-and-compiled mask.
- Gate HIDDeviceManager.acquire() (USB/BLE device scanning) on a new
  nativeIsHIDAPIEnabled() query so HIDAPI-less builds never touch it.
- Gate SDLControllerManager.initializeDeviceListener() and the
  joystick key-event path on the controller subsystem; gate the
  clipboard handler on video, mirroring the native guards.
- Guard the SDLControllerManager JNI bindings with
  SDL_ANDROID_NEED_CONTROLLER_MANAGER (joystick or haptic enabled).
- Skip layout updates in ShowTextInputTask when no layout exists
  (video disabled).
- Reorganize SDL_android.h/.c into guarded per-subsystem groups with
  matching ordering.
Holden Ramsey 5 дней назад
Родитель
Сommit
2919e1899f

+ 28 - 8
android-project/app/src/main/java/org/libsdl/app/SDL.java

@@ -22,23 +22,29 @@ public class SDL {
     public static final int SDL_INIT_EVERYTHING = SDL_INIT_AUDIO | SDL_INIT_VIDEO |
         SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMEPAD | SDL_INIT_SENSOR | SDL_INIT_CAMERA;
 
+    // SDLControllerManager backs all three of these, so it is set up when any are present.
+    private static final int SDL_INIT_CONTROLLER = SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC;
+
     private static int mInitializedSubsystems = SDL_INIT_EVERYTHING;
+    private static int mCompiledSubsystems = SDL_INIT_EVERYTHING;
 
     static public void setupJNI() {
         setupJNI(SDL_INIT_EVERYTHING);
     }
 
-    // Mask must match the native build's SDL_*_DISABLED flags: dropping SDL_INIT_AUDIO when the lib was built with audio stalls checkJNIReady() and SDL_SetMainReady() never fires.
     static public void setupJNI(int subsystems) {
-        mInitializedSubsystems = subsystems;
-
         SDLActivity.nativeSetupJNI();
 
-        if ((subsystems & SDL_INIT_AUDIO) != 0) {
+        mCompiledSubsystems = SDLActivity.nativeGetCompiledSubsystems();
+        mInitializedSubsystems = (subsystems & mCompiledSubsystems);
+
+        if (isSubsystemCompiled(SDL_INIT_AUDIO)) {
             SDLAudioManager.nativeSetupJNI();
         }
 
-        SDLControllerManager.nativeSetupJNI();
+        if (isSubsystemCompiled(SDL_INIT_CONTROLLER)) {
+            SDLControllerManager.nativeSetupJNI();
+        }
     }
 
     static public void initialize() {
@@ -50,16 +56,30 @@ public class SDL {
 
         SDLActivity.initialize();
 
-        if ((subsystems & SDL_INIT_AUDIO) != 0) {
+        if (isSubsystemCompiled(SDL_INIT_AUDIO)) {
             SDLAudioManager.initialize();
         }
 
-        SDLControllerManager.initialize();
+        if (isSubsystemCompiled(SDL_INIT_CONTROLLER)) {
+            SDLControllerManager.initialize();
+        }
+    }
+
+    static boolean isSubsystemInitialized(int subsystem) {
+        return (mInitializedSubsystems & subsystem) != 0;
+    }
+
+    static boolean isSubsystemCompiled(int subsystem) {
+        return (mCompiledSubsystems & subsystem) != 0;
+    }
+
+    static boolean isControllerManagerReady() {
+        return isSubsystemInitialized(SDL_INIT_CONTROLLER);
     }
 
     // This function stores the current activity (SDL or not)
     static public void setContext(Activity context) {
-        if ((mInitializedSubsystems & SDL_INIT_AUDIO) != 0) {
+        if (isSubsystemCompiled(SDL_INIT_AUDIO)) {
             SDLAudioManager.setContext(context);
         }
         mContext = context;

+ 67 - 25
android-project/app/src/main/java/org/libsdl/app/SDLActivity.java

@@ -337,6 +337,20 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
         return new String[0];
     }
 
+    /**
+     * This method returns the SDL_INIT_* subsystems this activity should set up on the Java side.
+     * It can be overridden to skip the surface/layout creation, device listener and input event
+     * routing for unused subsystems. Manager JNI setup always follows the subsystems compiled
+     * into the native library, regardless of this mask. The native app must not SDL_Init() a
+     * subsystem excluded here: it would initialize without Java-side events (no window surface,
+     * no controller hotplug or input). Called from onCreate(); overrides must not depend on
+     * state the subclass assigns after super.onCreate().
+     * @return mask of SDL.SDL_INIT_* values.
+     */
+    protected int getInitSubsystems() {
+        return SDL.SDL_INIT_EVERYTHING;
+    }
+
     public static void initialize() {
         // The static nature of the singleton and Android quirkyness force us to initialize everything here
         // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values
@@ -458,7 +472,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
         }
 
         // Set up JNI
-        SDL.setupJNI();
+        SDL.setupJNI(getInitSubsystems());
 
         // Initialize state
         SDL.initialize();
@@ -467,22 +481,30 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
         mSingleton = this;
         SDL.setContext(this);
 
-        SDLControllerManager.initializeDeviceListener();
+        if (SDL.isControllerManagerReady()) {
+            SDLControllerManager.initializeDeviceListener();
+        }
 
-        mClipboardHandler = new SDLClipboardHandler();
+        if (SDL.isSubsystemCompiled(SDL.SDL_INIT_VIDEO)) {
+            mClipboardHandler = new SDLClipboardHandler();
+        }
 
-        mHIDDeviceManager = HIDDeviceManager.acquire(this);
+        if (nativeIsHIDAPIEnabled()) {
+            mHIDDeviceManager = HIDDeviceManager.acquire(this);
+        }
 
         // Set up the surface
-        mSurface = createSDLSurface(this);
+        if (SDL.isSubsystemInitialized(SDL.SDL_INIT_VIDEO)) {
+            mSurface = createSDLSurface(this);
 
-        mLayout = new RelativeLayout(this);
-        mLayout.addView(mSurface);
+            mLayout = new RelativeLayout(this);
+            mLayout.addView(mSurface);
 
-        // Get our current screen orientation and pass it down.
-        SDLActivity.nativeSetNaturalOrientation(SDLActivity.getNaturalOrientation());
-        mCurrentRotation = SDLActivity.getCurrentRotation();
-        SDLActivity.onNativeRotationChanged(mCurrentRotation);
+            // Get our current screen orientation and pass it down.
+            SDLActivity.nativeSetNaturalOrientation(SDLActivity.getNaturalOrientation());
+            mCurrentRotation = SDLActivity.getCurrentRotation();
+            SDLActivity.onNativeRotationChanged(mCurrentRotation);
+        }
 
         try {
             if (Build.VERSION.SDK_INT < 24 /* Android 7.0 (N) */) {
@@ -502,19 +524,22 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
             break;
         }
 
-        setContentView(mLayout);
-
-        setWindowStyle(false);
+        if (mLayout != null) {
+            setContentView(mLayout);
+            setWindowStyle(false);
+        }
 
         getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this);
 
         // Get filename from "Open with" of another application
-        Intent intent = getIntent();
-        if (intent != null && intent.getData() != null) {
-            String filename = intent.getData().getPath();
-            if (filename != null) {
-                Log.v(TAG, "Got filename: " + filename);
-                SDLActivity.onNativeDropFile(filename);
+        if (SDL.isSubsystemInitialized(SDL.SDL_INIT_VIDEO)) {
+            Intent intent = getIntent();
+            if (intent != null && intent.getData() != null) {
+                String filename = intent.getData().getPath();
+                if (filename != null) {
+                    Log.v(TAG, "Got filename: " + filename);
+                    SDLActivity.onNativeDropFile(filename);
+                }
             }
         }
     }
@@ -886,21 +911,27 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
 
         // Try a transition to resumed state
         if (mNextNativeState == NativeState.RESUMED) {
-            if (mSurface.mIsSurfaceReady && (mHasFocus || mHasMultiWindow) && mIsResumedCalled) {
+            boolean readyToRun = (mSurface == null) ? mIsResumedCalled
+                    : (mSurface.mIsSurfaceReady && (mHasFocus || mHasMultiWindow) && mIsResumedCalled);
+            if (readyToRun) {
                 if (mSDLThread == null) {
                     // This is the entry point to the C app.
                     // Start up the C app thread and enable sensor input for the first time
                     // FIXME: Why aren't we enabling sensor input at start?
 
                     mSDLThread = new Thread(new SDLMain(), "SDLThread");
-                    mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true);
+                    if (mSurface != null) {
+                        mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true);
+                    }
                     mSDLThread.start();
 
                     // No nativeResume(), don't signal Android_ResumeSem
                 } else {
                     nativeResume();
                 }
-                mSurface.handleResume();
+                if (mSurface != null) {
+                    mSurface.handleResume();
+                }
 
                 mCurrentNativeState = mNextNativeState;
             }
@@ -1058,7 +1089,8 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
                 DisplayMetrics realMetrics = new DisplayMetrics();
                 display.getRealMetrics(realMetrics);
 
-                boolean bFullscreenLayout = ((realMetrics.widthPixels == mSurface.getWidth()) &&
+                boolean bFullscreenLayout = (mSurface != null) &&
+                        ((realMetrics.widthPixels == mSurface.getWidth()) &&
                         (realMetrics.heightPixels == mSurface.getHeight()));
 
                 if ((Integer) data == 1) {
@@ -1102,6 +1134,8 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
     // C functions we call
     public static native String nativeGetVersion();
     public static native void nativeSetupJNI();
+    public static native int nativeGetCompiledSubsystems();
+    public static native boolean nativeIsHIDAPIEnabled();
     public static native void nativeInitMainThread();
     public static native void nativeCleanupMainThread();
     public static native int nativeRunMain(String library, String function, Object arguments);
@@ -1486,6 +1520,10 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
 
         @Override
         public void run() {
+            if (mLayout == null) {
+                return;
+            }
+
             RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(w, h + HEIGHT_PADDING);
             params.leftMargin = x;
             params.topMargin = y;
@@ -1515,6 +1553,10 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
      * This method is called by SDL using JNI.
      */
     public static boolean showTextInput(int input_type, int x, int y, int w, int h) {
+        if (mLayout == null) {
+            return false;
+        }
+
         // Transfer the task to the main thread as a Runnable
         return mSingleton.commandHandler.post(new ShowTextInputTask(input_type, x, y, w, h));
     }
@@ -1553,7 +1595,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
         // Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
         // SOURCE_JOYSTICK, while its key events arrive from the keyboard source
         // So, retrieve the device itself and check all of its sources
-        if (SDLControllerManager.isDeviceSDLJoystick(device)) {
+        if (SDL.isControllerManagerReady() && SDLControllerManager.isDeviceSDLJoystick(device)) {
             // Note that we process events with specific key codes here
             if (event.getAction() == KeyEvent.ACTION_DOWN) {
                 if (SDLControllerManager.onNativePadDown(deviceId, keyCode, event.getScanCode())) {

+ 25 - 11
android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java

@@ -60,7 +60,7 @@ public class SDLControllerManager
             mJoystickHandler = new SDLJoystickHandler();
         }
 
-        if (mHapticHandler == null) {
+        if (mHapticHandler == null && SDL.isSubsystemCompiled(SDL.SDL_INIT_HAPTIC)) {
             if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
                 mHapticHandler = new SDLHapticHandler_API31();
             } else if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) {
@@ -90,7 +90,7 @@ public class SDLControllerManager
 
     // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
     static public boolean handleJoystickMotionEvent(MotionEvent event) {
-        return mJoystickHandler.handleMotionEvent(event);
+        return mJoystickHandler != null && mJoystickHandler.handleMotionEvent(event);
     }
 
     /**
@@ -118,21 +118,27 @@ public class SDLControllerManager
      * This method is called by SDL using JNI.
      */
     static void detectHapticDevices() {
-        mHapticHandler.detectHapticDevices();
+        if (mHapticHandler != null) {
+            mHapticHandler.detectHapticDevices();
+        }
     }
 
     /**
      * This method is called by SDL using JNI.
      */
     static void hapticRun(int device_id, float intensity, int length) {
-        mHapticHandler.run(device_id, intensity, length);
+        if (mHapticHandler != null) {
+            mHapticHandler.run(device_id, intensity, length);
+        }
     }
 
     /**
      * This method is called by SDL using JNI.
      */
     static void hapticRumble(int device_id, float low_frequency_intensity, float high_frequency_intensity, int length) {
-        mHapticHandler.rumble(device_id, low_frequency_intensity, high_frequency_intensity, length);
+        if (mHapticHandler != null) {
+            mHapticHandler.rumble(device_id, low_frequency_intensity, high_frequency_intensity, length);
+        }
     }
 
     /**
@@ -140,7 +146,9 @@ public class SDLControllerManager
      */
     static void hapticStop(int device_id)
     {
-        mHapticHandler.stop(device_id);
+        if (mHapticHandler != null) {
+            mHapticHandler.stop(device_id);
+        }
     }
 
     // Check if a given device is considered a possible SDL joystick
@@ -977,10 +985,13 @@ class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
         }
 
         if (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */) {
-            if (enabled) {
-                SDLActivity.getContentView().requestPointerCapture();
-            } else {
-                SDLActivity.getContentView().releasePointerCapture();
+            View contentView = SDLActivity.getContentView();
+            if (contentView != null) {
+                if (enabled) {
+                    contentView.requestPointerCapture();
+                } else {
+                    contentView.releasePointerCapture();
+                }
             }
             mRelativeModeEnabled = enabled;
             return true;
@@ -998,7 +1009,10 @@ class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
         }
 
         if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) {
-            SDLActivity.getContentView().requestPointerCapture();
+            View contentView = SDLActivity.getContentView();
+            if (contentView != null) {
+                contentView.requestPointerCapture();
+            }
         }
     }
 

Разница между файлами не показана из-за своего большого размера
+ 376 - 453
src/core/android/SDL_android.c


+ 41 - 51
src/core/android/SDL_android.h

@@ -30,17 +30,17 @@ extern "C" {
 /* *INDENT-ON* */
 #endif
 
-#ifndef SDL_VIDEO_DISABLED
-#include <EGL/eglplatform.h>
-#include <android/native_window_jni.h>
-#endif
-
 #ifndef SDL_AUDIO_DISABLED
+// Audio support
 #include "../../audio/SDL_sysaudio.h"
 
 // this appears to be broken right now (on Android, not SDL, I think...?).
 #define ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES 0
-#endif
+
+void Android_StartAudioHotplug(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording);
+void Android_StopAudioHotplug(void);
+extern void Android_AudioThreadInit(SDL_AudioDevice *device);
+#endif // !SDL_AUDIO_DISABLED
 
 // Life cycle
 typedef enum
@@ -62,6 +62,9 @@ void Android_UnlockActivityMutex(void);
 void Android_SetAllowRecreateActivity(bool enabled);
 
 #ifndef SDL_VIDEO_DISABLED
+#include <EGL/eglplatform.h>
+#include <android/native_window_jni.h>
+
 // Interface from the SDL library into the Android Java activity
 extern void Android_JNI_SetActivityTitle(const char *title);
 extern void Android_JNI_SetWindowStyle(bool fullscreen);
@@ -71,24 +74,34 @@ extern bool Android_JNI_ShouldMinimizeOnFocusLoss(void);
 
 extern void Android_JNI_ShowScreenKeyboard(int input_type, SDL_Rect *inputRect);
 extern void Android_JNI_HideScreenKeyboard(void);
+bool Android_JNI_SuspendScreenSaver(bool suspend);
 extern ANativeWindow *Android_JNI_GetNativeWindow(void);
 
-#endif // !SDL_VIDEO_DISABLED
-
-// Kept outside the video guard for the camera driver; stays SDL_ORIENTATION_UNKNOWN when video is disabled (only the video Java layer updates it).
 extern SDL_DisplayOrientation Android_JNI_GetDisplayNaturalOrientation(void);
+// Also used by the camera subsystem, which is safe because camera requires video.
 extern SDL_DisplayOrientation Android_JNI_GetDisplayCurrentOrientation(void);
 
-#ifndef SDL_AUDIO_DISABLED
-// Audio support
-void Android_StartAudioHotplug(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording);
-void Android_StopAudioHotplug(void);
-extern void Android_AudioThreadInit(SDL_AudioDevice *device);
-#endif // !SDL_AUDIO_DISABLED
+// Clipboard support
+bool Android_JNI_SetClipboardText(const char *text);
+char *Android_JNI_GetClipboardText(void);
+bool Android_JNI_HasClipboardText(void);
 
-// Detecting device type
-extern bool Android_IsDeXMode(void);
-extern bool Android_IsChromebook(void);
+// Touch support
+void Android_JNI_InitTouch(void);
+
+// Cursor support
+int Android_JNI_CreateCustomCursor(SDL_Surface *surface, int hot_x, int hot_y);
+void Android_JNI_DestroyCustomCursor(int cursorID);
+bool Android_JNI_SetCustomCursor(int cursorID);
+bool Android_JNI_SetSystemCursor(int cursorID);
+
+// Relative mouse support
+bool Android_JNI_SupportsRelativeMouse(void);
+bool Android_JNI_SetRelativeMouseEnabled(bool enabled);
+
+// MessageBox
+bool Android_JNI_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID);
+#endif // !SDL_VIDEO_DISABLED
 
 bool Android_JNI_FileOpen(void **puserdata, const char *fileName, const char *mode);
 Sint64 Android_JNI_FileSize(void *userdata);
@@ -103,34 +116,25 @@ bool Android_JNI_GetAssetPathInfo(const char *path, SDL_PathInfo *info);
 void Android_JNI_GetManifestEnvironmentVariables(void);
 int Android_JNI_OpenFileDescriptor(const char *uri, const char *mode);
 
-#ifndef SDL_VIDEO_DISABLED
-// Clipboard support
-bool Android_JNI_SetClipboardText(const char *text);
-char *Android_JNI_GetClipboardText(void);
-bool Android_JNI_HasClipboardText(void);
-#endif // !SDL_VIDEO_DISABLED
-
+#ifndef SDL_POWER_DISABLED
 // Power support
 int Android_JNI_GetPowerInfo(int *plugged, int *charged, int *battery, int *seconds, int *percent);
+#endif // !SDL_POWER_DISABLED
 
+#ifndef SDL_JOYSTICK_DISABLED
 // Joystick support
 void Android_JNI_DetectDevices(void);
 void Android_JNI_JoystickSetLED(int device_id, int red, int green, int blue);
 void Android_JNI_JoystickSetSensorsEnabled(int device_id, bool enabled);
+#endif // !SDL_JOYSTICK_DISABLED
 
+#ifndef SDL_HAPTIC_DISABLED
 // Haptic support
 void Android_JNI_DetectHapticDevices(void);
 void Android_JNI_HapticRun(int device_id, float intensity, int length);
 void Android_JNI_HapticRumble(int device_id, float low_frequency_intensity, float high_frequency_intensity, int length);
 void Android_JNI_HapticStop(int device_id);
-
-// Video
-bool Android_JNI_SuspendScreenSaver(bool suspend);
-
-#ifndef SDL_VIDEO_DISABLED
-// Touch support
-void Android_JNI_InitTouch(void);
-#endif // !SDL_VIDEO_DISABLED
+#endif // !SDL_HAPTIC_DISABLED
 
 // Threads
 #include <jni.h>
@@ -143,21 +147,6 @@ bool Android_JNI_GetLocale(char *buf, size_t buflen);
 // Generic messages
 bool Android_JNI_SendMessage(int command, int param);
 
-// MessageBox
-bool Android_JNI_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID);
-
-#ifndef SDL_VIDEO_DISABLED
-// Cursor support
-int Android_JNI_CreateCustomCursor(SDL_Surface *surface, int hot_x, int hot_y);
-void Android_JNI_DestroyCustomCursor(int cursorID);
-bool Android_JNI_SetCustomCursor(int cursorID);
-bool Android_JNI_SetSystemCursor(int cursorID);
-
-// Relative mouse support
-bool Android_JNI_SupportsRelativeMouse(void);
-bool Android_JNI_SetRelativeMouseEnabled(bool enabled);
-#endif // !SDL_VIDEO_DISABLED
-
 // Show toast notification
 bool Android_JNI_ShowToast(const char *message, int duration, int gravity, int xOffset, int yOffset);
 
@@ -171,10 +160,11 @@ SDL_FormFactor SDL_GetAndroidDeviceFormFactor(void);
 
 char *SDL_GetAndroidPackageName(void);  // this is a SDL_malloc'd string the caller will own.
 
+#ifndef SDL_DIALOG_DISABLED
 // File Dialogs
-bool Android_JNI_ShowFileDialog(SDL_DialogFileCallback callback, void *userdata,
-    const SDL_DialogFileFilter *filters, int nfilters, SDL_FileDialogType type,
-    bool multiple, const char *initialPath);
+bool Android_JNI_ShowFileDialog(SDL_DialogFileCallback callback, void *userdata, const SDL_DialogFileFilter *filters,
+    int nfilters, SDL_FileDialogType type, bool multiple, const char *initialPath);
+#endif // !SDL_DIALOG_DISABLED
 
 // Ends C function definitions when using C++
 #ifdef __cplusplus

+ 4 - 0
src/joystick/android/SDL_sysjoystick.c

@@ -675,6 +675,7 @@ static bool ANDROID_JoystickOpen(SDL_Joystick *joystick, int device_index)
 
 static bool ANDROID_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
 {
+#ifndef SDL_HAPTIC_DISABLED
     SDL_joylist_item *item = (SDL_joylist_item *)joystick->hwdata;
     if (!item) {
         return SDL_SetError("Rumble failed, device disconnected");
@@ -687,6 +688,9 @@ static bool ANDROID_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_
     float high_frequency_intensity = (float)high_frequency_rumble / SDL_MAX_UINT16;
     Android_JNI_HapticRumble(item->device_id, low_frequency_intensity, high_frequency_intensity, 5000);
     return true;
+#else
+    return SDL_Unsupported();
+#endif
 }
 
 static bool ANDROID_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)

Некоторые файлы не были показаны из-за большого количества измененных файлов