| | |
| | | /* |
| | | Simple DirectMedia Layer |
| | | Copyright (C) 1997-2018 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 "SDL_timer.h" |
| | | #include "SDL_events.h" |
| | | #include "SDL_events_c.h" |
| | | #include "../SDL_hints_c.h" |
| | | #include "../video/SDL_sysvideo.h" |
| | | #ifdef __WIN32__ |
| | | #include "../core/windows/SDL_windows.h" // For GetDoubleClickTime() |
| | | #endif |
| | | |
| | | /* #define DEBUG_MOUSE */ |
| | | |
| | | /* The mouse state */ |
| | | static SDL_Mouse SDL_mouse; |
| | | |
| | | /* for mapping mouse events to touch */ |
| | | static SDL_bool track_mouse_down = SDL_FALSE; |
| | | |
| | | static int |
| | | SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y); |
| | |
| | | { |
| | | SDL_Mouse *mouse = (SDL_Mouse *)userdata; |
| | | |
| | | if (hint && (*hint == '0' || SDL_strcasecmp(hint, "false") == 0)) { |
| | | mouse->touch_mouse_events = SDL_FALSE; |
| | | } else { |
| | | mouse->touch_mouse_events = SDL_TRUE; |
| | | mouse->touch_mouse_events = SDL_GetStringBoolean(hint, SDL_TRUE); |
| | | } |
| | | |
| | | static void SDLCALL |
| | | SDL_MouseTouchEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) |
| | | { |
| | | SDL_Mouse *mouse = (SDL_Mouse *)userdata; |
| | | SDL_bool default_value; |
| | | |
| | | #if defined(__ANDROID__) || (defined(__IPHONEOS__) && !defined(__TVOS__)) |
| | | default_value = SDL_TRUE; |
| | | #else |
| | | default_value = SDL_FALSE; |
| | | #endif |
| | | mouse->mouse_touch_events = SDL_GetStringBoolean(hint, default_value); |
| | | |
| | | if (mouse->mouse_touch_events) { |
| | | SDL_AddTouch(SDL_MOUSE_TOUCHID, SDL_TOUCH_DEVICE_DIRECT, "mouse_input"); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | SDL_AddHintCallback(SDL_HINT_TOUCH_MOUSE_EVENTS, |
| | | SDL_TouchMouseEventsChanged, mouse); |
| | | |
| | | SDL_AddHintCallback(SDL_HINT_MOUSE_TOUCH_EVENTS, |
| | | SDL_MouseTouchEventsChanged, mouse); |
| | | |
| | | mouse->was_touch_mouse_events = SDL_FALSE; /* no touch to mouse movement event pending */ |
| | | |
| | | mouse->cursor_shown = SDL_TRUE; |
| | | |
| | |
| | | |
| | | /* Check to see if we need to synthesize focus events */ |
| | | static SDL_bool |
| | | SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate) |
| | | SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate, SDL_bool send_mouse_motion) |
| | | { |
| | | SDL_Mouse *mouse = SDL_GetMouse(); |
| | | SDL_bool inWindow = SDL_TRUE; |
| | |
| | | #ifdef DEBUG_MOUSE |
| | | printf("Mouse left window, synthesizing move & focus lost event\n"); |
| | | #endif |
| | | SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y); |
| | | if (send_mouse_motion) { |
| | | SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y); |
| | | } |
| | | SDL_SetMouseFocus(NULL); |
| | | } |
| | | return SDL_FALSE; |
| | |
| | | printf("Mouse entered window, synthesizing focus gain & move event\n"); |
| | | #endif |
| | | SDL_SetMouseFocus(window); |
| | | SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y); |
| | | if (send_mouse_motion) { |
| | | SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y); |
| | | } |
| | | } |
| | | return SDL_TRUE; |
| | | } |
| | |
| | | { |
| | | if (window && !relative) { |
| | | SDL_Mouse *mouse = SDL_GetMouse(); |
| | | if (!SDL_UpdateMouseFocus(window, x, y, mouse->buttonstate)) { |
| | | if (!SDL_UpdateMouseFocus(window, x, y, mouse->buttonstate, (mouseID == SDL_TOUCH_MOUSEID) ? SDL_FALSE : SDL_TRUE)) { |
| | | return 0; |
| | | } |
| | | } |
| | |
| | | int xrel; |
| | | int yrel; |
| | | |
| | | if (mouseID == SDL_TOUCH_MOUSEID && !mouse->touch_mouse_events) { |
| | | return 0; |
| | | /* SDL_HINT_MOUSE_TOUCH_EVENTS: controlling whether mouse events should generate synthetic touch events */ |
| | | if (mouse->mouse_touch_events) { |
| | | if (mouseID != SDL_TOUCH_MOUSEID && !relative && track_mouse_down) { |
| | | if (window) { |
| | | float fx = (float)x / (float)window->w; |
| | | float fy = (float)y / (float)window->h; |
| | | SDL_SendTouchMotion(SDL_MOUSE_TOUCHID, 0, window, fx, fy, 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 0; |
| | | } |
| | | } |
| | | |
| | | if (mouseID != SDL_TOUCH_MOUSEID && mouse->relative_mode_warp) { |
| | |
| | | yrel = y - mouse->last_y; |
| | | } |
| | | |
| | | /* Drop events that don't change state */ |
| | | if (!xrel && !yrel) { |
| | | #ifdef DEBUG_MOUSE |
| | | printf("Mouse event didn't change state - dropped!\n"); |
| | | #endif |
| | | return 0; |
| | | } |
| | | |
| | | /* Ignore relative motion when first positioning the mouse */ |
| | | if (!mouse->has_position) { |
| | | xrel = 0; |
| | | yrel = 0; |
| | | mouse->has_position = SDL_TRUE; |
| | | } else if (!xrel && !yrel) { /* Drop events that don't change state */ |
| | | #ifdef DEBUG_MOUSE |
| | | printf("Mouse event didn't change state - dropped!\n"); |
| | | #endif |
| | | return 0; |
| | | } |
| | | |
| | | /* Ignore relative motion positioning the first touch */ |
| | |
| | | event.motion.type = SDL_MOUSEMOTION; |
| | | 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)? SDL_TRUE : SDL_FALSE; |
| | | event.motion.state = mouse->buttonstate; |
| | | event.motion.x = mouse->x; |
| | | event.motion.y = mouse->y; |
| | |
| | | Uint32 type; |
| | | Uint32 buttonstate = mouse->buttonstate; |
| | | |
| | | if (mouseID == SDL_TOUCH_MOUSEID && !mouse->touch_mouse_events) { |
| | | return 0; |
| | | /* SDL_HINT_MOUSE_TOUCH_EVENTS: controlling whether mouse events should generate synthetic touch events */ |
| | | if (mouse->mouse_touch_events) { |
| | | if (mouseID != SDL_TOUCH_MOUSEID && button == SDL_BUTTON_LEFT) { |
| | | if (state == SDL_PRESSED) { |
| | | track_mouse_down = SDL_TRUE; |
| | | } else { |
| | | track_mouse_down = SDL_FALSE; |
| | | } |
| | | if (window) { |
| | | float fx = (float)mouse->x / (float)window->w; |
| | | float fy = (float)mouse->y / (float)window->h; |
| | | SDL_SendTouch(SDL_MOUSE_TOUCHID, 0, window, track_mouse_down, fx, fy, 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 0; |
| | | } |
| | | } |
| | | |
| | | /* Figure out which event to perform */ |
| | |
| | | |
| | | /* We do this after calculating buttonstate so button presses gain focus */ |
| | | if (window && state == SDL_PRESSED) { |
| | | SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate); |
| | | SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate, SDL_TRUE); |
| | | } |
| | | |
| | | if (buttonstate == mouse->buttonstate) { |
| | |
| | | |
| | | /* We do this after dispatching event so button releases can lose focus */ |
| | | if (window && state == SDL_RELEASED) { |
| | | SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate); |
| | | SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate, SDL_TRUE); |
| | | } |
| | | |
| | | |
| | | return posted; |
| | | } |
| | | |
| | |
| | | SDL_SetMouseFocus(window); |
| | | } |
| | | |
| | | if (!x && !y) { |
| | | if (x == 0.0f && y == 0.0f) { |
| | | return 0; |
| | | } |
| | | |
| | |
| | | cursor = next; |
| | | } |
| | | mouse->cursors = NULL; |
| | | mouse->cur_cursor = NULL; |
| | | |
| | | if (mouse->def_cursor && mouse->FreeCursor) { |
| | | mouse->FreeCursor(mouse->def_cursor); |
| | |
| | | static SDL_bool |
| | | ShouldUseRelativeModeWarp(SDL_Mouse *mouse) |
| | | { |
| | | if (!mouse->SetRelativeMouseMode) { |
| | | SDL_assert(mouse->WarpMouse); /* Need this functionality for relative mode warp implementation */ |
| | | return SDL_TRUE; |
| | | if (!mouse->WarpMouse) { |
| | | /* Need this functionality for relative mode warp implementation */ |
| | | return SDL_FALSE; |
| | | } |
| | | |
| | | return SDL_GetHintBoolean(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, SDL_FALSE); |
| | |
| | | return 0; |
| | | } |
| | | |
| | | if (enabled && focusWindow) { |
| | | /* Center it in the focused window to prevent clicks from going through |
| | | * to background windows. |
| | | */ |
| | | SDL_SetMouseFocus(focusWindow); |
| | | SDL_WarpMouseInWindow(focusWindow, focusWindow->w/2, focusWindow->h/2); |
| | | } |
| | | |
| | | /* Set the relative mode */ |
| | | if (!enabled && mouse->relative_mode_warp) { |
| | | mouse->relative_mode_warp = SDL_FALSE; |
| | | } else if (enabled && ShouldUseRelativeModeWarp(mouse)) { |
| | | mouse->relative_mode_warp = SDL_TRUE; |
| | | } else if (mouse->SetRelativeMouseMode(enabled) < 0) { |
| | | } else if (!mouse->SetRelativeMouseMode || mouse->SetRelativeMouseMode(enabled) < 0) { |
| | | if (enabled) { |
| | | /* Fall back to warp mode if native relative mode failed */ |
| | | if (!mouse->WarpMouse) { |
| | |
| | | mouse->scale_accum_x = 0.0f; |
| | | mouse->scale_accum_y = 0.0f; |
| | | |
| | | if (enabled && focusWindow) { |
| | | /* Center it in the focused window to prevent clicks from going through |
| | | * to background windows. |
| | | */ |
| | | SDL_SetMouseFocus(focusWindow); |
| | | SDL_WarpMouseInWindow(focusWindow, focusWindow->w/2, focusWindow->h/2); |
| | | } |
| | | |
| | | if (mouse->focus) { |
| | | SDL_UpdateWindowGrab(mouse->focus); |
| | | |