From 281fcc705254653edc4b418ab68951c6fd069622 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 25 Sep 2024 22:50:44 +0200 Subject: [PATCH] Extract mouse capture Factorize mouse capture for relative mouse mode to reduce code duplication between normal and OTG modes. PR #5322 --- app/meson.build | 1 + app/src/mouse_capture.c | 120 ++++++++++++++++++++++++++++++++++++++ app/src/mouse_capture.h | 35 +++++++++++ app/src/screen.c | 123 ++++----------------------------------- app/src/screen.h | 6 +- app/src/usb/screen_otg.c | 104 ++++----------------------------- app/src/usb/screen_otg.h | 4 +- 7 files changed, 183 insertions(+), 210 deletions(-) create mode 100644 app/src/mouse_capture.c create mode 100644 app/src/mouse_capture.h diff --git a/app/meson.build b/app/meson.build index 99e7e3a2..9d179101 100644 --- a/app/meson.build +++ b/app/meson.build @@ -23,6 +23,7 @@ src = [ 'src/frame_buffer.c', 'src/input_manager.c', 'src/keyboard_sdk.c', + 'src/mouse_capture.c', 'src/mouse_sdk.c', 'src/opengl.c', 'src/options.c', diff --git a/app/src/mouse_capture.c b/app/src/mouse_capture.c new file mode 100644 index 00000000..1420bad6 --- /dev/null +++ b/app/src/mouse_capture.c @@ -0,0 +1,120 @@ +#include "mouse_capture.h" + +#include "util/log.h" + +void +sc_mouse_capture_init(struct sc_mouse_capture *mc, SDL_Window *window) { + mc->window = window; + mc->mouse_capture_key_pressed = SDLK_UNKNOWN; +} + +static inline bool +sc_mouse_capture_is_capture_key(SDL_Keycode key) { + return key == SDLK_LALT || key == SDLK_LGUI || key == SDLK_RGUI; +} + +bool +sc_mouse_capture_handle_event(struct sc_mouse_capture *mc, + const SDL_Event *event) { + switch (event->type) { + case SDL_WINDOWEVENT: + if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST) { + sc_mouse_capture_set_active(mc, false); + return true; + } + break; + case SDL_KEYDOWN: { + SDL_Keycode key = event->key.keysym.sym; + if (sc_mouse_capture_is_capture_key(key)) { + if (!mc->mouse_capture_key_pressed) { + mc->mouse_capture_key_pressed = key; + } else { + // Another mouse capture key has been pressed, cancel + // mouse (un)capture + mc->mouse_capture_key_pressed = 0; + } + // Mouse capture keys are never forwarded to the device + return true; + } + break; + } + case SDL_KEYUP: { + SDL_Keycode key = event->key.keysym.sym; + SDL_Keycode cap = mc->mouse_capture_key_pressed; + mc->mouse_capture_key_pressed = 0; + if (sc_mouse_capture_is_capture_key(key)) { + if (key == cap) { + // A mouse capture key has been pressed then released: + // toggle the capture mouse mode + sc_mouse_capture_toggle(mc); + } + // Mouse capture keys are never forwarded to the device + return true; + } + break; + } + case SDL_MOUSEWHEEL: + case SDL_MOUSEMOTION: + case SDL_MOUSEBUTTONDOWN: + if (!sc_mouse_capture_is_active(mc)) { + // The mouse will be captured on SDL_MOUSEBUTTONUP, so consume + // the event + return true; + } + break; + case SDL_MOUSEBUTTONUP: + if (!sc_mouse_capture_is_active(mc)) { + sc_mouse_capture_set_active(mc, true); + return true; + } + break; + case SDL_FINGERMOTION: + case SDL_FINGERDOWN: + case SDL_FINGERUP: + // Touch events are not compatible with relative mode + // (coordinates are not relative), so consume the event + return true; + } + + return false; +} + +void +sc_mouse_capture_set_active(struct sc_mouse_capture *mc, bool capture) { +#ifdef __APPLE__ + // Workaround for SDL bug on macOS: + // + if (capture) { + int mouse_x, mouse_y; + SDL_GetGlobalMouseState(&mouse_x, &mouse_y); + + int x, y, w, h; + SDL_GetWindowPosition(window, &x, &y); + SDL_GetWindowSize(window, &w, &h); + + bool outside_window = mouse_x < x || mouse_x >= x + w + || mouse_y < y || mouse_y >= y + h; + if (outside_window) { + SDL_WarpMouseInWindow(mc->window, w / 2, h / 2); + } + } +#else + (void) mc; +#endif + if (SDL_SetRelativeMouseMode(capture)) { + LOGE("Could not set relative mouse mode to %s: %s", + capture ? "true" : "false", SDL_GetError()); + } +} + +bool +sc_mouse_capture_is_active(struct sc_mouse_capture *mc) { + (void) mc; + return SDL_GetRelativeMouseMode(); +} + +void +sc_mouse_capture_toggle(struct sc_mouse_capture *mc) { + bool new_value = !sc_mouse_capture_is_active(mc); + sc_mouse_capture_set_active(mc, new_value); +} diff --git a/app/src/mouse_capture.h b/app/src/mouse_capture.h new file mode 100644 index 00000000..53018c19 --- /dev/null +++ b/app/src/mouse_capture.h @@ -0,0 +1,35 @@ +#ifndef SC_MOUSE_CAPTURE_H +#define SC_MOUSE_CAPTURE_H + +#include "common.h" + +#include + +#include + +struct sc_mouse_capture { + SDL_Window *window; + + // 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; +}; + +void +sc_mouse_capture_init(struct sc_mouse_capture *mc, SDL_Window *window); + +void +sc_mouse_capture_set_active(struct sc_mouse_capture *mc, bool capture); + +bool +sc_mouse_capture_is_active(struct sc_mouse_capture *mc); + +void +sc_mouse_capture_toggle(struct sc_mouse_capture *mc); + +// Return true if it consumed the event +bool +sc_mouse_capture_handle_event(struct sc_mouse_capture *mc, + const SDL_Event *event); + +#endif diff --git a/app/src/screen.c b/app/src/screen.c index ce730f19..146f10a5 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -162,47 +162,6 @@ sc_screen_is_relative_mode(struct sc_screen *screen) { return screen->im.mp && screen->im.mp->relative_mode; } -static void -sc_screen_set_mouse_capture(struct sc_screen *screen, bool capture) { -#ifdef __APPLE__ - // Workaround for SDL bug on macOS: - // - if (capture) { - int mouse_x, mouse_y; - SDL_GetGlobalMouseState(&mouse_x, &mouse_y); - - int x, y, w, h; - SDL_GetWindowPosition(screen->window, &x, &y); - SDL_GetWindowSize(screen->window, &w, &h); - - bool outside_window = mouse_x < x || mouse_x >= x + w - || mouse_y < y || mouse_y >= y + h; - if (outside_window) { - SDL_WarpMouseInWindow(screen->window, w / 2, h / 2); - } - } -#else - (void) screen; -#endif - if (SDL_SetRelativeMouseMode(capture)) { - LOGE("Could not set relative mouse mode to %s: %s", - capture ? "true" : "false", SDL_GetError()); - } -} - -static inline bool -sc_screen_get_mouse_capture(struct sc_screen *screen) { - (void) screen; - return SDL_GetRelativeMouseMode(); -} - -static inline void -sc_screen_toggle_mouse_capture(struct sc_screen *screen) { - (void) screen; - bool new_value = !sc_screen_get_mouse_capture(screen); - sc_screen_set_mouse_capture(screen, new_value); -} - static void sc_screen_update_content_rect(struct sc_screen *screen) { assert(screen->video); @@ -371,7 +330,6 @@ sc_screen_init(struct sc_screen *screen, screen->fullscreen = false; screen->maximized = false; screen->minimized = false; - screen->mouse_capture_key_pressed = 0; screen->paused = false; screen->resume_frame = NULL; screen->orientation = SC_ORIENTATION_0; @@ -486,6 +444,9 @@ sc_screen_init(struct sc_screen *screen, sc_input_manager_init(&screen->im, &im_params); + // Initialize even if not used for simplicity + sc_mouse_capture_init(&screen->mc, screen->window); + #ifdef CONTINUOUS_RESIZING_WORKAROUND if (screen->video) { SDL_AddEventWatch(event_watcher, screen); @@ -506,7 +467,7 @@ sc_screen_init(struct sc_screen *screen, if (!screen->video && sc_screen_is_relative_mode(screen)) { // Capture mouse immediately if video mirroring is disabled - sc_screen_set_mouse_capture(screen, true); + sc_mouse_capture_set_active(&screen->mc, true); } return true; @@ -713,7 +674,7 @@ sc_screen_apply_frame(struct sc_screen *screen) { if (sc_screen_is_relative_mode(screen)) { // Capture mouse on start - sc_screen_set_mouse_capture(screen, true); + sc_mouse_capture_set_active(&screen->mc, true); } } @@ -837,15 +798,8 @@ sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) { content_size.height); } -static inline bool -sc_screen_is_mouse_capture_key(SDL_Keycode key) { - return key == SDLK_LALT || key == SDLK_LGUI || key == SDLK_RGUI; -} - bool sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) { - bool relative_mode = sc_screen_is_relative_mode(screen); - switch (event->type) { case SC_EVENT_SCREEN_INIT_SIZE: { // The initial size is passed via screen->frame_size @@ -903,69 +857,14 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) { apply_pending_resize(screen); sc_screen_render(screen, true); break; - case SDL_WINDOWEVENT_FOCUS_LOST: - if (relative_mode) { - sc_screen_set_mouse_capture(screen, false); - } - break; } return true; - case SDL_KEYDOWN: - if (relative_mode) { - SDL_Keycode key = event->key.keysym.sym; - if (sc_screen_is_mouse_capture_key(key)) { - if (!screen->mouse_capture_key_pressed) { - screen->mouse_capture_key_pressed = key; - } else { - // Another mouse capture key has been pressed, cancel - // mouse (un)capture - screen->mouse_capture_key_pressed = 0; - } - // Mouse capture keys are never forwarded to the device - return true; - } - } - break; - case SDL_KEYUP: - if (relative_mode) { - SDL_Keycode key = event->key.keysym.sym; - SDL_Keycode cap = screen->mouse_capture_key_pressed; - screen->mouse_capture_key_pressed = 0; - if (sc_screen_is_mouse_capture_key(key)) { - if (key == cap) { - // A mouse capture key has been pressed then released: - // toggle the capture mouse mode - sc_screen_toggle_mouse_capture(screen); - } - // Mouse capture keys are never forwarded to the device - return true; - } - } - break; - case SDL_MOUSEWHEEL: - case SDL_MOUSEMOTION: - case SDL_MOUSEBUTTONDOWN: - if (relative_mode && !sc_screen_get_mouse_capture(screen)) { - // 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 (relative_mode) { - // Touch events are not compatible with relative mode - // (coordinates are not relative) - return true; - } - break; - case SDL_MOUSEBUTTONUP: - if (relative_mode && !sc_screen_get_mouse_capture(screen)) { - sc_screen_set_mouse_capture(screen, true); - return true; - } - break; + } + + if (sc_screen_is_relative_mode(screen) + && sc_mouse_capture_handle_event(&screen->mc, event)) { + // The mouse capture handler consumed the event + return true; } sc_input_manager_handle_event(&screen->im, event); diff --git a/app/src/screen.h b/app/src/screen.h index 6d5964bd..c716c399 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -13,6 +13,7 @@ #include "fps_counter.h" #include "frame_buffer.h" #include "input_manager.h" +#include "mouse_capture.h" #include "opengl.h" #include "options.h" #include "trait/key_processor.h" @@ -30,6 +31,7 @@ struct sc_screen { struct sc_display display; struct sc_input_manager im; + struct sc_mouse_capture mc; // only used in mouse relative mode struct sc_frame_buffer fb; struct sc_fps_counter fps_counter; @@ -61,10 +63,6 @@ struct sc_screen { bool maximized; bool minimized; - // 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; bool paused; diff --git a/app/src/usb/screen_otg.c b/app/src/usb/screen_otg.c index b13f8d04..aabb8a7f 100644 --- a/app/src/usb/screen_otg.c +++ b/app/src/usb/screen_otg.c @@ -4,47 +4,6 @@ #include "options.h" #include "util/log.h" -static void -sc_screen_otg_set_mouse_capture(struct sc_screen_otg *screen, bool capture) { -#ifdef __APPLE__ - // Workaround for SDL bug on macOS: - // - if (capture) { - int mouse_x, mouse_y; - SDL_GetGlobalMouseState(&mouse_x, &mouse_y); - - int x, y, w, h; - SDL_GetWindowPosition(screen->window, &x, &y); - SDL_GetWindowSize(screen->window, &w, &h); - - bool outside_window = mouse_x < x || mouse_x >= x + w - || mouse_y < y || mouse_y >= y + h; - if (outside_window) { - SDL_WarpMouseInWindow(screen->window, w / 2, h / 2); - } - } -#else - (void) screen; -#endif - if (SDL_SetRelativeMouseMode(capture)) { - LOGE("Could not set relative mouse mode to %s: %s", - capture ? "true" : "false", SDL_GetError()); - } -} - -static inline bool -sc_screen_otg_get_mouse_capture(struct sc_screen_otg *screen) { - (void) screen; - return SDL_GetRelativeMouseMode(); -} - -static inline void -sc_screen_otg_toggle_mouse_capture(struct sc_screen_otg *screen) { - (void) screen; - bool new_value = !sc_screen_otg_get_mouse_capture(screen); - sc_screen_otg_set_mouse_capture(screen, new_value); -} - static void sc_screen_otg_render(struct sc_screen_otg *screen) { SDL_RenderClear(screen->renderer); @@ -61,8 +20,6 @@ sc_screen_otg_init(struct sc_screen_otg *screen, screen->mouse = params->mouse; screen->gamepad = params->gamepad; - screen->mouse_capture_key_pressed = 0; - const char *title = params->window_title; assert(title); @@ -113,9 +70,11 @@ sc_screen_otg_init(struct sc_screen_otg *screen, LOGW("Could not load icon"); } + sc_mouse_capture_init(&screen->mc, screen->window); + if (screen->mouse) { // Capture mouse on start - sc_screen_otg_set_mouse_capture(screen, true); + sc_mouse_capture_set_active(&screen->mc, true); } return true; @@ -137,11 +96,6 @@ sc_screen_otg_destroy(struct sc_screen_otg *screen) { SDL_DestroyWindow(screen->window); } -static inline bool -sc_screen_otg_is_mouse_capture_key(SDL_Keycode key) { - return key == SDLK_LALT || key == SDLK_LGUI || key == SDLK_RGUI; -} - static void sc_screen_otg_process_key(struct sc_screen_otg *screen, const SDL_KeyboardEvent *event) { @@ -298,80 +252,46 @@ sc_screen_otg_process_gamepad_button(struct sc_screen_otg *screen, void sc_screen_otg_handle_event(struct sc_screen_otg *screen, SDL_Event *event) { + if (sc_mouse_capture_handle_event(&screen->mc, event)) { + // The mouse capture handler consumed the event + return; + } + switch (event->type) { case SDL_WINDOWEVENT: switch (event->window.event) { case SDL_WINDOWEVENT_EXPOSED: sc_screen_otg_render(screen); break; - case SDL_WINDOWEVENT_FOCUS_LOST: - if (screen->mouse) { - sc_screen_otg_set_mouse_capture(screen, false); - } - break; } return; case SDL_KEYDOWN: - if (screen->mouse) { - SDL_Keycode key = event->key.keysym.sym; - if (sc_screen_otg_is_mouse_capture_key(key)) { - if (!screen->mouse_capture_key_pressed) { - screen->mouse_capture_key_pressed = key; - } else { - // Another mouse capture key has been pressed, cancel - // mouse (un)capture - screen->mouse_capture_key_pressed = 0; - } - // Mouse capture keys are never forwarded to the device - return; - } - } - if (screen->keyboard) { sc_screen_otg_process_key(screen, &event->key); } break; case SDL_KEYUP: - if (screen->mouse) { - SDL_Keycode key = event->key.keysym.sym; - SDL_Keycode cap = screen->mouse_capture_key_pressed; - screen->mouse_capture_key_pressed = 0; - if (sc_screen_otg_is_mouse_capture_key(key)) { - if (key == cap) { - // A mouse capture key has been pressed then released: - // toggle the capture mouse mode - sc_screen_otg_toggle_mouse_capture(screen); - } - // Mouse capture keys are never forwarded to the device - return; - } - } - if (screen->keyboard) { sc_screen_otg_process_key(screen, &event->key); } break; case SDL_MOUSEMOTION: - if (screen->mouse && sc_screen_otg_get_mouse_capture(screen)) { + if (screen->mouse) { sc_screen_otg_process_mouse_motion(screen, &event->motion); } break; case SDL_MOUSEBUTTONDOWN: - if (screen->mouse && sc_screen_otg_get_mouse_capture(screen)) { + if (screen->mouse) { sc_screen_otg_process_mouse_button(screen, &event->button); } break; case SDL_MOUSEBUTTONUP: if (screen->mouse) { - if (sc_screen_otg_get_mouse_capture(screen)) { - sc_screen_otg_process_mouse_button(screen, &event->button); - } else { - sc_screen_otg_set_mouse_capture(screen, true); - } + sc_screen_otg_process_mouse_button(screen, &event->button); } break; case SDL_MOUSEWHEEL: - if (screen->mouse && sc_screen_otg_get_mouse_capture(screen)) { + if (screen->mouse) { sc_screen_otg_process_mouse_wheel(screen, &event->wheel); } break; diff --git a/app/src/usb/screen_otg.h b/app/src/usb/screen_otg.h index 2ea76eda..850a6ae5 100644 --- a/app/src/usb/screen_otg.h +++ b/app/src/usb/screen_otg.h @@ -8,6 +8,7 @@ #include "keyboard_aoa.h" #include "mouse_aoa.h" +#include "mouse_capture.h" #include "gamepad_aoa.h" struct sc_screen_otg { @@ -19,8 +20,7 @@ struct sc_screen_otg { SDL_Renderer *renderer; SDL_Texture *texture; - // See equivalent mechanism in screen.h - SDL_Keycode mouse_capture_key_pressed; + struct sc_mouse_capture mc; }; struct sc_screen_otg_params {