| | |
| | | /* |
| | | Simple DirectMedia Layer |
| | | Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org> |
| | | Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> |
| | | |
| | | This software is provided 'as-is', without any express or implied |
| | | warranty. In no event will the authors be held liable for any damages |
| | |
| | | |
| | | #include "../../core/android/SDL_android.h" |
| | | |
| | | /* See Android's MotionEvent class for constants */ |
| | | #define ACTION_DOWN 0 |
| | | #define ACTION_UP 1 |
| | | #define ACTION_MOVE 2 |
| | |
| | | #define BUTTON_BACK 8 |
| | | #define BUTTON_FORWARD 16 |
| | | |
| | | static Uint8 SDLButton; |
| | | typedef struct |
| | | { |
| | | int custom_cursor; |
| | | int system_cursor; |
| | | |
| | | } SDL_AndroidCursorData; |
| | | |
| | | /* Last known Android mouse button state (includes all buttons) */ |
| | | static int last_state; |
| | | |
| | | /* Blank cursor */ |
| | | static SDL_Cursor *empty_cursor; |
| | | |
| | | static SDL_Cursor * |
| | | Android_WrapCursor(int custom_cursor, int system_cursor) |
| | | { |
| | | SDL_Cursor *cursor; |
| | | |
| | | cursor = SDL_calloc(1, sizeof(*cursor)); |
| | | if (cursor) { |
| | | SDL_AndroidCursorData *data = (SDL_AndroidCursorData *)SDL_calloc(1, sizeof(*data)); |
| | | if (data) { |
| | | data->custom_cursor = custom_cursor; |
| | | data->system_cursor = system_cursor; |
| | | cursor->driverdata = data; |
| | | } else { |
| | | SDL_free(cursor); |
| | | cursor = NULL; |
| | | SDL_OutOfMemory(); |
| | | } |
| | | } else { |
| | | SDL_OutOfMemory(); |
| | | } |
| | | |
| | | return cursor; |
| | | } |
| | | |
| | | static SDL_Cursor * |
| | | Android_CreateDefaultCursor() |
| | | { |
| | | return Android_WrapCursor(0, SDL_SYSTEM_CURSOR_ARROW); |
| | | } |
| | | |
| | | static SDL_Cursor * |
| | | Android_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y) |
| | | { |
| | | int custom_cursor; |
| | | SDL_Surface *converted; |
| | | |
| | | converted = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0); |
| | | if (!converted) { |
| | | return NULL; |
| | | } |
| | | custom_cursor = Android_JNI_CreateCustomCursor(converted, hot_x, hot_y); |
| | | SDL_FreeSurface(converted); |
| | | if (!custom_cursor) { |
| | | SDL_Unsupported(); |
| | | return NULL; |
| | | } |
| | | return Android_WrapCursor(custom_cursor, 0); |
| | | } |
| | | |
| | | static SDL_Cursor * |
| | | Android_CreateSystemCursor(SDL_SystemCursor id) |
| | | { |
| | | return Android_WrapCursor(0, id); |
| | | } |
| | | |
| | | static void |
| | | Android_FreeCursor(SDL_Cursor * cursor) |
| | | { |
| | | SDL_free(cursor->driverdata); |
| | | SDL_free(cursor); |
| | | } |
| | | |
| | | static SDL_Cursor * |
| | | Android_CreateEmptyCursor() |
| | | { |
| | | if (!empty_cursor) { |
| | | SDL_Surface *empty_surface = SDL_CreateRGBSurfaceWithFormat(0, 1, 1, 32, SDL_PIXELFORMAT_ARGB8888); |
| | | if (empty_surface) { |
| | | SDL_memset(empty_surface->pixels, 0, empty_surface->h * empty_surface->pitch); |
| | | empty_cursor = Android_CreateCursor(empty_surface, 0, 0); |
| | | SDL_FreeSurface(empty_surface); |
| | | } |
| | | } |
| | | return empty_cursor; |
| | | } |
| | | |
| | | static void |
| | | Android_DestroyEmptyCursor() |
| | | { |
| | | if (empty_cursor) { |
| | | Android_FreeCursor(empty_cursor); |
| | | empty_cursor = NULL; |
| | | } |
| | | } |
| | | |
| | | static int |
| | | Android_ShowCursor(SDL_Cursor *cursor) |
| | | { |
| | | if (!cursor) { |
| | | cursor = Android_CreateEmptyCursor(); |
| | | } |
| | | if (cursor) { |
| | | SDL_AndroidCursorData *data = (SDL_AndroidCursorData *)cursor->driverdata; |
| | | if (data->custom_cursor) { |
| | | if (!Android_JNI_SetCustomCursor(data->custom_cursor)) { |
| | | return SDL_Unsupported(); |
| | | } |
| | | } else { |
| | | if (!Android_JNI_SetSystemCursor(data->system_cursor)) { |
| | | return SDL_Unsupported(); |
| | | } |
| | | } |
| | | return 0; |
| | | } else { |
| | | /* SDL error set inside Android_CreateEmptyCursor() */ |
| | | return -1; |
| | | } |
| | | } |
| | | |
| | | static int |
| | | Android_SetRelativeMouseMode(SDL_bool enabled) |
| | | { |
| | | if (!Android_JNI_SupportsRelativeMouse()) { |
| | | return SDL_Unsupported(); |
| | | } |
| | | |
| | | if (!Android_JNI_SetRelativeMouseEnabled(enabled)) { |
| | | return SDL_Unsupported(); |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | void |
| | | Android_InitMouse(void) |
| | | { |
| | | SDLButton = 0; |
| | | SDL_Mouse *mouse = SDL_GetMouse(); |
| | | |
| | | mouse->CreateCursor = Android_CreateCursor; |
| | | mouse->CreateSystemCursor = Android_CreateSystemCursor; |
| | | mouse->ShowCursor = Android_ShowCursor; |
| | | mouse->FreeCursor = Android_FreeCursor; |
| | | mouse->SetRelativeMouseMode = Android_SetRelativeMouseMode; |
| | | |
| | | SDL_SetDefaultCursor(Android_CreateDefaultCursor()); |
| | | |
| | | last_state = 0; |
| | | } |
| | | |
| | | void Android_OnMouse( int androidButton, int action, float x, float y) { |
| | | if (!Android_Window) { |
| | | void |
| | | Android_QuitMouse(void) |
| | | { |
| | | Android_DestroyEmptyCursor(); |
| | | } |
| | | |
| | | /* Translate Android mouse button state to SDL mouse button */ |
| | | static Uint8 |
| | | TranslateButton(int state) |
| | | { |
| | | if (state & BUTTON_PRIMARY) { |
| | | return SDL_BUTTON_LEFT; |
| | | } else if (state & BUTTON_SECONDARY) { |
| | | return SDL_BUTTON_RIGHT; |
| | | } else if (state & BUTTON_TERTIARY) { |
| | | return SDL_BUTTON_MIDDLE; |
| | | } else if (state & BUTTON_FORWARD) { |
| | | return SDL_BUTTON_X1; |
| | | } else if (state & BUTTON_BACK) { |
| | | return SDL_BUTTON_X2; |
| | | } else { |
| | | return 0; |
| | | } |
| | | } |
| | | |
| | | void |
| | | Android_OnMouse(SDL_Window *window, int state, int action, float x, float y, SDL_bool relative) |
| | | { |
| | | int changes; |
| | | Uint8 button; |
| | | |
| | | if (!window) { |
| | | return; |
| | | } |
| | | |
| | | switch(action) { |
| | | case ACTION_DOWN: |
| | | // Determine which button originated the event, and store it for ACTION_UP |
| | | SDLButton = SDL_BUTTON_LEFT; |
| | | if (androidButton == BUTTON_SECONDARY) { |
| | | SDLButton = SDL_BUTTON_RIGHT; |
| | | } else if (androidButton == BUTTON_TERTIARY) { |
| | | SDLButton = SDL_BUTTON_MIDDLE; |
| | | } else if (androidButton == BUTTON_FORWARD) { |
| | | SDLButton = SDL_BUTTON_X1; |
| | | } else if (androidButton == BUTTON_BACK) { |
| | | SDLButton = SDL_BUTTON_X2; |
| | | } |
| | | SDL_SendMouseMotion(Android_Window, 0, 0, x, y); |
| | | SDL_SendMouseButton(Android_Window, 0, SDL_PRESSED, SDLButton); |
| | | changes = state & ~last_state; |
| | | button = TranslateButton(changes); |
| | | last_state = state; |
| | | SDL_SendMouseMotion(window, 0, relative, (int)x, (int)y); |
| | | SDL_SendMouseButton(window, 0, SDL_PRESSED, button); |
| | | break; |
| | | |
| | | case ACTION_UP: |
| | | // Android won't give us the button that originated the ACTION_DOWN event, so we'll |
| | | // assume it's the one we stored |
| | | SDL_SendMouseMotion(Android_Window, 0, 0, x, y); |
| | | SDL_SendMouseButton(Android_Window, 0, SDL_RELEASED, SDLButton); |
| | | changes = last_state & ~state; |
| | | button = TranslateButton(changes); |
| | | last_state = state; |
| | | SDL_SendMouseMotion(window, 0, relative, (int)x, (int)y); |
| | | SDL_SendMouseButton(window, 0, SDL_RELEASED, button); |
| | | break; |
| | | |
| | | case ACTION_MOVE: |
| | | case ACTION_HOVER_MOVE: |
| | | SDL_SendMouseMotion(Android_Window, 0, 0, x, y); |
| | | SDL_SendMouseMotion(window, 0, relative, (int)x, (int)y); |
| | | break; |
| | | |
| | | case ACTION_SCROLL: |
| | | SDL_SendMouseWheel(Android_Window, 0, x, y, SDL_MOUSEWHEEL_NORMAL); |
| | | SDL_SendMouseWheel(window, 0, x, y, SDL_MOUSEWHEEL_NORMAL); |
| | | break; |
| | | |
| | | default: |