From b1ee5a2421871eb6eefb430771baaf6017a313df Mon Sep 17 00:00:00 2001 From: jdgleaver Date: Sat, 16 Jan 2021 13:56:17 +0000 Subject: [PATCH] (X11) Fix mouse input when mouse is grabbed --- input/drivers/x11_input.c | 191 +++++++++++++++++++++++++------------ menu/drivers/materialui.c | 7 +- menu/drivers/ozone/ozone.c | 4 +- menu/drivers/stripes.c | 5 +- menu/drivers/xmb.c | 5 +- retroarch.c | 24 ++++- retroarch.h | 3 + ui/drivers/ui_qt.cpp | 5 + 8 files changed, 174 insertions(+), 70 deletions(-) diff --git a/input/drivers/x11_input.c b/input/drivers/x11_input.c index 583fe618ad..43ecffc31a 100644 --- a/input/drivers/x11_input.c +++ b/input/drivers/x11_input.c @@ -37,11 +37,15 @@ typedef struct x11_input Display *display; Window win; - int mouse_x, mouse_y; - int mouse_last_x, mouse_last_y; + int mouse_x; + int mouse_y; + int mouse_delta_x; + int mouse_delta_y; bool mouse_grabbed; char state[32]; - bool mouse_l, mouse_r, mouse_m; + bool mouse_l; + bool mouse_r; + bool mouse_m; } x11_input_t; /* Public global variable */ @@ -222,11 +226,11 @@ static int16_t x_input_state( case RETRO_DEVICE_ID_MOUSE_X: if (device == RARCH_DEVICE_MOUSE_SCREEN) return x11->mouse_x; - return x11->mouse_x - x11->mouse_last_x; + return x11->mouse_delta_x; case RETRO_DEVICE_ID_MOUSE_Y: if (device == RARCH_DEVICE_MOUSE_SCREEN) return x11->mouse_y; - return x11->mouse_y - x11->mouse_last_y; + return x11->mouse_delta_y; case RETRO_DEVICE_ID_MOUSE_LEFT: return x11->mouse_l; case RETRO_DEVICE_ID_MOUSE_RIGHT: @@ -592,9 +596,9 @@ static int16_t x_input_state( break; /*deprecated*/ case RETRO_DEVICE_ID_LIGHTGUN_X: - return x11->mouse_x - x11->mouse_last_x; + return x11->mouse_delta_x; case RETRO_DEVICE_ID_LIGHTGUN_Y: - return x11->mouse_y - x11->mouse_last_y; + return x11->mouse_delta_y; case RETRO_DEVICE_ID_LIGHTGUN_PAUSE: { unsigned new_id = RARCH_LIGHTGUN_START; @@ -637,71 +641,140 @@ static void x_input_free(void *data) static void x_input_poll(void *data) { - unsigned mask; - int root_x, root_y, win_x, win_y; - Window root_win, child_win; + Window root_win; + Window child_win; x11_input_t *x11 = (x11_input_t*)data; bool video_has_focus = video_driver_has_focus(); + int root_x = 0; + int root_y = 0; + int win_x = 0; + int win_y = 0; + unsigned mask = 0; - if (video_has_focus) - XQueryKeymap(x11->display, x11->state); - else - memset(x11->state, 0, sizeof(x11->state)); - - x11->mouse_last_x = x11->mouse_x; - x11->mouse_last_y = x11->mouse_y; - - XQueryPointer(x11->display, - x11->win, - &root_win, &child_win, - &root_x, &root_y, - &win_x, &win_y, - &mask); - - if (g_x11_entered) + /* If window loses focus, 'reset' keyboard + * and ignore mouse input */ + if (!video_has_focus) { - x11->mouse_x = win_x; - x11->mouse_y = win_y; - x11->mouse_l = mask & Button1Mask; - x11->mouse_m = mask & Button2Mask; - x11->mouse_r = mask & Button3Mask; + memset(x11->state, 0, sizeof(x11->state)); + x11->mouse_delta_x = 0; + x11->mouse_delta_y = 0; + x11->mouse_l = 0; + x11->mouse_m = 0; + x11->mouse_r = 0; + return; + } - /* Major grab kludge required to circumvent - * absolute pointer area limitation - * AND to be able to use mouse in menu - */ - if (x11->mouse_grabbed && video_has_focus) + /* If pointer is not inside the application + * window, ignore mouse input */ + if (!g_x11_entered) + { + x11->mouse_delta_x = 0; + x11->mouse_delta_y = 0; + x11->mouse_l = 0; + x11->mouse_m = 0; + x11->mouse_r = 0; + return; + } + + /* Process keyboard */ + XQueryKeymap(x11->display, x11->state); + + /* Process mouse */ + if (!XQueryPointer(x11->display, + x11->win, + &root_win, &child_win, + &root_x, &root_y, + &win_x, &win_y, + &mask)) + return; + + /* > Mouse buttons */ + x11->mouse_l = mask & Button1Mask; + x11->mouse_m = mask & Button2Mask; + x11->mouse_r = mask & Button3Mask; + + /* > Mouse pointer */ + if (!x11->mouse_grabbed) + { + /* Mouse is not grabbed - this corresponds + * to 'conventional' pointer input, using + * absolute screen coordinates */ + int mouse_last_x = x11->mouse_x; + int mouse_last_y = x11->mouse_y; + + x11->mouse_x = win_x; + x11->mouse_y = win_y; + + x11->mouse_delta_x = x11->mouse_x - mouse_last_x; + x11->mouse_delta_y = x11->mouse_y - mouse_last_y; + } + else + { + /* Mouse is grabbed - all pointer movement + * must be considered 'relative' */ + XWindowAttributes win_attr; + int centre_x; + int centre_y; + int warp_x = win_x; + int warp_y = win_y; + bool do_warp = false; + + /* Get dimensions/centre coordinates of + * application window */ + if (!XGetWindowAttributes(x11->display, x11->win, &win_attr)) { - int new_x = win_x, new_y = win_y; - int margin = 0; - float margin_pct = 0.05f; - struct video_viewport vp; + x11->mouse_delta_x = 0; + x11->mouse_delta_y = 0; + return; + } - video_driver_get_viewport_info(&vp); + centre_x = win_attr.width >> 1; + centre_y = win_attr.height >> 1; - margin = ((vp.full_height < vp.full_width) ? vp.full_height : vp.full_width) * margin_pct; + /* Get relative movement delta since last + * poll event */ + x11->mouse_delta_x = win_x - centre_x; + x11->mouse_delta_y = win_y - centre_y; - if (win_x + 1 > vp.full_width - margin) - new_x = vp.full_width - margin; - else if (win_x + 1 < margin) - new_x = margin; + /* Get effective 'absolute' pointer location + * (last position + delta, bounded by current + * application window dimensions) */ + x11->mouse_x += x11->mouse_delta_x; + x11->mouse_x = (x11->mouse_x < 0) ? 0 : x11->mouse_x; + x11->mouse_x = (x11->mouse_x >= win_attr.width) ? (win_attr.width - 1) : x11->mouse_x; - if (win_y + 1 > vp.full_height - margin) - new_y = vp.full_height - margin; - else if (win_y + 1 < margin) - new_y = margin; + x11->mouse_y += x11->mouse_delta_y; + x11->mouse_y = (x11->mouse_y < 0) ? 0 : x11->mouse_y; + x11->mouse_y = (x11->mouse_y >= win_attr.height) ? (win_attr.height - 1) : x11->mouse_y; - if (new_x != win_x || new_y != win_y) - { - XWarpPointer(x11->display, None, x11->win, - 0, 0, 0 ,0, - new_x, new_y); + /* Hack/workaround: + * - X11 gives absolute pointer coordinates + * - Once the pointer reaches a screen edge + * it cannot go any further + * - To achieve 'relative' motion, we therefore + * have to reset the hardware cursor to the + * centre of the screen after polling each + * movement delta, such that it is always + * free to move in all directions during the + * time interval until the next poll event */ + if (win_x != centre_x) + { + warp_x = centre_x; + do_warp = true; + } - XSync(x11->display, False); - } + if (win_y != centre_y) + { + warp_y = centre_y; + do_warp = true; + } - x11->mouse_last_x = new_x + x11->mouse_last_x - x11->mouse_x; - x11->mouse_last_y = new_y + x11->mouse_last_y - x11->mouse_y; + if (do_warp) + { + XWarpPointer(x11->display, None, + x11->win, 0, 0, 0, 0, + warp_x, warp_y); + XSync(x11->display, False); } } } diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index ce3210d565..f4c1c006d5 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -6606,6 +6606,7 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) unsigned materialui_color_theme = video_info->materialui_color_theme; bool video_fullscreen = video_info->fullscreen; + bool mouse_grabbed = video_info->input_driver_grab_mouse_state; bool menu_mouse_enable = video_info->menu_mouse_enable; gfx_animation_t *p_anim = anim_get_ptr(); @@ -6791,14 +6792,14 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) /* Draw mouse cursor */ if (mui->mouse_show && (mui->pointer.type != MENU_POINTER_DISABLED)) { - float color_white[16] = { + float color_white[16] = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; - bool cursor_visible = video_fullscreen - && menu_mouse_enable; + bool cursor_visible = (video_fullscreen || mouse_grabbed) && + menu_mouse_enable; if (cursor_visible) gfx_display_draw_cursor( diff --git a/menu/drivers/ozone/ozone.c b/menu/drivers/ozone/ozone.c index f381ee9a20..a621c4a0e4 100644 --- a/menu/drivers/ozone/ozone.c +++ b/menu/drivers/ozone/ozone.c @@ -2780,6 +2780,7 @@ static void ozone_frame(void *data, video_frame_info_t *video_info) float menu_framebuffer_opacity = video_info->menu_framebuffer_opacity; bool libretro_running = video_info->libretro_running; bool video_fullscreen = video_info->fullscreen; + bool mouse_grabbed = video_info->input_driver_grab_mouse_state; bool menu_mouse_enable = video_info->menu_mouse_enable; bool input_menu_swap_ok_cancel_buttons = video_info->input_menu_swap_ok_cancel_buttons; bool battery_level_enable = video_info->battery_level_enable; @@ -3032,7 +3033,8 @@ static void ozone_frame(void *data, video_frame_info_t *video_info) /* Cursor */ if (ozone->show_cursor && (ozone->pointer.type != MENU_POINTER_DISABLED)) { - bool cursor_visible = video_fullscreen && menu_mouse_enable; + bool cursor_visible = (video_fullscreen || mouse_grabbed) && + menu_mouse_enable; gfx_display_set_alpha(ozone->pure_white, 1.0f); if (cursor_visible) diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index d1355bbe62..07af0e77fb 100644 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -2791,6 +2791,7 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) float xmb_alpha_factor = video_info->xmb_alpha_factor; bool xmb_shadows_enable = video_info->xmb_shadows_enable; bool video_fullscreen = video_info->fullscreen; + bool mouse_grabbed = video_info->input_driver_grab_mouse_state; bool menu_mouse_enable = video_info->menu_mouse_enable; const float under_thumb_margin = 0.96; float scale_factor = 0.0f; @@ -3029,8 +3030,8 @@ static void stripes_frame(void *data, video_frame_info_t *video_info) if (stripes->mouse_show) { menu_input_pointer_t pointer; - bool cursor_visible = video_fullscreen - && menu_mouse_enable; + bool cursor_visible = (video_fullscreen || mouse_grabbed) && + menu_mouse_enable; menu_input_get_pointer_state(&pointer); diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 9f94d7e634..c194be6be8 100644 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -4653,6 +4653,7 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) bool timedate_enable = video_info->timedate_enable; bool battery_level_enable = video_info->battery_level_enable; bool video_fullscreen = video_info->fullscreen; + bool mouse_grabbed = video_info->input_driver_grab_mouse_state; bool menu_mouse_enable = video_info->menu_mouse_enable; unsigned xmb_color_theme = video_info->xmb_color_theme; bool libretro_running = video_info->libretro_running; @@ -5286,8 +5287,8 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) /* Cursor image */ if (xmb->mouse_show) { - bool cursor_visible = video_fullscreen - && menu_mouse_enable; + bool cursor_visible = (video_fullscreen || mouse_grabbed) && + menu_mouse_enable; gfx_display_set_alpha(coord_white, MIN(xmb->alpha, 1.00f)); if (cursor_visible) diff --git a/retroarch.c b/retroarch.c index f8982650c5..3a58326f52 100644 --- a/retroarch.c +++ b/retroarch.c @@ -14389,8 +14389,10 @@ bool command_event(enum event_command cmd, void *data) break; case CMD_EVENT_GRAB_MOUSE_TOGGLE: { - bool ret = false; + bool ret = false; bool grab_mouse_state = p_rarch->input_driver_grab_mouse_state; + bool video_fullscreen = + settings->bools.video_fullscreen || p_rarch->rarch_force_fullscreen; grab_mouse_state = !grab_mouse_state; @@ -14408,7 +14410,7 @@ bool command_event(enum event_command cmd, void *data) if (grab_mouse_state) video_driver_hide_mouse(); - else + else if (!video_fullscreen) video_driver_show_mouse(); } break; @@ -24762,6 +24764,12 @@ bool input_key_pressed(int key, bool keyboard_pressed) key); } +bool input_mouse_grabbed(void) +{ + struct rarch_state *p_rarch = &rarch_st; + return p_rarch->input_driver_grab_mouse_state; +} + int16_t button_is_pressed( const input_device_driver_t *joypad, rarch_joypad_info_t *joypad_info, @@ -29880,7 +29888,15 @@ static bool video_driver_init_internal(bool *video_is_threaded) if ((enum rotation)settings->uints.screen_orientation != ORIENTATION_NORMAL) video_display_server_set_screen_orientation((enum rotation)settings->uints.screen_orientation); - if (video.fullscreen) + /* Ensure that we preserve the 'grab mouse' + * state if it was enabled prior to driver + * (re-)initialisation */ + if (p_rarch->input_driver_grab_mouse_state) + { + video_driver_hide_mouse(); + input_driver_grab_mouse(p_rarch); + } + else if (video.fullscreen) { video_driver_hide_mouse(); if (!settings->bools.video_windowed_fullscreen) @@ -31514,6 +31530,8 @@ void video_driver_build_info(video_frame_info_t *video_info) video_info->runloop_is_slowmotion = p_rarch->runloop_slowmotion; video_info->input_driver_nonblock_state = p_rarch->input_driver_nonblock_state; + video_info->input_driver_grab_mouse_state = p_rarch->input_driver_grab_mouse_state; + video_info->userdata = VIDEO_DRIVER_GET_PTR_INTERNAL(false); #ifdef HAVE_THREADS diff --git a/retroarch.h b/retroarch.h index cb9ee79d23..073b2571e6 100644 --- a/retroarch.h +++ b/retroarch.h @@ -1182,6 +1182,7 @@ typedef struct video_frame_info bool widgets_is_rewinding; bool input_menu_swap_ok_cancel_buttons; bool input_driver_nonblock_state; + bool input_driver_grab_mouse_state; bool hard_sync; bool fps_show; bool memory_show; @@ -1981,6 +1982,8 @@ void retroarch_init_task_queue(void); bool input_key_pressed(int key, bool keyboard_pressed); +bool input_mouse_grabbed(void); + const char *joypad_driver_name(unsigned i); void joypad_driver_reinit(void *data, const char *joypad_driver_name); diff --git a/ui/drivers/ui_qt.cpp b/ui/drivers/ui_qt.cpp index 97ec345b34..d42d87ad06 100644 --- a/ui/drivers/ui_qt.cpp +++ b/ui/drivers/ui_qt.cpp @@ -656,6 +656,7 @@ static void ui_companion_qt_toggle(void *data, bool force) settings_t *settings = config_get_ptr(); bool ui_companion_toggle = settings->bools.ui_companion_toggle; bool video_fullscreen = settings->bools.video_fullscreen; + bool mouse_grabbed = input_mouse_grabbed(); if (ui_companion_toggle || force) { @@ -664,7 +665,11 @@ static void ui_companion_qt_toggle(void *data, bool force) win_handle->qtWindow->activateWindow(); win_handle->qtWindow->raise(); + + if (mouse_grabbed) + command_event(CMD_EVENT_GRAB_MOUSE_TOGGLE, NULL); video_driver_show_mouse(); + win_handle->qtWindow->show(); if (video_driver_started_fullscreen())