From 17d01b5bf7832d4c65435e6076a1c75f7aec66be Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 31 Dec 2021 18:52:34 +0100 Subject: [PATCH] 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). --- app/src/screen.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++ app/src/screen.h | 5 ++++ 2 files changed, 83 insertions(+) diff --git a/app/src/screen.c b/app/src/screen.c index 9ad81cb8..9c9f80bc 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -156,6 +156,17 @@ get_initial_optimal_size(struct sc_size content_size, uint16_t req_width, 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 screen_update_content_rect(struct screen *screen) { int dw; @@ -354,6 +365,8 @@ screen_init(struct screen *screen, const struct screen_params *params) { screen->fullscreen = false; screen->maximized = false; screen->event_failed = false; + screen->mouse_captured = false; + screen->mouse_capture_key_pressed = 0; static const struct sc_video_buffer_callbacks cbs = { .on_new_frame = sc_video_buffer_on_new_frame, @@ -741,6 +754,11 @@ screen_resize_to_pixel_perfect(struct screen *screen) { 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 screen_handle_event(struct screen *screen, SDL_Event *event) { switch (event->type) { @@ -783,8 +801,68 @@ screen_handle_event(struct screen *screen, SDL_Event *event) { apply_pending_resize(screen); screen_render(screen, true); break; + case SDL_WINDOWEVENT_FOCUS_LOST: + if (screen->im.mp->relative_mode) { + screen_capture_mouse(screen, false); + } + break; } 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); diff --git a/app/src/screen.h b/app/src/screen.h index bc7696f1..f0e15a7d 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -51,6 +51,11 @@ struct screen { 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; };