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; };