Add UI/UX support for relative mouse mode

In relative mouse mode, the mouse pointer must be "captured" from the
computer.

Toggle (disable/enable) relative mouse mode using any of the hardcoded
capture keys:
 - left-Alt
 - left-Super
 - right-Super

These capture keys do not conflict with shortcuts, since a shortcut is
always a combination of the MOD key and some other key, while the
capture key triggers an action only if it is pressed and released alone.

The relative mouse mode is also automatically enabled on any click in
the window, and automatically disabled on focus lost (it is possible to
lose focus even without the mouse).
This commit is contained in:
Romain Vimont 2021-12-31 18:52:34 +01:00
parent 40fca82b60
commit 17d01b5bf7
2 changed files with 83 additions and 0 deletions

View file

@ -156,6 +156,17 @@ get_initial_optimal_size(struct sc_size content_size, uint16_t req_width,
return window_size; return window_size;
} }
static inline void
screen_capture_mouse(struct screen *screen, bool capture) {
if (SDL_SetRelativeMouseMode(capture)) {
LOGE("Could not set relative mouse mode to %s: %s",
capture ? "true" : "false", SDL_GetError());
return;
}
screen->mouse_captured = capture;
}
static void static void
screen_update_content_rect(struct screen *screen) { screen_update_content_rect(struct screen *screen) {
int dw; int dw;
@ -354,6 +365,8 @@ screen_init(struct screen *screen, const struct screen_params *params) {
screen->fullscreen = false; screen->fullscreen = false;
screen->maximized = false; screen->maximized = false;
screen->event_failed = false; screen->event_failed = false;
screen->mouse_captured = false;
screen->mouse_capture_key_pressed = 0;
static const struct sc_video_buffer_callbacks cbs = { static const struct sc_video_buffer_callbacks cbs = {
.on_new_frame = sc_video_buffer_on_new_frame, .on_new_frame = sc_video_buffer_on_new_frame,
@ -741,6 +754,11 @@ screen_resize_to_pixel_perfect(struct screen *screen) {
content_size.height); content_size.height);
} }
static inline bool
screen_is_mouse_capture_key(SDL_Keycode key) {
return key == SDLK_LALT || key == SDLK_LGUI || key == SDLK_RGUI;
}
bool bool
screen_handle_event(struct screen *screen, SDL_Event *event) { screen_handle_event(struct screen *screen, SDL_Event *event) {
switch (event->type) { switch (event->type) {
@ -783,8 +801,68 @@ screen_handle_event(struct screen *screen, SDL_Event *event) {
apply_pending_resize(screen); apply_pending_resize(screen);
screen_render(screen, true); screen_render(screen, true);
break; break;
case SDL_WINDOWEVENT_FOCUS_LOST:
if (screen->im.mp->relative_mode) {
screen_capture_mouse(screen, false);
}
break;
} }
return true; return true;
case SDL_KEYDOWN:
if (screen->im.mp->relative_mode) {
SDL_Keycode key = event->key.keysym.sym;
if (screen_is_mouse_capture_key(key)) {
if (!screen->mouse_capture_key_pressed) {
screen->mouse_capture_key_pressed = key;
return true;
} else {
// Another mouse capture key has been pressed, cancel
// mouse (un)capture
screen->mouse_capture_key_pressed = 0;
// Do not return, the event must be forwarded to the
// input manager
}
}
}
break;
case SDL_KEYUP:
if (screen->im.mp->relative_mode) {
SDL_Keycode key = event->key.keysym.sym;
SDL_Keycode cap = screen->mouse_capture_key_pressed;
screen->mouse_capture_key_pressed = 0;
if (key == cap) {
// A mouse capture key has been pressed then released:
// toggle the capture mouse mode
screen_capture_mouse(screen, !screen->mouse_captured);
return true;
}
// Do not return, the event must be forwarded to the input
// manager
}
break;
case SDL_MOUSEWHEEL:
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONDOWN:
if (screen->im.mp->relative_mode && !screen->mouse_captured) {
// Do not forward to input manager, the mouse will be captured
// on SDL_MOUSEBUTTONUP
return true;
}
break;
case SDL_FINGERMOTION:
case SDL_FINGERDOWN:
case SDL_FINGERUP:
if (screen->im.mp->relative_mode) {
// Touch events are not compatible with relative mode
// (coordinates are not relative)
return true;
}
break;
case SDL_MOUSEBUTTONUP:
if (screen->im.mp->relative_mode && !screen->mouse_captured) {
screen_capture_mouse(screen, true);
return true;
}
} }
return input_manager_handle_event(&screen->im, event); return input_manager_handle_event(&screen->im, event);

View file

@ -51,6 +51,11 @@ struct screen {
bool event_failed; // in case SDL_PushEvent() returned an error bool event_failed; // in case SDL_PushEvent() returned an error
bool mouse_captured; // only relevant in relative mouse mode
// To enable/disable mouse capture, a mouse capture key (LALT, LGUI or
// RGUI) must be pressed. This variable tracks the pressed capture key.
SDL_Keycode mouse_capture_key_pressed;
AVFrame *frame; AVFrame *frame;
}; };