| | |
| | | /* |
| | | Simple DirectMedia Layer |
| | | Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org> |
| | | Copyright (C) 1997-2018 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 "default_cursor.h" |
| | | #include "../video/SDL_sysvideo.h" |
| | | |
| | | /* #define DEBUG_MOUSE */ |
| | | |
| | | /* The mouse state */ |
| | | static SDL_Mouse SDL_mouse; |
| | | static Uint32 SDL_double_click_time = 500; |
| | | static int SDL_double_click_radius = 1; |
| | | |
| | | static int |
| | | SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int 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 { |
| | | #ifdef __WIN32__ |
| | | 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->normal_speed_scale = (float)SDL_atof(hint); |
| | | } else { |
| | | 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->relative_speed_scale = (float)SDL_atof(hint); |
| | | } else { |
| | | mouse->relative_speed_scale = 1.0f; |
| | | } |
| | | } |
| | | |
| | | static void SDLCALL |
| | | SDL_TouchMouseEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) |
| | | { |
| | | 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; |
| | | } |
| | | } |
| | | |
| | | /* Public functions */ |
| | | int |
| | | SDL_MouseInit(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_TOUCH_MOUSE_EVENTS, |
| | | SDL_TouchMouseEventsChanged, mouse); |
| | | |
| | | mouse->cursor_shown = SDL_TRUE; |
| | | |
| | |
| | | return &SDL_mouse; |
| | | } |
| | | |
| | | void |
| | | SDL_SetDoubleClickTime(Uint32 interval) |
| | | { |
| | | SDL_double_click_time = interval; |
| | | } |
| | | |
| | | SDL_Window * |
| | | SDL_GetMouseFocus(void) |
| | | { |
| | |
| | | return mouse->focus; |
| | | } |
| | | |
| | | #if 0 |
| | | void |
| | | SDL_ResetMouse(void) |
| | | { |
| | |
| | | } |
| | | SDL_assert(mouse->buttonstate == 0); |
| | | } |
| | | #endif |
| | | |
| | | void |
| | | SDL_SetMouseFocus(SDL_Window * window) |
| | |
| | | } |
| | | |
| | | mouse->focus = window; |
| | | mouse->has_position = SDL_FALSE; |
| | | |
| | | if (mouse->focus) { |
| | | SDL_SendWindowEvent(mouse->focus, SDL_WINDOWEVENT_ENTER, 0, 0); |
| | |
| | | |
| | | if (window != mouse->focus) { |
| | | #ifdef DEBUG_MOUSE |
| | | printf("Mouse entered window, synthesizing focus gain & move event\n"); |
| | | printf("Mouse entered window, synthesizing focus gain & move event\n"); |
| | | #endif |
| | | SDL_SetMouseFocus(window); |
| | | SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y); |
| | | SDL_SetMouseFocus(window); |
| | | SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y); |
| | | } |
| | | return SDL_TRUE; |
| | | } |
| | |
| | | } |
| | | |
| | | static int |
| | | GetScaledMouseDelta(float scale, int value, float *accum) |
| | | { |
| | | if (scale != 1.0f) { |
| | | *accum += scale * value; |
| | | if (*accum >= 0.0f) { |
| | | value = (int)SDL_floor(*accum); |
| | | } else { |
| | | value = (int)SDL_ceil(*accum); |
| | | } |
| | | *accum -= value; |
| | | } |
| | | return value; |
| | | } |
| | | |
| | | static int |
| | | SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y) |
| | | { |
| | | SDL_Mouse *mouse = SDL_GetMouse(); |
| | |
| | | int xrel; |
| | | int yrel; |
| | | |
| | | if (mouse->relative_mode_warp) { |
| | | if (mouseID == SDL_TOUCH_MOUSEID && !mouse->touch_mouse_events) { |
| | | return 0; |
| | | } |
| | | |
| | | if (mouseID != SDL_TOUCH_MOUSEID && mouse->relative_mode_warp) { |
| | | int center_x = 0, center_y = 0; |
| | | SDL_GetWindowSize(window, ¢er_x, ¢er_y); |
| | | center_x /= 2; |
| | |
| | | } |
| | | |
| | | if (relative) { |
| | | if (mouse->relative_mode) { |
| | | x = GetScaledMouseDelta(mouse->relative_speed_scale, x, &mouse->scale_accum_x); |
| | | y = GetScaledMouseDelta(mouse->relative_speed_scale, y, &mouse->scale_accum_y); |
| | | } else { |
| | | x = GetScaledMouseDelta(mouse->normal_speed_scale, x, &mouse->scale_accum_x); |
| | | y = GetScaledMouseDelta(mouse->normal_speed_scale, y, &mouse->scale_accum_y); |
| | | } |
| | | xrel = x; |
| | | yrel = y; |
| | | x = (mouse->last_x + xrel); |
| | |
| | | return 0; |
| | | } |
| | | |
| | | /* Ignore relative motion when first positioning the mouse */ |
| | | if (!mouse->has_position) { |
| | | xrel = 0; |
| | | yrel = 0; |
| | | mouse->has_position = SDL_TRUE; |
| | | } |
| | | |
| | | /* Ignore relative motion positioning the first touch */ |
| | | if (mouseID == SDL_TOUCH_MOUSEID && !mouse->buttonstate) { |
| | | xrel = 0; |
| | | yrel = 0; |
| | | } |
| | | |
| | | /* Update internal mouse coordinates */ |
| | | if (!mouse->relative_mode) { |
| | | mouse->x = x; |
| | |
| | | if (window && ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0)) { |
| | | int x_max = 0, y_max = 0; |
| | | |
| | | // !!! FIXME: shouldn't this be (window) instead of (mouse->focus)? |
| | | /* !!! FIXME: shouldn't this be (window) instead of (mouse->focus)? */ |
| | | SDL_GetWindowSize(mouse->focus, &x_max, &y_max); |
| | | --x_max; |
| | | --y_max; |
| | |
| | | Uint32 type; |
| | | Uint32 buttonstate = mouse->buttonstate; |
| | | |
| | | if (mouseID == SDL_TOUCH_MOUSEID && !mouse->touch_mouse_events) { |
| | | return 0; |
| | | } |
| | | |
| | | /* Figure out which event to perform */ |
| | | switch (state) { |
| | | case SDL_PRESSED: |
| | |
| | | if (state == SDL_PRESSED) { |
| | | Uint32 now = SDL_GetTicks(); |
| | | |
| | | if (SDL_TICKS_PASSED(now, clickstate->last_timestamp + SDL_double_click_time) || |
| | | SDL_abs(mouse->x - clickstate->last_x) > SDL_double_click_radius || |
| | | SDL_abs(mouse->y - clickstate->last_y) > SDL_double_click_radius) { |
| | | if (SDL_TICKS_PASSED(now, clickstate->last_timestamp + mouse->double_click_time) || |
| | | SDL_abs(mouse->x - clickstate->last_x) > mouse->double_click_radius || |
| | | SDL_abs(mouse->y - clickstate->last_y) > mouse->double_click_radius) { |
| | | clickstate->click_count = 0; |
| | | } |
| | | clickstate->last_timestamp = now; |
| | |
| | | } |
| | | |
| | | int |
| | | SDL_SendMouseWheel(SDL_Window * window, SDL_MouseID mouseID, int x, int y, SDL_MouseWheelDirection direction) |
| | | SDL_SendMouseWheel(SDL_Window * window, SDL_MouseID mouseID, float x, float y, SDL_MouseWheelDirection direction) |
| | | { |
| | | SDL_Mouse *mouse = SDL_GetMouse(); |
| | | int posted; |
| | | int integral_x, integral_y; |
| | | |
| | | if (window) { |
| | | SDL_SetMouseFocus(window); |
| | |
| | | return 0; |
| | | } |
| | | |
| | | mouse->accumulated_wheel_x += x; |
| | | if (mouse->accumulated_wheel_x > 0) { |
| | | integral_x = (int)SDL_floor(mouse->accumulated_wheel_x); |
| | | } else if (mouse->accumulated_wheel_x < 0) { |
| | | integral_x = (int)SDL_ceil(mouse->accumulated_wheel_x); |
| | | } else { |
| | | integral_x = 0; |
| | | } |
| | | mouse->accumulated_wheel_x -= integral_x; |
| | | |
| | | mouse->accumulated_wheel_y += y; |
| | | if (mouse->accumulated_wheel_y > 0) { |
| | | integral_y = (int)SDL_floor(mouse->accumulated_wheel_y); |
| | | } else if (mouse->accumulated_wheel_y < 0) { |
| | | integral_y = (int)SDL_ceil(mouse->accumulated_wheel_y); |
| | | } else { |
| | | integral_y = 0; |
| | | } |
| | | mouse->accumulated_wheel_y -= integral_y; |
| | | |
| | | /* Post the event, if desired */ |
| | | posted = 0; |
| | | if (SDL_GetEventState(SDL_MOUSEWHEEL) == SDL_ENABLE) { |
| | |
| | | event.type = SDL_MOUSEWHEEL; |
| | | event.wheel.windowID = mouse->focus ? mouse->focus->id : 0; |
| | | event.wheel.which = mouseID; |
| | | event.wheel.x = x; |
| | | event.wheel.y = y; |
| | | #if 0 /* Uncomment this when it goes in for SDL 2.1 */ |
| | | event.wheel.preciseX = x; |
| | | event.wheel.preciseY = y; |
| | | #endif |
| | | event.wheel.x = integral_x; |
| | | event.wheel.y = integral_y; |
| | | event.wheel.direction = (Uint32)direction; |
| | | posted = (SDL_PushEvent(&event) > 0); |
| | | } |
| | |
| | | SDL_FreeCursor(cursor); |
| | | cursor = next; |
| | | } |
| | | mouse->cursors = NULL; |
| | | |
| | | if (mouse->def_cursor && mouse->FreeCursor) { |
| | | mouse->FreeCursor(mouse->def_cursor); |
| | | mouse->def_cursor = NULL; |
| | | } |
| | | |
| | | if (mouse->clickstate) { |
| | | SDL_free(mouse->clickstate); |
| | | mouse->clickstate = NULL; |
| | | } |
| | | |
| | | SDL_zerop(mouse); |
| | | SDL_DelHintCallback(SDL_HINT_MOUSE_NORMAL_SPEED_SCALE, |
| | | SDL_MouseNormalSpeedScaleChanged, mouse); |
| | | |
| | | SDL_DelHintCallback(SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE, |
| | | SDL_MouseRelativeSpeedScaleChanged, mouse); |
| | | } |
| | | |
| | | Uint32 |
| | |
| | | *x = *y = 0; |
| | | |
| | | if (!mouse->GetGlobalMouseState) { |
| | | SDL_assert(0 && "This should really be implemented for every target."); |
| | | return 0; |
| | | } |
| | | |
| | |
| | | ShouldUseRelativeModeWarp(SDL_Mouse *mouse) |
| | | { |
| | | if (!mouse->SetRelativeMouseMode) { |
| | | SDL_assert(mouse->WarpMouse); /* Need this functionality for relative mode warp implementation */ |
| | | return SDL_TRUE; |
| | | } |
| | | |
| | |
| | | } else if (mouse->SetRelativeMouseMode(enabled) < 0) { |
| | | if (enabled) { |
| | | /* Fall back to warp mode if native relative mode failed */ |
| | | if (!mouse->WarpMouse) { |
| | | return SDL_SetError("No relative mode implementation available"); |
| | | } |
| | | mouse->relative_mode_warp = SDL_TRUE; |
| | | } |
| | | } |
| | | mouse->relative_mode = enabled; |
| | | mouse->scale_accum_x = 0.0f; |
| | | mouse->scale_accum_y = 0.0f; |
| | | |
| | | if (mouse->focus) { |
| | | SDL_UpdateWindowGrab(mouse->focus); |