From 5a079a2d114f96d4847d1ee305d5b7c16eeec50e Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 27 Dec 2025 12:03:39 -0800 Subject: Initial commit --- contrib/SDL-3.2.8/src/events/SDL_mouse.c | 1673 ++++++++++++++++++++++++++++++ 1 file changed, 1673 insertions(+) create mode 100644 contrib/SDL-3.2.8/src/events/SDL_mouse.c (limited to 'contrib/SDL-3.2.8/src/events/SDL_mouse.c') diff --git a/contrib/SDL-3.2.8/src/events/SDL_mouse.c b/contrib/SDL-3.2.8/src/events/SDL_mouse.c new file mode 100644 index 0000000..2ea5995 --- /dev/null +++ b/contrib/SDL-3.2.8/src/events/SDL_mouse.c @@ -0,0 +1,1673 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +// General mouse handling code for SDL + +#include "../SDL_hints_c.h" +#include "../video/SDL_sysvideo.h" +#include "SDL_events_c.h" +#include "SDL_mouse_c.h" +#if defined(SDL_PLATFORM_WINDOWS) +#include "../core/windows/SDL_windows.h" // For GetDoubleClickTime() +#endif + +// #define DEBUG_MOUSE + +#define WARP_EMULATION_THRESHOLD_NS SDL_MS_TO_NS(30) + +typedef struct SDL_MouseInstance +{ + SDL_MouseID instance_id; + char *name; +} SDL_MouseInstance; + +// The mouse state +static SDL_Mouse SDL_mouse; +static int SDL_mouse_count; +static SDL_MouseInstance *SDL_mice; + +// for mapping mouse events to touch +static bool track_mouse_down = false; + +static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, bool relative, float x, float y); + +static void SDLCALL SDL_MouseDoubleClickTimeChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_Mouse *mouse = (SDL_Mouse *)userdata; + + if (hint && *hint) { + mouse->double_click_time = SDL_atoi(hint); + } else { +#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) + mouse->double_click_time = GetDoubleClickTime(); +#else + mouse->double_click_time = 500; +#endif + } +} + +static void SDLCALL SDL_MouseDoubleClickRadiusChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_Mouse *mouse = (SDL_Mouse *)userdata; + + if (hint && *hint) { + mouse->double_click_radius = SDL_atoi(hint); + } else { + mouse->double_click_radius = 32; // 32 pixels seems about right for touch interfaces + } +} + +static void SDLCALL SDL_MouseNormalSpeedScaleChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_Mouse *mouse = (SDL_Mouse *)userdata; + + if (hint && *hint) { + mouse->enable_normal_speed_scale = true; + mouse->normal_speed_scale = (float)SDL_atof(hint); + } else { + mouse->enable_normal_speed_scale = false; + mouse->normal_speed_scale = 1.0f; + } +} + +static void SDLCALL SDL_MouseRelativeSpeedScaleChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_Mouse *mouse = (SDL_Mouse *)userdata; + + if (hint && *hint) { + mouse->enable_relative_speed_scale = true; + mouse->relative_speed_scale = (float)SDL_atof(hint); + } else { + mouse->enable_relative_speed_scale = false; + mouse->relative_speed_scale = 1.0f; + } +} + +static void SDLCALL SDL_MouseRelativeModeCenterChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_Mouse *mouse = (SDL_Mouse *)userdata; + + mouse->relative_mode_center = SDL_GetStringBoolean(hint, true); +} + +static void SDLCALL SDL_MouseRelativeSystemScaleChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_Mouse *mouse = (SDL_Mouse *)userdata; + + mouse->enable_relative_system_scale = SDL_GetStringBoolean(hint, false); +} + +static void SDLCALL SDL_MouseWarpEmulationChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_Mouse *mouse = (SDL_Mouse *)userdata; + + mouse->warp_emulation_hint = SDL_GetStringBoolean(hint, true); + + if (!mouse->warp_emulation_hint && mouse->warp_emulation_active) { + SDL_SetRelativeMouseMode(false); + mouse->warp_emulation_active = false; + } +} + +static void SDLCALL SDL_TouchMouseEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_Mouse *mouse = (SDL_Mouse *)userdata; + + mouse->touch_mouse_events = SDL_GetStringBoolean(hint, true); +} + +#ifdef SDL_PLATFORM_VITA +static void SDLCALL SDL_VitaTouchMouseDeviceChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_Mouse *mouse = (SDL_Mouse *)userdata; + if (hint) { + switch (*hint) { + default: + case '0': + mouse->vita_touch_mouse_device = 1; + break; + case '1': + mouse->vita_touch_mouse_device = 2; + break; + case '2': + mouse->vita_touch_mouse_device = 3; + break; + } + } +} +#endif + +static void SDLCALL SDL_MouseTouchEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_Mouse *mouse = (SDL_Mouse *)userdata; + bool default_value; + +#if defined(SDL_PLATFORM_ANDROID) || (defined(SDL_PLATFORM_IOS) && !defined(SDL_PLATFORM_TVOS)) + default_value = true; +#else + default_value = false; +#endif + mouse->mouse_touch_events = SDL_GetStringBoolean(hint, default_value); + + if (mouse->mouse_touch_events) { + if (!mouse->added_mouse_touch_device) { + SDL_AddTouch(SDL_MOUSE_TOUCHID, SDL_TOUCH_DEVICE_DIRECT, "mouse_input"); + mouse->added_mouse_touch_device = true; + } + } else { + if (mouse->added_mouse_touch_device) { + SDL_DelTouch(SDL_MOUSE_TOUCHID); + mouse->added_mouse_touch_device = false; + } + } +} + +static void SDLCALL SDL_PenMouseEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_Mouse *mouse = (SDL_Mouse *)userdata; + + mouse->pen_mouse_events = SDL_GetStringBoolean(hint, true); +} + +static void SDLCALL SDL_PenTouchEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_Mouse *mouse = (SDL_Mouse *)userdata; + + mouse->pen_touch_events = SDL_GetStringBoolean(hint, true); + + if (mouse->pen_touch_events) { + if (!mouse->added_pen_touch_device) { + SDL_AddTouch(SDL_PEN_TOUCHID, SDL_TOUCH_DEVICE_DIRECT, "pen_input"); + mouse->added_pen_touch_device = true; + } + } else { + if (mouse->added_pen_touch_device) { + SDL_DelTouch(SDL_PEN_TOUCHID); + mouse->added_pen_touch_device = false; + } + } +} + +static void SDLCALL SDL_MouseAutoCaptureChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_Mouse *mouse = (SDL_Mouse *)userdata; + bool auto_capture = SDL_GetStringBoolean(hint, true); + + if (auto_capture != mouse->auto_capture) { + mouse->auto_capture = auto_capture; + SDL_UpdateMouseCapture(false); + } +} + +static void SDLCALL SDL_MouseRelativeWarpMotionChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_Mouse *mouse = (SDL_Mouse *)userdata; + + mouse->relative_mode_warp_motion = SDL_GetStringBoolean(hint, false); +} + +static void SDLCALL SDL_MouseRelativeCursorVisibleChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_Mouse *mouse = (SDL_Mouse *)userdata; + + mouse->relative_mode_cursor_visible = SDL_GetStringBoolean(hint, false); + + SDL_SetCursor(NULL); // Update cursor visibility +} + +// Public functions +bool SDL_PreInitMouse(void) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + SDL_zerop(mouse); + + SDL_AddHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_TIME, + SDL_MouseDoubleClickTimeChanged, mouse); + + SDL_AddHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS, + SDL_MouseDoubleClickRadiusChanged, mouse); + + SDL_AddHintCallback(SDL_HINT_MOUSE_NORMAL_SPEED_SCALE, + SDL_MouseNormalSpeedScaleChanged, mouse); + + SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE, + SDL_MouseRelativeSpeedScaleChanged, mouse); + + SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE, + SDL_MouseRelativeSystemScaleChanged, mouse); + + SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, + SDL_MouseRelativeModeCenterChanged, mouse); + + SDL_AddHintCallback(SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE, + SDL_MouseWarpEmulationChanged, mouse); + + SDL_AddHintCallback(SDL_HINT_TOUCH_MOUSE_EVENTS, + SDL_TouchMouseEventsChanged, mouse); + +#ifdef SDL_PLATFORM_VITA + SDL_AddHintCallback(SDL_HINT_VITA_TOUCH_MOUSE_DEVICE, + SDL_VitaTouchMouseDeviceChanged, mouse); +#endif + + SDL_AddHintCallback(SDL_HINT_MOUSE_TOUCH_EVENTS, + SDL_MouseTouchEventsChanged, mouse); + + SDL_AddHintCallback(SDL_HINT_PEN_MOUSE_EVENTS, + SDL_PenMouseEventsChanged, mouse); + + SDL_AddHintCallback(SDL_HINT_PEN_TOUCH_EVENTS, + SDL_PenTouchEventsChanged, mouse); + + SDL_AddHintCallback(SDL_HINT_MOUSE_AUTO_CAPTURE, + SDL_MouseAutoCaptureChanged, mouse); + + SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_WARP_MOTION, + SDL_MouseRelativeWarpMotionChanged, mouse); + + SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE, + SDL_MouseRelativeCursorVisibleChanged, mouse); + + mouse->was_touch_mouse_events = false; // no touch to mouse movement event pending + + mouse->cursor_shown = true; + + return true; +} + +void SDL_PostInitMouse(void) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + /* Create a dummy mouse cursor for video backends that don't support true cursors, + * so that mouse grab and focus functionality will work. + */ + if (!mouse->def_cursor) { + SDL_Surface *surface = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_ARGB8888); + if (surface) { + SDL_memset(surface->pixels, 0, (size_t)surface->h * surface->pitch); + SDL_SetDefaultCursor(SDL_CreateColorCursor(surface, 0, 0)); + SDL_DestroySurface(surface); + } + } +} + +bool SDL_IsMouse(Uint16 vendor, Uint16 product) +{ + // Eventually we'll have a blacklist of devices that enumerate as mice but aren't really + return true; +} + +static int SDL_GetMouseIndex(SDL_MouseID mouseID) +{ + for (int i = 0; i < SDL_mouse_count; ++i) { + if (mouseID == SDL_mice[i].instance_id) { + return i; + } + } + return -1; +} + +void SDL_AddMouse(SDL_MouseID mouseID, const char *name, bool send_event) +{ + int mouse_index = SDL_GetMouseIndex(mouseID); + if (mouse_index >= 0) { + // We already know about this mouse + return; + } + + SDL_assert(mouseID != 0); + + SDL_MouseInstance *mice = (SDL_MouseInstance *)SDL_realloc(SDL_mice, (SDL_mouse_count + 1) * sizeof(*mice)); + if (!mice) { + return; + } + SDL_MouseInstance *instance = &mice[SDL_mouse_count]; + instance->instance_id = mouseID; + instance->name = SDL_strdup(name ? name : ""); + SDL_mice = mice; + ++SDL_mouse_count; + + if (send_event) { + SDL_Event event; + SDL_zero(event); + event.type = SDL_EVENT_MOUSE_ADDED; + event.mdevice.which = mouseID; + SDL_PushEvent(&event); + } +} + +void SDL_RemoveMouse(SDL_MouseID mouseID, bool send_event) +{ + int mouse_index = SDL_GetMouseIndex(mouseID); + if (mouse_index < 0) { + // We don't know about this mouse + return; + } + + SDL_free(SDL_mice[mouse_index].name); + + if (mouse_index != SDL_mouse_count - 1) { + SDL_memmove(&SDL_mice[mouse_index], &SDL_mice[mouse_index + 1], (SDL_mouse_count - mouse_index - 1) * sizeof(SDL_mice[mouse_index])); + } + --SDL_mouse_count; + + // Remove any mouse input sources for this mouseID + SDL_Mouse *mouse = SDL_GetMouse(); + for (int i = 0; i < mouse->num_sources; ++i) { + SDL_MouseInputSource *source = &mouse->sources[i]; + if (source->mouseID == mouseID) { + SDL_free(source->clickstate); + if (i != mouse->num_sources - 1) { + SDL_memmove(&mouse->sources[i], &mouse->sources[i + 1], (mouse->num_sources - i - 1) * sizeof(mouse->sources[i])); + } + --mouse->num_sources; + break; + } + } + + if (send_event) { + SDL_Event event; + SDL_zero(event); + event.type = SDL_EVENT_MOUSE_REMOVED; + event.mdevice.which = mouseID; + SDL_PushEvent(&event); + } +} + +bool SDL_HasMouse(void) +{ + return (SDL_mouse_count > 0); +} + +SDL_MouseID *SDL_GetMice(int *count) +{ + int i; + SDL_MouseID *mice; + + mice = (SDL_JoystickID *)SDL_malloc((SDL_mouse_count + 1) * sizeof(*mice)); + if (mice) { + if (count) { + *count = SDL_mouse_count; + } + + for (i = 0; i < SDL_mouse_count; ++i) { + mice[i] = SDL_mice[i].instance_id; + } + mice[i] = 0; + } else { + if (count) { + *count = 0; + } + } + + return mice; +} + +const char *SDL_GetMouseNameForID(SDL_MouseID instance_id) +{ + int mouse_index = SDL_GetMouseIndex(instance_id); + if (mouse_index < 0) { + SDL_SetError("Mouse %" SDL_PRIu32 " not found", instance_id); + return NULL; + } + return SDL_GetPersistentString(SDL_mice[mouse_index].name); +} + +void SDL_SetDefaultCursor(SDL_Cursor *cursor) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + if (cursor == mouse->def_cursor) { + return; + } + + if (mouse->def_cursor) { + SDL_Cursor *default_cursor = mouse->def_cursor; + SDL_Cursor *prev, *curr; + + if (mouse->cur_cursor == mouse->def_cursor) { + mouse->cur_cursor = NULL; + } + mouse->def_cursor = NULL; + + for (prev = NULL, curr = mouse->cursors; curr; + prev = curr, curr = curr->next) { + if (curr == default_cursor) { + if (prev) { + prev->next = curr->next; + } else { + mouse->cursors = curr->next; + } + + break; + } + } + + if (mouse->FreeCursor && default_cursor->internal) { + mouse->FreeCursor(default_cursor); + } else { + SDL_free(default_cursor); + } + } + + mouse->def_cursor = cursor; + + if (!mouse->cur_cursor) { + SDL_SetCursor(cursor); + } +} + +SDL_SystemCursor SDL_GetDefaultSystemCursor(void) +{ + SDL_SystemCursor id = SDL_SYSTEM_CURSOR_DEFAULT; + const char *value = SDL_GetHint(SDL_HINT_MOUSE_DEFAULT_SYSTEM_CURSOR); + if (value) { + int index = SDL_atoi(value); + if (0 <= index && index < SDL_SYSTEM_CURSOR_COUNT) { + id = (SDL_SystemCursor)index; + } + } + return id; +} + +SDL_Mouse *SDL_GetMouse(void) +{ + return &SDL_mouse; +} + +static SDL_MouseButtonFlags SDL_GetMouseButtonState(SDL_Mouse *mouse, SDL_MouseID mouseID, bool include_touch) +{ + int i; + SDL_MouseButtonFlags buttonstate = 0; + + for (i = 0; i < mouse->num_sources; ++i) { + if (mouseID == SDL_GLOBAL_MOUSE_ID || mouseID == SDL_TOUCH_MOUSEID) { + if (include_touch || mouse->sources[i].mouseID != SDL_TOUCH_MOUSEID) { + buttonstate |= mouse->sources[i].buttonstate; + } + } else { + if (mouseID == mouse->sources[i].mouseID) { + buttonstate |= mouse->sources[i].buttonstate; + break; + } + } + } + return buttonstate; +} + +SDL_Window *SDL_GetMouseFocus(void) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + return mouse->focus; +} + +/* TODO RECONNECT: Hello from the Wayland video driver! + * This was once removed from SDL, but it's been added back in comment form + * because we will need it when Wayland adds compositor reconnect support. + * If you need this before we do, great! Otherwise, leave this alone, we'll + * uncomment it at the right time. + * -flibit + */ +#if 0 +void SDL_ResetMouse(void) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + Uint32 buttonState = SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, false); + int i; + + for (i = 1; i <= sizeof(buttonState)*8; ++i) { + if (buttonState & SDL_BUTTON_MASK(i)) { + SDL_SendMouseButton(0, mouse->focus, mouse->mouseID, i, false); + } + } + SDL_assert(SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, false) == 0); +} +#endif // 0 + +void SDL_SetMouseFocus(SDL_Window *window) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + if (mouse->focus == window) { + return; + } + + /* Actually, this ends up being a bad idea, because most operating + systems have an implicit grab when you press the mouse button down + so you can drag things out of the window and then get the mouse up + when it happens. So, #if 0... + */ +#if 0 + if (mouse->focus && !window) { + // We won't get anymore mouse messages, so reset mouse state + SDL_ResetMouse(); + } +#endif + + // See if the current window has lost focus + if (mouse->focus) { + SDL_SendWindowEvent(mouse->focus, SDL_EVENT_WINDOW_MOUSE_LEAVE, 0, 0); + } + + mouse->focus = window; + mouse->has_position = false; + + if (mouse->focus) { + SDL_SendWindowEvent(mouse->focus, SDL_EVENT_WINDOW_MOUSE_ENTER, 0, 0); + } + + // Update cursor visibility + SDL_SetCursor(NULL); +} + +bool SDL_MousePositionInWindow(SDL_Window *window, float x, float y) +{ + if (!window) { + return false; + } + + if (window && !(window->flags & SDL_WINDOW_MOUSE_CAPTURE)) { + if (x < 0.0f || y < 0.0f || x >= (float)window->w || y >= (float)window->h) { + return false; + } + } + return true; +} + +// Check to see if we need to synthesize focus events +static bool SDL_UpdateMouseFocus(SDL_Window *window, float x, float y, Uint32 buttonstate, bool send_mouse_motion) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + bool inWindow = SDL_MousePositionInWindow(window, x, y); + + if (!inWindow) { + if (window == mouse->focus) { +#ifdef DEBUG_MOUSE + SDL_Log("Mouse left window, synthesizing move & focus lost event"); +#endif + if (send_mouse_motion) { + SDL_PrivateSendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, x, y); + } + SDL_SetMouseFocus(NULL); + } + return false; + } + + if (window != mouse->focus) { +#ifdef DEBUG_MOUSE + SDL_Log("Mouse entered window, synthesizing focus gain & move event"); +#endif + SDL_SetMouseFocus(window); + if (send_mouse_motion) { + SDL_PrivateSendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, x, y); + } + } + return true; +} + +void SDL_SendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, bool relative, float x, float y) +{ + if (window && !relative) { + SDL_Mouse *mouse = SDL_GetMouse(); + if (!SDL_UpdateMouseFocus(window, x, y, SDL_GetMouseButtonState(mouse, mouseID, true), (mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID))) { + return; + } + } + + SDL_PrivateSendMouseMotion(timestamp, window, mouseID, relative, x, y); +} + +static void ConstrainMousePosition(SDL_Mouse *mouse, SDL_Window *window, float *x, float *y) +{ + /* make sure that the pointers find themselves inside the windows, + unless we have the mouse captured. */ + if (window && !(window->flags & SDL_WINDOW_MOUSE_CAPTURE)) { + int x_min = 0, x_max = window->w - 1; + int y_min = 0, y_max = window->h - 1; + const SDL_Rect *confine = SDL_GetWindowMouseRect(window); + + if (confine) { + SDL_Rect window_rect; + SDL_Rect mouse_rect; + + window_rect.x = 0; + window_rect.y = 0; + window_rect.w = x_max + 1; + window_rect.h = y_max + 1; + if (SDL_GetRectIntersection(confine, &window_rect, &mouse_rect)) { + x_min = mouse_rect.x; + y_min = mouse_rect.y; + x_max = x_min + mouse_rect.w - 1; + y_max = y_min + mouse_rect.h - 1; + } + } + + if (*x >= (float)(x_max + 1)) { + *x = SDL_max((float)x_max, mouse->last_x); + } + if (*x < (float)x_min) { + *x = (float)x_min; + } + + if (*y >= (float)(y_max + 1)) { + *y = SDL_max((float)y_max, mouse->last_y); + } + if (*y < (float)y_min) { + *y = (float)y_min; + } + } +} + +static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, bool relative, float x, float y) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + float xrel = 0.0f; + float yrel = 0.0f; + bool window_is_relative = mouse->focus && (mouse->focus->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE); + + // SDL_HINT_MOUSE_TOUCH_EVENTS: controlling whether mouse events should generate synthetic touch events + if (mouse->mouse_touch_events) { + if (mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID && !relative && track_mouse_down) { + if (window) { + float normalized_x = x / (float)window->w; + float normalized_y = y / (float)window->h; + SDL_SendTouchMotion(timestamp, SDL_MOUSE_TOUCHID, SDL_BUTTON_LEFT, window, normalized_x, normalized_y, 1.0f); + } + } + } + + // SDL_HINT_TOUCH_MOUSE_EVENTS: if not set, discard synthetic mouse events coming from platform layer + if (!mouse->touch_mouse_events && mouseID == SDL_TOUCH_MOUSEID) { + return; + } + + if (relative) { + if (mouse->relative_mode) { + if (mouse->enable_relative_system_scale) { + if (mouse->ApplySystemScale) { + mouse->ApplySystemScale(mouse->system_scale_data, timestamp, window, mouseID, &x, &y); + } + } + if (mouse->enable_relative_speed_scale) { + x *= mouse->relative_speed_scale; + y *= mouse->relative_speed_scale; + } + } else { + if (mouse->enable_normal_speed_scale) { + x *= mouse->normal_speed_scale; + y *= mouse->normal_speed_scale; + } + } + xrel = x; + yrel = y; + x = (mouse->last_x + xrel); + y = (mouse->last_y + yrel); + ConstrainMousePosition(mouse, window, &x, &y); + } else { + ConstrainMousePosition(mouse, window, &x, &y); + if (mouse->has_position) { + xrel = x - mouse->last_x; + yrel = y - mouse->last_y; + } + } + + if (mouse->has_position && xrel == 0.0f && yrel == 0.0f) { // Drop events that don't change state +#ifdef DEBUG_MOUSE + SDL_Log("Mouse event didn't change state - dropped!"); +#endif + return; + } + + // Ignore relative motion positioning the first touch + if (mouseID == SDL_TOUCH_MOUSEID && !SDL_GetMouseButtonState(mouse, mouseID, true)) { + xrel = 0.0f; + yrel = 0.0f; + } + + // modify internal state + { + mouse->x_accu += xrel; + mouse->y_accu += yrel; + + if (relative && mouse->has_position) { + mouse->x += xrel; + mouse->y += yrel; + ConstrainMousePosition(mouse, window, &mouse->x, &mouse->y); + } else { + mouse->x = x; + mouse->y = y; + } + mouse->has_position = true; + + // Use unclamped values if we're getting events outside the window + mouse->last_x = relative ? mouse->x : x; + mouse->last_y = relative ? mouse->y : y; + + mouse->click_motion_x += xrel; + mouse->click_motion_y += yrel; + } + + // Move the mouse cursor, if needed + if (mouse->cursor_shown && !mouse->relative_mode && + mouse->MoveCursor && mouse->cur_cursor) { + mouse->MoveCursor(mouse->cur_cursor); + } + + // Post the event, if desired + if (SDL_EventEnabled(SDL_EVENT_MOUSE_MOTION)) { + if ((!mouse->relative_mode || mouse->warp_emulation_active) && mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID) { + // We're not in relative mode, so all mouse events are global mouse events + mouseID = SDL_GLOBAL_MOUSE_ID; + } + + if (!relative && window_is_relative) { + if (!mouse->relative_mode_warp_motion) { + return; + } + xrel = 0.0f; + yrel = 0.0f; + } + + SDL_Event event; + event.type = SDL_EVENT_MOUSE_MOTION; + event.common.timestamp = timestamp; + event.motion.windowID = mouse->focus ? mouse->focus->id : 0; + event.motion.which = mouseID; + // Set us pending (or clear during a normal mouse movement event) as having triggered + mouse->was_touch_mouse_events = (mouseID == SDL_TOUCH_MOUSEID); + event.motion.state = SDL_GetMouseButtonState(mouse, mouseID, true); + event.motion.x = mouse->x; + event.motion.y = mouse->y; + event.motion.xrel = xrel; + event.motion.yrel = yrel; + SDL_PushEvent(&event); + } +} + +static SDL_MouseInputSource *GetMouseInputSource(SDL_Mouse *mouse, SDL_MouseID mouseID, bool down, Uint8 button) +{ + SDL_MouseInputSource *source, *match = NULL, *sources; + int i; + + for (i = 0; i < mouse->num_sources; ++i) { + source = &mouse->sources[i]; + if (source->mouseID == mouseID) { + match = source; + break; + } + } + + if (!down && (!match || !(match->buttonstate & SDL_BUTTON_MASK(button)))) { + /* This might be a button release from a transition between mouse messages and raw input. + * See if there's another mouse source that already has that button down and use that. + */ + for (i = 0; i < mouse->num_sources; ++i) { + source = &mouse->sources[i]; + if ((source->buttonstate & SDL_BUTTON_MASK(button))) { + match = source; + break; + } + } + } + if (match) { + return match; + } + + sources = (SDL_MouseInputSource *)SDL_realloc(mouse->sources, (mouse->num_sources + 1) * sizeof(*mouse->sources)); + if (sources) { + mouse->sources = sources; + ++mouse->num_sources; + source = &sources[mouse->num_sources - 1]; + SDL_zerop(source); + source->mouseID = mouseID; + return source; + } + return NULL; +} + +static SDL_MouseClickState *GetMouseClickState(SDL_MouseInputSource *source, Uint8 button) +{ + if (button >= source->num_clickstates) { + int i, count = button + 1; + SDL_MouseClickState *clickstate = (SDL_MouseClickState *)SDL_realloc(source->clickstate, count * sizeof(*source->clickstate)); + if (!clickstate) { + return NULL; + } + source->clickstate = clickstate; + + for (i = source->num_clickstates; i < count; ++i) { + SDL_zero(source->clickstate[i]); + } + source->num_clickstates = count; + } + return &source->clickstate[button]; +} + +static void SDL_PrivateSendMouseButton(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, Uint8 button, bool down, int clicks) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + SDL_EventType type; + Uint32 buttonstate; + SDL_MouseInputSource *source; + + source = GetMouseInputSource(mouse, mouseID, down, button); + if (!source) { + return; + } + buttonstate = source->buttonstate; + + // SDL_HINT_MOUSE_TOUCH_EVENTS: controlling whether mouse events should generate synthetic touch events + if (mouse->mouse_touch_events) { + if (mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID && button == SDL_BUTTON_LEFT) { + if (down) { + track_mouse_down = true; + } else { + track_mouse_down = false; + } + if (window) { + type = track_mouse_down ? SDL_EVENT_FINGER_DOWN : SDL_EVENT_FINGER_UP; + float normalized_x = mouse->x / (float)window->w; + float normalized_y = mouse->y / (float)window->h; + SDL_SendTouch(timestamp, SDL_MOUSE_TOUCHID, SDL_BUTTON_LEFT, window, type, normalized_x, normalized_y, 1.0f); + } + } + } + + // SDL_HINT_TOUCH_MOUSE_EVENTS: if not set, discard synthetic mouse events coming from platform layer + if (mouse->touch_mouse_events == 0) { + if (mouseID == SDL_TOUCH_MOUSEID) { + return; + } + } + + // Figure out which event to perform + if (down) { + type = SDL_EVENT_MOUSE_BUTTON_DOWN; + buttonstate |= SDL_BUTTON_MASK(button); + } else { + type = SDL_EVENT_MOUSE_BUTTON_UP; + buttonstate &= ~SDL_BUTTON_MASK(button); + } + + // We do this after calculating buttonstate so button presses gain focus + if (window && down) { + SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate, true); + } + + if (buttonstate == source->buttonstate) { + // Ignore this event, no state change + return; + } + source->buttonstate = buttonstate; + + if (clicks < 0) { + SDL_MouseClickState *clickstate = GetMouseClickState(source, button); + if (clickstate) { + if (down) { + Uint64 now = SDL_GetTicks(); + + if (now >= (clickstate->last_timestamp + mouse->double_click_time) || + SDL_fabs(mouse->click_motion_x - clickstate->click_motion_x) > mouse->double_click_radius || + SDL_fabs(mouse->click_motion_y - clickstate->click_motion_y) > mouse->double_click_radius) { + clickstate->click_count = 0; + } + clickstate->last_timestamp = now; + clickstate->click_motion_x = mouse->click_motion_x; + clickstate->click_motion_y = mouse->click_motion_y; + if (clickstate->click_count < 255) { + ++clickstate->click_count; + } + } + clicks = clickstate->click_count; + } else { + clicks = 1; + } + } + + // Post the event, if desired + if (SDL_EventEnabled(type)) { + if ((!mouse->relative_mode || mouse->warp_emulation_active) && mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID) { + // We're not in relative mode, so all mouse events are global mouse events + mouseID = SDL_GLOBAL_MOUSE_ID; + } else { + mouseID = source->mouseID; + } + + SDL_Event event; + event.type = type; + event.common.timestamp = timestamp; + event.button.windowID = mouse->focus ? mouse->focus->id : 0; + event.button.which = mouseID; + event.button.down = down; + event.button.button = button; + event.button.clicks = (Uint8)SDL_min(clicks, 255); + event.button.x = mouse->x; + event.button.y = mouse->y; + SDL_PushEvent(&event); + } + + // We do this after dispatching event so button releases can lose focus + if (window && !down) { + SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate, true); + } + + // Automatically capture the mouse while buttons are pressed + if (mouse->auto_capture) { + SDL_UpdateMouseCapture(false); + } +} + +void SDL_SendMouseButtonClicks(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, Uint8 button, bool down, int clicks) +{ + clicks = SDL_max(clicks, 0); + SDL_PrivateSendMouseButton(timestamp, window, mouseID, button, down, clicks); +} + +void SDL_SendMouseButton(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, Uint8 button, bool down) +{ + SDL_PrivateSendMouseButton(timestamp, window, mouseID, button, down, -1); +} + +void SDL_SendMouseWheel(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, float x, float y, SDL_MouseWheelDirection direction) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + if (window) { + SDL_SetMouseFocus(window); + } + + if (x == 0.0f && y == 0.0f) { + return; + } + + // Post the event, if desired + if (SDL_EventEnabled(SDL_EVENT_MOUSE_WHEEL)) { + if (!mouse->relative_mode || mouse->warp_emulation_active) { + // We're not in relative mode, so all mouse events are global mouse events + mouseID = SDL_GLOBAL_MOUSE_ID; + } + + SDL_Event event; + event.type = SDL_EVENT_MOUSE_WHEEL; + event.common.timestamp = timestamp; + event.wheel.windowID = mouse->focus ? mouse->focus->id : 0; + event.wheel.which = mouseID; + event.wheel.x = x; + event.wheel.y = y; + event.wheel.direction = direction; + event.wheel.mouse_x = mouse->x; + event.wheel.mouse_y = mouse->y; + SDL_PushEvent(&event); + } +} + +void SDL_QuitMouse(void) +{ + SDL_Cursor *cursor, *next; + SDL_Mouse *mouse = SDL_GetMouse(); + + if (mouse->added_mouse_touch_device) { + SDL_DelTouch(SDL_MOUSE_TOUCHID); + } + + if (mouse->added_pen_touch_device) { + SDL_DelTouch(SDL_PEN_TOUCHID); + } + + if (mouse->CaptureMouse) { + SDL_CaptureMouse(false); + SDL_UpdateMouseCapture(true); + } + SDL_SetRelativeMouseMode(false); + SDL_ShowCursor(); + + if (mouse->def_cursor) { + SDL_SetDefaultCursor(NULL); + } + + cursor = mouse->cursors; + while (cursor) { + next = cursor->next; + SDL_DestroyCursor(cursor); + cursor = next; + } + mouse->cursors = NULL; + mouse->cur_cursor = NULL; + + if (mouse->sources) { + for (int i = 0; i < mouse->num_sources; ++i) { + SDL_MouseInputSource *source = &mouse->sources[i]; + SDL_free(source->clickstate); + } + SDL_free(mouse->sources); + mouse->sources = NULL; + } + mouse->num_sources = 0; + + SDL_RemoveHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_TIME, + SDL_MouseDoubleClickTimeChanged, mouse); + + SDL_RemoveHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS, + SDL_MouseDoubleClickRadiusChanged, mouse); + + SDL_RemoveHintCallback(SDL_HINT_MOUSE_NORMAL_SPEED_SCALE, + SDL_MouseNormalSpeedScaleChanged, mouse); + + SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE, + SDL_MouseRelativeSpeedScaleChanged, mouse); + + SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE, + SDL_MouseRelativeSystemScaleChanged, mouse); + + SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, + SDL_MouseRelativeModeCenterChanged, mouse); + + SDL_RemoveHintCallback(SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE, + SDL_MouseWarpEmulationChanged, mouse); + + SDL_RemoveHintCallback(SDL_HINT_TOUCH_MOUSE_EVENTS, + SDL_TouchMouseEventsChanged, mouse); + + SDL_RemoveHintCallback(SDL_HINT_MOUSE_TOUCH_EVENTS, + SDL_MouseTouchEventsChanged, mouse); + + SDL_RemoveHintCallback(SDL_HINT_PEN_MOUSE_EVENTS, + SDL_PenMouseEventsChanged, mouse); + + SDL_RemoveHintCallback(SDL_HINT_PEN_TOUCH_EVENTS, + SDL_PenTouchEventsChanged, mouse); + + SDL_RemoveHintCallback(SDL_HINT_MOUSE_AUTO_CAPTURE, + SDL_MouseAutoCaptureChanged, mouse); + + SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_WARP_MOTION, + SDL_MouseRelativeWarpMotionChanged, mouse); + + SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE, + SDL_MouseRelativeCursorVisibleChanged, mouse); + + for (int i = SDL_mouse_count; i--; ) { + SDL_RemoveMouse(SDL_mice[i].instance_id, false); + } + SDL_free(SDL_mice); + SDL_mice = NULL; +} + +SDL_MouseButtonFlags SDL_GetMouseState(float *x, float *y) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + if (x) { + *x = mouse->x; + } + if (y) { + *y = mouse->y; + } + return SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, true); +} + +SDL_MouseButtonFlags SDL_GetRelativeMouseState(float *x, float *y) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + if (x) { + *x = mouse->x_accu; + } + if (y) { + *y = mouse->y_accu; + } + mouse->x_accu = 0.0f; + mouse->y_accu = 0.0f; + return SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, true); +} + +SDL_MouseButtonFlags SDL_GetGlobalMouseState(float *x, float *y) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + if (mouse->GetGlobalMouseState) { + float tmpx, tmpy; + + // make sure these are never NULL for the backend implementations... + if (!x) { + x = &tmpx; + } + if (!y) { + y = &tmpy; + } + + *x = *y = 0.0f; + + return mouse->GetGlobalMouseState(x, y); + } else { + return SDL_GetMouseState(x, y); + } +} + +void SDL_PerformWarpMouseInWindow(SDL_Window *window, float x, float y, bool ignore_relative_mode) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + if (!window) { + window = mouse->focus; + } + + if (!window) { + return; + } + + if ((window->flags & SDL_WINDOW_MINIMIZED) == SDL_WINDOW_MINIMIZED) { + return; + } + + // Ignore the previous position when we warp + mouse->last_x = x; + mouse->last_y = y; + mouse->has_position = false; + + if (mouse->relative_mode && !ignore_relative_mode) { + /* 2.0.22 made warping in relative mode actually functional, which + * surprised many applications that weren't expecting the additional + * mouse motion. + * + * So for now, warping in relative mode adjusts the absolution position + * but doesn't generate motion events, unless SDL_HINT_MOUSE_RELATIVE_WARP_MOTION is set. + */ + if (!mouse->relative_mode_warp_motion) { + mouse->x = x; + mouse->y = y; + mouse->has_position = true; + return; + } + } + + if (mouse->WarpMouse && !mouse->relative_mode) { + mouse->WarpMouse(window, x, y); + } else { + SDL_PrivateSendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, x, y); + } +} + +void SDL_DisableMouseWarpEmulation(void) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + if (mouse->warp_emulation_active) { + SDL_SetRelativeMouseMode(false); + } + + mouse->warp_emulation_prohibited = true; +} + +static void SDL_MaybeEnableWarpEmulation(SDL_Window *window, float x, float y) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + if (!mouse->warp_emulation_prohibited && mouse->warp_emulation_hint && !mouse->cursor_shown && !mouse->warp_emulation_active) { + if (!window) { + window = mouse->focus; + } + + if (window) { + const float cx = window->w / 2.f; + const float cy = window->h / 2.f; + if (x >= SDL_floorf(cx) && x <= SDL_ceilf(cx) && + y >= SDL_floorf(cy) && y <= SDL_ceilf(cy)) { + + // Require two consecutive warps to the center within a certain timespan to enter warp emulation mode. + const Uint64 now = SDL_GetTicksNS(); + if (now - mouse->last_center_warp_time_ns < WARP_EMULATION_THRESHOLD_NS) { + if (SDL_SetRelativeMouseMode(true)) { + mouse->warp_emulation_active = true; + } + } + + mouse->last_center_warp_time_ns = now; + return; + } + } + + mouse->last_center_warp_time_ns = 0; + } +} + +void SDL_WarpMouseInWindow(SDL_Window *window, float x, float y) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + SDL_MaybeEnableWarpEmulation(window, x, y); + + SDL_PerformWarpMouseInWindow(window, x, y, mouse->warp_emulation_active); +} + +bool SDL_WarpMouseGlobal(float x, float y) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + if (mouse->WarpMouseGlobal) { + return mouse->WarpMouseGlobal(x, y); + } + + return SDL_Unsupported(); +} + +bool SDL_SetRelativeMouseMode(bool enabled) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + SDL_Window *focusWindow = SDL_GetKeyboardFocus(); + + if (!enabled) { + // If warps were being emulated, reset the flag. + mouse->warp_emulation_active = false; + } + + if (enabled == mouse->relative_mode) { + return true; + } + + // Set the relative mode + if (!mouse->SetRelativeMouseMode || !mouse->SetRelativeMouseMode(enabled)) { + if (enabled) { + return SDL_SetError("No relative mode implementation available"); + } + } + mouse->relative_mode = enabled; + + if (enabled) { + // Update cursor visibility before we potentially warp the mouse + SDL_SetCursor(NULL); + } + + if (enabled && focusWindow) { + SDL_SetMouseFocus(focusWindow); + } + + if (focusWindow) { + SDL_UpdateWindowGrab(focusWindow); + + // Put the cursor back to where the application expects it + if (!enabled) { + SDL_PerformWarpMouseInWindow(focusWindow, mouse->x, mouse->y, true); + } + + SDL_UpdateMouseCapture(false); + } + + if (!enabled) { + // Update cursor visibility after we restore the mouse position + SDL_SetCursor(NULL); + } + + // Flush pending mouse motion - ideally we would pump events, but that's not always safe + SDL_FlushEvent(SDL_EVENT_MOUSE_MOTION); + + return true; +} + +bool SDL_GetRelativeMouseMode(void) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + return mouse->relative_mode; +} + +void SDL_UpdateRelativeMouseMode(void) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + SDL_Window *focus = SDL_GetKeyboardFocus(); + bool relative_mode = (focus && (focus->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE)); + + if (relative_mode != mouse->relative_mode) { + SDL_SetRelativeMouseMode(relative_mode); + } +} + +bool SDL_UpdateMouseCapture(bool force_release) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + SDL_Window *capture_window = NULL; + + if (!mouse->CaptureMouse) { + return true; + } + + if (!force_release) { + if (SDL_GetMessageBoxCount() == 0 && + (mouse->capture_desired || (mouse->auto_capture && SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, false) != 0))) { + if (!mouse->relative_mode) { + capture_window = mouse->focus; + } + } + } + + if (capture_window != mouse->capture_window) { + /* We can get here recursively on Windows, so make sure we complete + * all of the window state operations before we change the capture state + * (e.g. https://github.com/libsdl-org/SDL/pull/5608) + */ + SDL_Window *previous_capture = mouse->capture_window; + + if (previous_capture) { + previous_capture->flags &= ~SDL_WINDOW_MOUSE_CAPTURE; + } + + if (capture_window) { + capture_window->flags |= SDL_WINDOW_MOUSE_CAPTURE; + } + + mouse->capture_window = capture_window; + + if (!mouse->CaptureMouse(capture_window)) { + // CaptureMouse() will have set an error, just restore the state + if (previous_capture) { + previous_capture->flags |= SDL_WINDOW_MOUSE_CAPTURE; + } + if (capture_window) { + capture_window->flags &= ~SDL_WINDOW_MOUSE_CAPTURE; + } + mouse->capture_window = previous_capture; + + return false; + } + } + return true; +} + +bool SDL_CaptureMouse(bool enabled) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + if (!mouse->CaptureMouse) { + return SDL_Unsupported(); + } + +#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) + /* Windows mouse capture is tied to the current thread, and must be called + * from the thread that created the window being captured. Since we update + * the mouse capture state from the event processing, any application state + * changes must be processed on that thread as well. + */ + if (!SDL_OnVideoThread()) { + return SDL_SetError("SDL_CaptureMouse() must be called on the main thread"); + } +#endif // defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) + + if (enabled && SDL_GetKeyboardFocus() == NULL) { + return SDL_SetError("No window has focus"); + } + mouse->capture_desired = enabled; + + return SDL_UpdateMouseCapture(false); +} + +SDL_Cursor *SDL_CreateCursor(const Uint8 *data, const Uint8 *mask, int w, int h, int hot_x, int hot_y) +{ + SDL_Surface *surface; + SDL_Cursor *cursor; + int x, y; + Uint32 *pixel; + Uint8 datab = 0, maskb = 0; + const Uint32 black = 0xFF000000; + const Uint32 white = 0xFFFFFFFF; + const Uint32 transparent = 0x00000000; +#if defined(SDL_PLATFORM_WIN32) + // Only Windows backend supports inverted pixels in mono cursors. + const Uint32 inverted = 0x00FFFFFF; +#else + const Uint32 inverted = 0xFF000000; +#endif // defined(SDL_PLATFORM_WIN32) + + // Make sure the width is a multiple of 8 + w = ((w + 7) & ~7); + + // Create the surface from a bitmap + surface = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_ARGB8888); + if (!surface) { + return NULL; + } + for (y = 0; y < h; ++y) { + pixel = (Uint32 *)((Uint8 *)surface->pixels + y * surface->pitch); + for (x = 0; x < w; ++x) { + if ((x % 8) == 0) { + datab = *data++; + maskb = *mask++; + } + if (maskb & 0x80) { + *pixel++ = (datab & 0x80) ? black : white; + } else { + *pixel++ = (datab & 0x80) ? inverted : transparent; + } + datab <<= 1; + maskb <<= 1; + } + } + + cursor = SDL_CreateColorCursor(surface, hot_x, hot_y); + + SDL_DestroySurface(surface); + + return cursor; +} + +SDL_Cursor *SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + SDL_Surface *temp = NULL; + SDL_Cursor *cursor; + + if (!surface) { + SDL_InvalidParamError("surface"); + return NULL; + } + + // Allow specifying the hot spot via properties on the surface + SDL_PropertiesID props = SDL_GetSurfaceProperties(surface); + hot_x = (int)SDL_GetNumberProperty(props, SDL_PROP_SURFACE_HOTSPOT_X_NUMBER, hot_x); + hot_y = (int)SDL_GetNumberProperty(props, SDL_PROP_SURFACE_HOTSPOT_Y_NUMBER, hot_y); + + // Sanity check the hot spot + if ((hot_x < 0) || (hot_y < 0) || + (hot_x >= surface->w) || (hot_y >= surface->h)) { + SDL_SetError("Cursor hot spot doesn't lie within cursor"); + return NULL; + } + + if (surface->format != SDL_PIXELFORMAT_ARGB8888) { + temp = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888); + if (!temp) { + return NULL; + } + surface = temp; + } + + if (mouse->CreateCursor) { + cursor = mouse->CreateCursor(surface, hot_x, hot_y); + } else { + cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor)); + } + if (cursor) { + cursor->next = mouse->cursors; + mouse->cursors = cursor; + } + + SDL_DestroySurface(temp); + + return cursor; +} + +SDL_Cursor *SDL_CreateSystemCursor(SDL_SystemCursor id) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + SDL_Cursor *cursor; + + if (!mouse->CreateSystemCursor) { + SDL_SetError("CreateSystemCursor is not currently supported"); + return NULL; + } + + cursor = mouse->CreateSystemCursor(id); + if (cursor) { + cursor->next = mouse->cursors; + mouse->cursors = cursor; + } + + return cursor; +} + +/* SDL_SetCursor(NULL) can be used to force the cursor redraw, + if this is desired for any reason. This is used when setting + the video mode and when the SDL window gains the mouse focus. + */ +bool SDL_SetCursor(SDL_Cursor *cursor) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + // Return immediately if setting the cursor to the currently set one (fixes #7151) + if (cursor == mouse->cur_cursor) { + return true; + } + + // Set the new cursor + if (cursor) { + // Make sure the cursor is still valid for this mouse + if (cursor != mouse->def_cursor) { + SDL_Cursor *found; + for (found = mouse->cursors; found; found = found->next) { + if (found == cursor) { + break; + } + } + if (!found) { + return SDL_SetError("Cursor not associated with the current mouse"); + } + } + mouse->cur_cursor = cursor; + } else { + if (mouse->focus) { + cursor = mouse->cur_cursor; + } else { + cursor = mouse->def_cursor; + } + } + + if (cursor && (!mouse->focus || (mouse->cursor_shown && (!mouse->relative_mode || mouse->relative_mode_cursor_visible)))) { + if (mouse->ShowCursor) { + mouse->ShowCursor(cursor); + } + } else { + if (mouse->ShowCursor) { + mouse->ShowCursor(NULL); + } + } + return true; +} + +SDL_Cursor *SDL_GetCursor(void) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + if (!mouse) { + return NULL; + } + return mouse->cur_cursor; +} + +SDL_Cursor *SDL_GetDefaultCursor(void) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + if (!mouse) { + return NULL; + } + return mouse->def_cursor; +} + +void SDL_DestroyCursor(SDL_Cursor *cursor) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + SDL_Cursor *curr, *prev; + + if (!cursor) { + return; + } + + if (cursor == mouse->def_cursor) { + return; + } + if (cursor == mouse->cur_cursor) { + SDL_SetCursor(mouse->def_cursor); + } + + for (prev = NULL, curr = mouse->cursors; curr; + prev = curr, curr = curr->next) { + if (curr == cursor) { + if (prev) { + prev->next = curr->next; + } else { + mouse->cursors = curr->next; + } + + if (mouse->FreeCursor && curr->internal) { + mouse->FreeCursor(curr); + } else { + SDL_free(curr); + } + return; + } + } +} + +bool SDL_ShowCursor(void) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + if (mouse->warp_emulation_active) { + SDL_SetRelativeMouseMode(false); + mouse->warp_emulation_active = false; + } + + if (!mouse->cursor_shown) { + mouse->cursor_shown = true; + SDL_SetCursor(NULL); + } + return true; +} + +bool SDL_HideCursor(void) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + if (mouse->cursor_shown) { + mouse->cursor_shown = false; + SDL_SetCursor(NULL); + } + return true; +} + +bool SDL_CursorVisible(void) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + return mouse->cursor_shown; +} -- cgit v1.2.3