mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-04-21 14:55:39 +00:00
mouse: Make pointer warp emulation via relative mode available to all platforms
Move the Wayland pointer warp emulation code up to the SDL mouse layer, and activate it when a client attempts to warp a hidden mouse cursor when the hint is set. testrelative adds the ability to test the warp emulation activation/deactivation with the --warp parameter and 'c' key for toggling cursor visibility.
This commit is contained in:
@@ -119,6 +119,18 @@ static void SDLCALL SDL_MouseRelativeSystemScaleChanged(void *userdata, const ch
|
||||
mouse->enable_relative_system_scale = SDL_GetStringBoolean(hint, SDL_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, SDL_TRUE);
|
||||
|
||||
if (!mouse->warp_emulation_hint && mouse->warp_emulation_active) {
|
||||
SDL_SetRelativeMouseMode(SDL_FALSE);
|
||||
mouse->warp_emulation_active = SDL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDLCALL SDL_TouchMouseEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
|
||||
{
|
||||
SDL_Mouse *mouse = (SDL_Mouse *)userdata;
|
||||
@@ -211,6 +223,9 @@ int SDL_PreInitMouse(void)
|
||||
SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE,
|
||||
SDL_MouseRelativeSystemScaleChanged, mouse);
|
||||
|
||||
SDL_AddHintCallback(SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE,
|
||||
SDL_MouseWarpEmulationChanged, mouse);
|
||||
|
||||
SDL_AddHintCallback(SDL_HINT_TOUCH_MOUSE_EVENTS,
|
||||
SDL_TouchMouseEventsChanged, mouse);
|
||||
|
||||
@@ -724,7 +739,7 @@ static int SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_
|
||||
float xrel = 0.0f;
|
||||
float yrel = 0.0f;
|
||||
|
||||
if (!mouse->relative_mode && mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID) {
|
||||
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;
|
||||
}
|
||||
@@ -1132,6 +1147,9 @@ void SDL_QuitMouse(void)
|
||||
SDL_DelHintCallback(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE,
|
||||
SDL_MouseRelativeSystemScaleChanged, mouse);
|
||||
|
||||
SDL_DelHintCallback(SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE,
|
||||
SDL_MouseWarpEmulationChanged, mouse);
|
||||
|
||||
SDL_DelHintCallback(SDL_HINT_TOUCH_MOUSE_EVENTS,
|
||||
SDL_TouchMouseEventsChanged, mouse);
|
||||
|
||||
@@ -1253,9 +1271,24 @@ void SDL_PerformWarpMouseInWindow(SDL_Window *window, float x, float y, SDL_bool
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_EnableWarpEmulation(SDL_Mouse *mouse)
|
||||
{
|
||||
if (!mouse->cursor_shown && mouse->warp_emulation_hint && !mouse->warp_emulation_prohibited) {
|
||||
if (SDL_SetRelativeMouseMode(SDL_TRUE) == 0) {
|
||||
mouse->warp_emulation_active = SDL_TRUE;
|
||||
}
|
||||
|
||||
/* Disable attempts at enabling warp emulation until further notice. */
|
||||
mouse->warp_emulation_prohibited = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_WarpMouseInWindow(SDL_Window *window, float x, float y)
|
||||
{
|
||||
SDL_PerformWarpMouseInWindow(window, x, y, SDL_FALSE);
|
||||
SDL_Mouse *mouse = SDL_GetMouse();
|
||||
SDL_EnableWarpEmulation(mouse);
|
||||
|
||||
SDL_PerformWarpMouseInWindow(window, x, y, mouse->warp_emulation_active);
|
||||
}
|
||||
|
||||
int SDL_WarpMouseGlobal(float x, float y)
|
||||
@@ -1284,6 +1317,18 @@ int SDL_SetRelativeMouseMode(SDL_bool enabled)
|
||||
SDL_Mouse *mouse = SDL_GetMouse();
|
||||
SDL_Window *focusWindow = SDL_GetKeyboardFocus();
|
||||
|
||||
if (enabled) {
|
||||
if (mouse->warp_emulation_active) {
|
||||
mouse->warp_emulation_active = SDL_FALSE;
|
||||
}
|
||||
|
||||
/* If the app has used relative mode before, it probably shouldn't
|
||||
* also be emulating it using repeated mouse warps, so disable
|
||||
* mouse warp emulation by default.
|
||||
*/
|
||||
mouse->warp_emulation_prohibited = SDL_TRUE;
|
||||
}
|
||||
|
||||
if (enabled == mouse->relative_mode) {
|
||||
return 0;
|
||||
}
|
||||
@@ -1642,6 +1687,12 @@ int SDL_ShowCursor(void)
|
||||
{
|
||||
SDL_Mouse *mouse = SDL_GetMouse();
|
||||
|
||||
if (mouse->warp_emulation_active) {
|
||||
SDL_SetRelativeMouseMode(SDL_FALSE);
|
||||
mouse->warp_emulation_active = SDL_FALSE;
|
||||
mouse->warp_emulation_prohibited = SDL_FALSE;
|
||||
}
|
||||
|
||||
if (!mouse->cursor_shown) {
|
||||
mouse->cursor_shown = SDL_TRUE;
|
||||
SDL_SetCursor(NULL);
|
||||
|
||||
@@ -92,6 +92,9 @@ typedef struct
|
||||
SDL_bool relative_mode_warp;
|
||||
SDL_bool relative_mode_warp_motion;
|
||||
SDL_bool relative_mode_cursor_visible;
|
||||
SDL_bool warp_emulation_hint;
|
||||
SDL_bool warp_emulation_active;
|
||||
SDL_bool warp_emulation_prohibited;
|
||||
int relative_mode_clip_interval;
|
||||
SDL_bool enable_normal_speed_scale;
|
||||
float normal_speed_scale;
|
||||
|
||||
@@ -171,10 +171,6 @@ struct SDL_WaylandInput
|
||||
|
||||
struct SDL_WaylandTabletInput *tablet;
|
||||
|
||||
/* are we forcing relative mouse mode? */
|
||||
SDL_bool cursor_visible;
|
||||
SDL_bool relative_mode_override;
|
||||
SDL_bool warp_emulation_prohibited;
|
||||
SDL_bool keyboard_is_virtual;
|
||||
|
||||
/* Current SDL modifier flags */
|
||||
|
||||
@@ -624,14 +624,8 @@ static int Wayland_ShowCursor(SDL_Cursor *cursor)
|
||||
if (input->cursor_shape) {
|
||||
Wayland_SetSystemCursorShape(input, data->cursor_data.system.id);
|
||||
|
||||
input->cursor_visible = SDL_TRUE;
|
||||
input->current_cursor = data;
|
||||
|
||||
if (input->relative_mode_override) {
|
||||
Wayland_input_disable_relative_pointer(input);
|
||||
input->relative_mode_override = SDL_FALSE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else if (!wayland_get_system_cursor(d, data, &scale)) {
|
||||
return -1;
|
||||
@@ -662,18 +656,10 @@ static int Wayland_ShowCursor(SDL_Cursor *cursor)
|
||||
} else {
|
||||
wl_surface_damage(data->surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32);
|
||||
}
|
||||
|
||||
wl_surface_commit(data->surface);
|
||||
|
||||
input->cursor_visible = SDL_TRUE;
|
||||
input->current_cursor = data;
|
||||
|
||||
if (input->relative_mode_override) {
|
||||
Wayland_input_disable_relative_pointer(input);
|
||||
input->relative_mode_override = SDL_FALSE;
|
||||
}
|
||||
|
||||
} else {
|
||||
input->cursor_visible = SDL_FALSE;
|
||||
input->current_cursor = NULL;
|
||||
wl_pointer_set_cursor(pointer, input->pointer_enter_serial, NULL, 0, 0);
|
||||
}
|
||||
@@ -688,40 +674,33 @@ static int Wayland_WarpMouse(SDL_Window *window, float x, float y)
|
||||
SDL_WindowData *wind = window->internal;
|
||||
struct SDL_WaylandInput *input = d->input;
|
||||
|
||||
if (input->cursor_visible || (input->warp_emulation_prohibited && !d->relative_mouse_mode)) {
|
||||
if (d->pointer_constraints) {
|
||||
const SDL_bool toggle_lock = !wind->locked_pointer;
|
||||
if (d->pointer_constraints) {
|
||||
const SDL_bool toggle_lock = !wind->locked_pointer;
|
||||
|
||||
/* The pointer confinement protocol allows setting a hint to warp the pointer,
|
||||
* but only when the pointer is locked.
|
||||
*
|
||||
* Lock the pointer, set the position hint, unlock, and hope for the best.
|
||||
*/
|
||||
if (toggle_lock) {
|
||||
Wayland_input_lock_pointer(input, window);
|
||||
}
|
||||
if (wind->locked_pointer) {
|
||||
const wl_fixed_t f_x = wl_fixed_from_double(x / wind->pointer_scale.x);
|
||||
const wl_fixed_t f_y = wl_fixed_from_double(y / wind->pointer_scale.y);
|
||||
zwp_locked_pointer_v1_set_cursor_position_hint(wind->locked_pointer, f_x, f_y);
|
||||
wl_surface_commit(wind->surface);
|
||||
}
|
||||
if (toggle_lock) {
|
||||
Wayland_input_unlock_pointer(input, window);
|
||||
}
|
||||
|
||||
/* NOTE: There is a pending warp event under discussion that should replace this when available.
|
||||
* https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/340
|
||||
*/
|
||||
SDL_SendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, SDL_FALSE, x, y);
|
||||
} else {
|
||||
return SDL_SetError("wayland: mouse warp failed; compositor lacks support for the required zwp_pointer_confinement_v1 protocol");
|
||||
/* The pointer confinement protocol allows setting a hint to warp the pointer,
|
||||
* but only when the pointer is locked.
|
||||
*
|
||||
* Lock the pointer, set the position hint, unlock, and hope for the best.
|
||||
*/
|
||||
if (toggle_lock) {
|
||||
Wayland_input_lock_pointer(input, window);
|
||||
}
|
||||
} else if (input->warp_emulation_prohibited) {
|
||||
return SDL_Unsupported();
|
||||
} else if (!d->relative_mouse_mode) {
|
||||
Wayland_input_enable_relative_pointer(input);
|
||||
input->relative_mode_override = SDL_TRUE;
|
||||
if (wind->locked_pointer) {
|
||||
const wl_fixed_t f_x = wl_fixed_from_double(x / wind->pointer_scale.x);
|
||||
const wl_fixed_t f_y = wl_fixed_from_double(y / wind->pointer_scale.y);
|
||||
zwp_locked_pointer_v1_set_cursor_position_hint(wind->locked_pointer, f_x, f_y);
|
||||
wl_surface_commit(wind->surface);
|
||||
}
|
||||
if (toggle_lock) {
|
||||
Wayland_input_unlock_pointer(input, window);
|
||||
}
|
||||
|
||||
/* NOTE: There is a pending warp event under discussion that should replace this when available.
|
||||
* https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/340
|
||||
*/
|
||||
SDL_SendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, SDL_FALSE, x, y);
|
||||
} else {
|
||||
return SDL_SetError("wayland: mouse warp failed; compositor lacks support for the required zwp_pointer_confinement_v1 protocol");
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -749,29 +728,12 @@ static int Wayland_SetRelativeMouseMode(SDL_bool enabled)
|
||||
SDL_VideoData *data = vd->internal;
|
||||
|
||||
if (enabled) {
|
||||
/* Disable mouse warp emulation if it's enabled. */
|
||||
if (data->input->relative_mode_override) {
|
||||
data->input->relative_mode_override = SDL_FALSE;
|
||||
}
|
||||
|
||||
/* If the app has used relative mode before, it probably shouldn't
|
||||
* also be emulating it using repeated mouse warps, so disable
|
||||
* mouse warp emulation by default.
|
||||
*/
|
||||
data->input->warp_emulation_prohibited = SDL_TRUE;
|
||||
return Wayland_input_enable_relative_pointer(data->input);
|
||||
} else {
|
||||
return Wayland_input_disable_relative_pointer(data->input);
|
||||
}
|
||||
}
|
||||
|
||||
static void SDLCALL Wayland_EmulateMouseWarpChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
|
||||
{
|
||||
struct SDL_WaylandInput *input = (struct SDL_WaylandInput *)userdata;
|
||||
|
||||
input->warp_emulation_prohibited = !SDL_GetStringBoolean(hint, !input->warp_emulation_prohibited);
|
||||
}
|
||||
|
||||
/* Wayland doesn't support getting the true global cursor position, but it can
|
||||
* be faked well enough for what most applications use it for: querying the
|
||||
* global cursor coordinates and transforming them to the window-relative
|
||||
@@ -862,7 +824,6 @@ void Wayland_InitMouse(void)
|
||||
SDL_Mouse *mouse = SDL_GetMouse();
|
||||
SDL_VideoDevice *vd = SDL_GetVideoDevice();
|
||||
SDL_VideoData *d = vd->internal;
|
||||
struct SDL_WaylandInput *input = d->input;
|
||||
|
||||
mouse->CreateCursor = Wayland_CreateCursor;
|
||||
mouse->CreateSystemCursor = Wayland_CreateSystemCursor;
|
||||
@@ -873,9 +834,6 @@ void Wayland_InitMouse(void)
|
||||
mouse->SetRelativeMouseMode = Wayland_SetRelativeMouseMode;
|
||||
mouse->GetGlobalMouseState = Wayland_GetGlobalMouseState;
|
||||
|
||||
input->relative_mode_override = SDL_FALSE;
|
||||
input->cursor_visible = SDL_TRUE;
|
||||
|
||||
SDL_HitTestResult r = SDL_HITTEST_NORMAL;
|
||||
while (r <= SDL_HITTEST_RESIZE_LEFT) {
|
||||
switch (r) {
|
||||
@@ -918,26 +876,17 @@ void Wayland_InitMouse(void)
|
||||
#endif
|
||||
|
||||
SDL_SetDefaultCursor(Wayland_CreateDefaultCursor());
|
||||
|
||||
SDL_AddHintCallback(SDL_HINT_VIDEO_WAYLAND_EMULATE_MOUSE_WARP,
|
||||
Wayland_EmulateMouseWarpChanged, input);
|
||||
}
|
||||
|
||||
void Wayland_FiniMouse(SDL_VideoData *data)
|
||||
{
|
||||
struct SDL_WaylandInput *input = data->input;
|
||||
int i;
|
||||
|
||||
Wayland_FreeCursorThemes(data);
|
||||
|
||||
#ifdef SDL_USE_LIBDBUS
|
||||
Wayland_DBusFinishCursorProperties();
|
||||
#endif
|
||||
|
||||
SDL_DelHintCallback(SDL_HINT_VIDEO_WAYLAND_EMULATE_MOUSE_WARP,
|
||||
Wayland_EmulateMouseWarpChanged, input);
|
||||
|
||||
for (i = 0; i < SDL_arraysize(sys_cursors); i++) {
|
||||
for (int i = 0; i < SDL_arraysize(sys_cursors); i++) {
|
||||
Wayland_FreeCursor(sys_cursors[i]);
|
||||
sys_cursors[i] = NULL;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user