Reimplemented Android cursor API support using reflection so it builds with older SDKs
authorSam Lantinga <slouken@libsdl.org>
Fri, 16 Mar 2018 11:08:53 -0700
changeset 11932a05a909eeddb
parent 11931 d479188e0155
child 11933 ea7df1e9a723
Reimplemented Android cursor API support using reflection so it builds with older SDKs
android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
src/core/android/SDL_android.c
src/core/android/SDL_android.h
src/video/android/SDL_androidmouse.c
     1.1 --- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java	Thu Mar 15 18:22:48 2018 -0700
     1.2 +++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java	Fri Mar 16 11:08:53 2018 -0700
     1.3 @@ -77,10 +77,8 @@
     1.4      protected static boolean mScreenKeyboardShown;
     1.5      protected static ViewGroup mLayout;
     1.6      protected static SDLClipboardHandler mClipboardHandler;
     1.7 -    //#CURSORIMPLEENTATION
     1.8 -    //protected static Hashtable<Integer, PointerIcon> mCursors;
     1.9 -    //protected static int mLastCursorID;
    1.10 -    //protected static PointerIcon mActiveCursor;
    1.11 +    protected static Hashtable<Integer, Object> mCursors;
    1.12 +    protected static int mLastCursorID;
    1.13  
    1.14  
    1.15      // This is what SDL runs in. It invokes SDL_main(), eventually
    1.16 @@ -153,10 +151,8 @@
    1.17          mTextEdit = null;
    1.18          mLayout = null;
    1.19          mClipboardHandler = null;
    1.20 -        //#CURSORIMPLEENTATION
    1.21 -        //mCursors = new Hashtable<Integer, PointerIcon>();
    1.22 -        //mLastCursorID = 0;
    1.23 -        //mActiveCursor = null;
    1.24 +        mCursors = new Hashtable<Integer, Object>();
    1.25 +        mLastCursorID = 0;
    1.26          mSDLThread = null;
    1.27          mExitCalledFromJava = false;
    1.28          mBrokenLibraries = false;
    1.29 @@ -1093,69 +1089,91 @@
    1.30      /**
    1.31       * This method is called by SDL using JNI.
    1.32       */
    1.33 -    /**
    1.34 -     * #CURSORIMPLEENTATION
    1.35 -     * The cursor implementation requires API 24 or above
    1.36 -     *
    1.37      public static int createCustomCursor(int[] colors, int width, int height, int hotSpotX, int hotSpotY) {
    1.38          Bitmap bitmap = Bitmap.createBitmap(colors, width, height, Bitmap.Config.ARGB_8888);
    1.39          ++mLastCursorID;
    1.40 -        mCursors.put(mLastCursorID, PointerIcon.create(bitmap, hotSpotX, hotSpotY));
    1.41 +        // This requires API 24, so use reflection to implement this
    1.42 +        try {
    1.43 +            Class PointerIconClass = Class.forName("android.view.PointerIcon");
    1.44 +            Class[] arg_types = new Class[] { Bitmap.class, float.class, float.class };
    1.45 +            Method create = PointerIconClass.getMethod("create", arg_types);
    1.46 +            mCursors.put(mLastCursorID, create.invoke(null, bitmap, hotSpotX, hotSpotY));
    1.47 +        } catch (Exception e) {
    1.48 +            return 0;
    1.49 +        }
    1.50          return mLastCursorID;
    1.51      }
    1.52  
    1.53 -    public static void setCustomCursor(int cursorID) {
    1.54 -        mActiveCursor = mCursors.get(cursorID);
    1.55 +    /**
    1.56 +     * This method is called by SDL using JNI.
    1.57 +     */
    1.58 +    public static boolean setCustomCursor(int cursorID) {
    1.59 +        // This requires API 24, so use reflection to implement this
    1.60 +        try {
    1.61 +            Class PointerIconClass = Class.forName("android.view.PointerIcon");
    1.62 +            Method setPointerIcon = SDLSurface.class.getMethod("setPointerIcon", PointerIconClass);
    1.63 +            setPointerIcon.invoke(mSurface, mCursors.get(cursorID));
    1.64 +        } catch (Exception e) {
    1.65 +            return false;
    1.66 +        }
    1.67 +        return true;
    1.68      }
    1.69  
    1.70 -    public static void setSystemCursor(int cursorID) {
    1.71 +    /**
    1.72 +     * This method is called by SDL using JNI.
    1.73 +     */
    1.74 +    public static boolean setSystemCursor(int cursorID) {
    1.75 +        int cursor_type = 0; //PointerIcon.TYPE_NULL;
    1.76          switch (cursorID) {
    1.77 -        case SDL_SYSTEM_CURSOR_NONE:
    1.78 -            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_NULL);
    1.79 -            break;
    1.80          case SDL_SYSTEM_CURSOR_ARROW:
    1.81 -            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_ARROW);
    1.82 +            cursor_type = 1000; //PointerIcon.TYPE_ARROW;
    1.83              break;
    1.84          case SDL_SYSTEM_CURSOR_IBEAM:
    1.85 -            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_TEXT);
    1.86 +            cursor_type = 1008; //PointerIcon.TYPE_TEXT;
    1.87              break;
    1.88          case SDL_SYSTEM_CURSOR_WAIT:
    1.89 -            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_WAIT);
    1.90 +            cursor_type = 1004; //PointerIcon.TYPE_WAIT;
    1.91              break;
    1.92          case SDL_SYSTEM_CURSOR_CROSSHAIR:
    1.93 -            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_CROSSHAIR);
    1.94 +            cursor_type = 1007; //PointerIcon.TYPE_CROSSHAIR;
    1.95              break;
    1.96          case SDL_SYSTEM_CURSOR_WAITARROW:
    1.97 -            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_WAIT);
    1.98 +            cursor_type = 1004; //PointerIcon.TYPE_WAIT;
    1.99              break;
   1.100          case SDL_SYSTEM_CURSOR_SIZENWSE:
   1.101 -            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW);
   1.102 +            cursor_type = 1017; //PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
   1.103              break;
   1.104          case SDL_SYSTEM_CURSOR_SIZENESW:
   1.105 -            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW);
   1.106 +            cursor_type = 1016; //PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
   1.107              break;
   1.108          case SDL_SYSTEM_CURSOR_SIZEWE:
   1.109 -            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW);
   1.110 +            cursor_type = 1014; //PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
   1.111              break;
   1.112          case SDL_SYSTEM_CURSOR_SIZENS:
   1.113 -            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW);
   1.114 +            cursor_type = 1015; //PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
   1.115              break;
   1.116          case SDL_SYSTEM_CURSOR_SIZEALL:
   1.117 -            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_GRAB);
   1.118 +            cursor_type = 1020; //PointerIcon.TYPE_GRAB;
   1.119              break;
   1.120          case SDL_SYSTEM_CURSOR_NO:
   1.121 -            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_NO_DROP);
   1.122 +            cursor_type = 1012; //PointerIcon.TYPE_NO_DROP;
   1.123              break;
   1.124          case SDL_SYSTEM_CURSOR_HAND:
   1.125 -            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_HAND);
   1.126 +            cursor_type = 1002; //PointerIcon.TYPE_HAND;
   1.127              break;
   1.128          }
   1.129 +        // This requires API 24, so use reflection to implement this
   1.130 +        try {
   1.131 +            Class PointerIconClass = Class.forName("android.view.PointerIcon");
   1.132 +            Class[] arg_types = new Class[] { Context.class, int.class };
   1.133 +            Method getSystemIcon = PointerIconClass.getMethod("getSystemIcon", arg_types);
   1.134 +            Method setPointerIcon = SDLSurface.class.getMethod("setPointerIcon", PointerIconClass);
   1.135 +            setPointerIcon.invoke(mSurface, getSystemIcon.invoke(null, SDL.getContext(), cursor_type));
   1.136 +        } catch (Exception e) {
   1.137 +            return false;
   1.138 +        }
   1.139 +        return true;
   1.140      }
   1.141 -
   1.142 -    public static PointerIcon getCursor() {
   1.143 -        return mActiveCursor;
   1.144 -    }
   1.145 -    */
   1.146  }
   1.147  
   1.148  /**
   1.149 @@ -1547,14 +1565,6 @@
   1.150                                        event.values[2] / SensorManager.GRAVITY_EARTH);
   1.151          }
   1.152      }
   1.153 -
   1.154 -    /**
   1.155 -     * #CURSORIMPLEENTATION
   1.156 -    @Override
   1.157 -    public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
   1.158 -        return SDLActivity.getCursor();
   1.159 -    }
   1.160 -    */
   1.161  }
   1.162  
   1.163  /* This is a fake invisible editor view that receives the input and defines the
     2.1 --- a/src/core/android/SDL_android.c	Thu Mar 15 18:22:48 2018 -0700
     2.2 +++ b/src/core/android/SDL_android.c	Fri Mar 16 11:08:53 2018 -0700
     2.3 @@ -335,17 +335,16 @@
     2.4                                  "getManifestEnvironmentVariables", "()Z");
     2.5  
     2.6      midGetDisplayDPI = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "getDisplayDPI", "()Landroid/util/DisplayMetrics;");
     2.7 -
     2.8 -    /* Custom cursor implementation is only available on API 24 and above */
     2.9      midCreateCustomCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "createCustomCursor", "([IIIII)I");
    2.10 -    midSetCustomCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setCustomCursor", "(I)V");
    2.11 -    midSetSystemCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setSystemCursor", "(I)V");
    2.12 +    midSetCustomCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setCustomCursor", "(I)Z");
    2.13 +    midSetSystemCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setSystemCursor", "(I)Z");
    2.14  
    2.15      if (!midGetNativeSurface ||
    2.16         !midSetActivityTitle || !midSetWindowStyle || !midSetOrientation || !midGetContext || !midIsAndroidTV || !midInputGetInputDeviceIds ||
    2.17         !midSendMessage || !midShowTextInput || !midIsScreenKeyboardShown ||
    2.18         !midClipboardSetText || !midClipboardGetText || !midClipboardHasText ||
    2.19 -       !midOpenAPKExpansionInputStream || !midGetManifestEnvironmentVariables|| !midGetDisplayDPI) {
    2.20 +       !midOpenAPKExpansionInputStream || !midGetManifestEnvironmentVariables || !midGetDisplayDPI ||
    2.21 +       !midCreateCustomCursor || !midSetCustomCursor || !midSetSystemCursor) {
    2.22          __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?");
    2.23      }
    2.24  
    2.25 @@ -2178,34 +2177,29 @@
    2.26  {
    2.27      JNIEnv *mEnv = Android_JNI_GetEnv();
    2.28      int custom_cursor = 0;
    2.29 -    if (midCreateCustomCursor) {
    2.30 -        jintArray pixels;
    2.31 -        pixels = (*mEnv)->NewIntArray(mEnv, surface->w * surface->h);
    2.32 -        if (!pixels) {
    2.33 -            return 0;
    2.34 -        }
    2.35 +    jintArray pixels;
    2.36 +    pixels = (*mEnv)->NewIntArray(mEnv, surface->w * surface->h);
    2.37 +    if (pixels) {
    2.38          (*mEnv)->SetIntArrayRegion(mEnv, pixels, 0, surface->w * surface->h, (int *)surface->pixels);
    2.39          custom_cursor = (*mEnv)->CallStaticIntMethod(mEnv, mActivityClass, midCreateCustomCursor, pixels, surface->w, surface->h, hot_x, hot_y);
    2.40          (*mEnv)->DeleteLocalRef(mEnv, pixels);
    2.41 +    } else {
    2.42 +        SDL_OutOfMemory();
    2.43      }
    2.44      return custom_cursor;
    2.45  }
    2.46  
    2.47  
    2.48 -void Android_JNI_SetCustomCursor(int cursorID)
    2.49 +SDL_bool Android_JNI_SetCustomCursor(int cursorID)
    2.50  {
    2.51      JNIEnv *mEnv = Android_JNI_GetEnv();
    2.52 -    if (midSetCustomCursor) {
    2.53 -        (*mEnv)->CallStaticVoidMethod(mEnv, mActivityClass, midSetCustomCursor, cursorID);
    2.54 -    }
    2.55 +    return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetCustomCursor, cursorID);
    2.56  }
    2.57  
    2.58 -void Android_JNI_SetSystemCursor(int cursorID)
    2.59 +SDL_bool Android_JNI_SetSystemCursor(int cursorID)
    2.60  {
    2.61      JNIEnv *mEnv = Android_JNI_GetEnv();
    2.62 -    if (midSetSystemCursor) {
    2.63 -        (*mEnv)->CallStaticVoidMethod(mEnv, mActivityClass, midSetSystemCursor, cursorID);
    2.64 -    }
    2.65 +    return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetSystemCursor, cursorID);
    2.66  }
    2.67  
    2.68  #endif /* __ANDROID__ */
     3.1 --- a/src/core/android/SDL_android.h	Thu Mar 15 18:22:48 2018 -0700
     3.2 +++ b/src/core/android/SDL_android.h	Fri Mar 16 11:08:53 2018 -0700
     3.3 @@ -104,8 +104,8 @@
     3.4  
     3.5  /* Cursor support */
     3.6  int Android_JNI_CreateCustomCursor(SDL_Surface *surface, int hot_x, int hot_y);
     3.7 -void Android_JNI_SetCustomCursor(int cursorID);
     3.8 -void Android_JNI_SetSystemCursor(int cursorID);
     3.9 +SDL_bool Android_JNI_SetCustomCursor(int cursorID);
    3.10 +SDL_bool Android_JNI_SetSystemCursor(int cursorID);
    3.11  
    3.12  /* Ends C function definitions when using C++ */
    3.13  #ifdef __cplusplus
     4.1 --- a/src/video/android/SDL_androidmouse.c	Thu Mar 15 18:22:48 2018 -0700
     4.2 +++ b/src/video/android/SDL_androidmouse.c	Fri Mar 16 11:08:53 2018 -0700
     4.3 @@ -121,12 +121,18 @@
     4.4      if (cursor) {
     4.5          SDL_AndroidCursorData *data = (SDL_AndroidCursorData*)cursor->driverdata;
     4.6          if (data->custom_cursor) {
     4.7 -            Android_JNI_SetCustomCursor(data->custom_cursor);
     4.8 +            if (!Android_JNI_SetCustomCursor(data->custom_cursor)) {
     4.9 +                return SDL_Unsupported();
    4.10 +            }
    4.11          } else {
    4.12 -            Android_JNI_SetSystemCursor(data->system_cursor);
    4.13 +            if (!Android_JNI_SetSystemCursor(data->system_cursor)) {
    4.14 +                return SDL_Unsupported();
    4.15 +            }
    4.16          }
    4.17      } else {
    4.18 -        Android_JNI_SetSystemCursor(-1);
    4.19 +        if (!Android_JNI_SetSystemCursor(-1)) {
    4.20 +            return SDL_Unsupported();
    4.21 +        }
    4.22      }
    4.23      return 0;
    4.24  }