Fix T98793: Wayland clamps cursor movement fails with gnome-shell

The current gnome-shell (v42.2) has a bug where grabbing the cursor
doesn't scale the region when confining it to the window.

For Hi-DPI displays this means the cursor may be confined to a quarter
of the window, making grab unusable.

Even though this has been fixed up-stream the issue remains in the
latest release - so workaround the problem by implementing window
confined grab using a software cursor.

This is only used gnome-shell for displays that use Hi-DPI scaling.
This commit is contained in:
Campbell Barton 2022-06-18 15:02:18 +10:00
parent 5c814e75f2
commit 35b2b9b6e6
10 changed files with 160 additions and 17 deletions

View File

@ -404,8 +404,9 @@ extern GHOST_TSuccess GHOST_SetCursorPosition(GHOST_SystemHandle systemhandle,
void GHOST_GetCursorGrabState(GHOST_WindowHandle windowhandle,
GHOST_TGrabCursorMode *r_mode,
GHOST_TAxisFlag *r_wrap_axis,
int r_bounds[4]);
GHOST_TAxisFlag *r_axis_flag,
int r_bounds[4],
bool *r_use_software_cursor);
/**
* Grabs the cursor for a modal operation, to keep receiving

View File

@ -258,7 +258,10 @@ class GHOST_IWindow {
virtual void getCursorGrabState(GHOST_TGrabCursorMode &mode,
GHOST_TAxisFlag &axis_flag,
GHOST_Rect &bounds) = 0;
GHOST_Rect &bounds,
bool &use_software_cursor) = 0;
virtual bool getCursorGrabUseSoftwareDisplay() = 0;
/**
* Test if the standard cursor shape is supported by current platform.

View File

@ -379,15 +379,18 @@ GHOST_TSuccess GHOST_SetCursorGrab(GHOST_WindowHandle windowhandle,
void GHOST_GetCursorGrabState(GHOST_WindowHandle windowhandle,
GHOST_TGrabCursorMode *r_mode,
GHOST_TAxisFlag *r_axis_flag,
int r_bounds[4])
int r_bounds[4],
bool *r_use_software_cursor)
{
GHOST_IWindow *window = (GHOST_IWindow *)windowhandle;
GHOST_Rect bounds_rect;
window->getCursorGrabState(*r_mode, *r_axis_flag, bounds_rect);
bool use_software_cursor;
window->getCursorGrabState(*r_mode, *r_axis_flag, bounds_rect, use_software_cursor);
r_bounds[0] = bounds_rect.m_l;
r_bounds[1] = bounds_rect.m_t;
r_bounds[2] = bounds_rect.m_r;
r_bounds[3] = bounds_rect.m_b;
*r_use_software_cursor = use_software_cursor;
}
GHOST_TSuccess GHOST_GetModifierKeyState(GHOST_SystemHandle systemhandle,

View File

@ -44,6 +44,24 @@
static GHOST_WindowWayland *window_from_surface(struct wl_surface *surface);
/**
* GNOME (mutter 42.2 had a bug with confine not respecting scale - Hi-DPI), See: T98793.
* Even though this has been fixed, at time of writing it's not yet in a release.
* Workaround the problem by implementing confine with a software cursor.
* While this isn't ideal, it's not adding a lot of overhead as software
* cursors are already used for warping (which WAYLAND doesn't support).
*/
#define USE_GNOME_CONFINE_HACK
/**
* Always use software confine (not just in GNOME).
* Useful for developing with compositors that don't need this workaround.
*/
// #define USE_GNOME_CONFINE_HACK_ALWAYS_ON
#ifdef USE_GNOME_CONFINE_HACK
static bool use_gnome_confine_hack = false;
#endif
/* -------------------------------------------------------------------- */
/** \name Private Types & Defines
* \{ */
@ -156,6 +174,11 @@ struct input_t {
* \endcode
*/
wl_fixed_t xy[2] = {0, 0};
#ifdef USE_GNOME_CONFINE_HACK
bool xy_software_confine = false;
#endif
GHOST_Buttons buttons = GHOST_Buttons();
struct cursor_t cursor;
@ -590,6 +613,21 @@ static void relative_pointer_handle_relative_motion(
input->xy[0] += dx / scale;
input->xy[1] += dy / scale;
#ifdef USE_GNOME_CONFINE_HACK
if (input->xy_software_confine) {
GHOST_Rect bounds;
win->getClientBounds(bounds);
/* Needed or the cursor is considered outside the window and doesn't restore the location. */
bounds.m_r -= 1;
bounds.m_b -= 1;
bounds.m_l = wl_fixed_from_int(bounds.m_l) / scale;
bounds.m_t = wl_fixed_from_int(bounds.m_t) / scale;
bounds.m_r = wl_fixed_from_int(bounds.m_r) / scale;
bounds.m_b = wl_fixed_from_int(bounds.m_b) / scale;
bounds.clampPoint(input->xy[0], input->xy[1]);
}
#endif
input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
GHOST_kEventCursorMove,
win,
@ -1868,6 +1906,13 @@ static void xdg_output_handle_logical_size(void *data,
* detected otherwise), then override if necessary. */
if ((output->size_logical[0] == width) && (output->scale_fractional == wl_fixed_from_int(1))) {
GHOST_PRINT("xdg_output scale did not match, overriding with wl_output scale");
#ifdef USE_GNOME_CONFINE_HACK
/* Use a bug in GNOME to check GNOME is in use. If the bug is fixed this won't cause an issue
* as T98793 has been fixed up-stream too, but not in a release at time of writing. */
use_gnome_confine_hack = true;
#endif
return;
}
}
@ -2695,6 +2740,49 @@ bool GHOST_SystemWayland::supportsWindowPosition()
return false;
}
bool GHOST_SystemWayland::getCursorGrabUseSoftwareDisplay(const GHOST_TGrabCursorMode mode)
{
if (d->inputs.empty()) {
return false;
}
if (mode == GHOST_kGrabWrap) {
return true;
}
#ifdef USE_GNOME_CONFINE_HACK
if (mode == GHOST_kGrabNormal) {
const input_t *input = d->inputs[0];
return input->xy_software_confine;
}
#endif
return false;
}
#ifdef USE_GNOME_CONFINE_HACK
static bool setCursorGrab_use_software_confine(const GHOST_TGrabCursorMode mode,
wl_surface *surface)
{
# ifndef USE_GNOME_CONFINE_HACK_ALWAYS_ON
if (use_gnome_confine_hack == false) {
return false;
}
# endif
if (mode != GHOST_kGrabNormal) {
return false;
}
GHOST_WindowWayland *win = window_from_surface(surface);
if (!win) {
return false;
}
# ifndef USE_GNOME_CONFINE_HACK_ALWAYS_ON
if (win->scale() <= 1) {
return false;
}
# endif
return true;
}
#endif
GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mode,
const GHOST_TGrabCursorMode mode_current,
wl_surface *surface)
@ -2715,19 +2803,28 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mo
input_t *input = d->inputs[0];
#ifdef USE_GNOME_CONFINE_HACK
const bool was_software_confine = input->xy_software_confine;
const bool use_software_confine = setCursorGrab_use_software_confine(mode, surface);
#else
const bool was_software_confine = false;
const bool use_software_confine = false;
#endif
/* Warping happens to require software cursor which also hides. */
#define MODE_NEEDS_LOCK(m) ((m) == GHOST_kGrabWrap || (m) == GHOST_kGrabHide)
#define MODE_NEEDS_HIDE(m) ((m) == GHOST_kGrabHide)
#define MODE_NEEDS_HIDE(m) (((m) == GHOST_kGrabHide) || ((m) == GHOST_kGrabWrap))
#define MODE_NEEDS_CONFINE(m) ((m) == GHOST_kGrabNormal)
const bool was_lock = MODE_NEEDS_LOCK(mode_current);
const bool use_lock = MODE_NEEDS_LOCK(mode);
const bool was_lock = MODE_NEEDS_LOCK(mode_current) || was_software_confine;
const bool use_lock = MODE_NEEDS_LOCK(mode) || use_software_confine;
/* Check for wrap as #supportsCursorWarp isn't supported. */
const bool was_hide = MODE_NEEDS_HIDE(mode_current) || (mode_current == GHOST_kGrabWrap);
const bool use_hide = MODE_NEEDS_HIDE(mode) || (mode == GHOST_kGrabWrap);
const bool was_hide = MODE_NEEDS_HIDE(mode_current) || was_software_confine;
const bool use_hide = MODE_NEEDS_HIDE(mode) || use_software_confine;
const bool was_confine = MODE_NEEDS_CONFINE(mode_current);
const bool use_confine = MODE_NEEDS_CONFINE(mode);
const bool was_confine = MODE_NEEDS_CONFINE(mode_current) && (was_software_confine == false);
const bool use_confine = MODE_NEEDS_CONFINE(mode) && (use_software_confine == false);
#undef MODE_NEEDS_LOCK
#undef MODE_NEEDS_HIDE
@ -2788,6 +2885,15 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mo
wl_surface_commit(surface);
}
}
#ifdef USE_GNOME_CONFINE_HACK
else if (mode_current == GHOST_kGrabNormal) {
if (input->xy_software_confine) {
zwp_locked_pointer_v1_set_cursor_position_hint(
input->locked_pointer, input->xy[0], input->xy[1]);
wl_surface_commit(surface);
}
}
#endif
zwp_locked_pointer_v1_destroy(input->locked_pointer);
input->locked_pointer = nullptr;
@ -2836,6 +2942,10 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mo
}
}
#ifdef USE_GNOME_CONFINE_HACK
input->xy_software_confine = use_software_confine;
#endif
return GHOST_kSuccess;
}

View File

@ -127,6 +127,8 @@ class GHOST_SystemWayland : public GHOST_System {
bool supportsCursorWarp();
bool supportsWindowPosition();
bool getCursorGrabUseSoftwareDisplay(const GHOST_TGrabCursorMode mode);
GHOST_TSuccess setCursorGrab(const GHOST_TGrabCursorMode mode,
const GHOST_TGrabCursorMode mode_current,
wl_surface *surface);

View File

@ -164,7 +164,8 @@ GHOST_TSuccess GHOST_Window::getCursorGrabBounds(GHOST_Rect &bounds)
void GHOST_Window::getCursorGrabState(GHOST_TGrabCursorMode &mode,
GHOST_TAxisFlag &wrap_axis,
GHOST_Rect &bounds)
GHOST_Rect &bounds,
bool &use_software_cursor)
{
mode = m_cursorGrab;
if (m_cursorGrab == GHOST_kGrabWrap) {
@ -178,6 +179,14 @@ void GHOST_Window::getCursorGrabState(GHOST_TGrabCursorMode &mode,
bounds.m_b = -1;
wrap_axis = GHOST_kGrabAxisNone;
}
use_software_cursor = (m_cursorGrab != GHOST_kGrabDisable) ? getCursorGrabUseSoftwareDisplay() :
false;
}
bool GHOST_Window::getCursorGrabUseSoftwareDisplay()
{
/* Sub-classes may override, by default don't use software cursor. */
return false;
}
GHOST_TSuccess GHOST_Window::setCursorShape(GHOST_TStandardCursor cursorShape)

View File

@ -154,7 +154,12 @@ class GHOST_Window : public GHOST_IWindow {
void getCursorGrabState(GHOST_TGrabCursorMode &mode,
GHOST_TAxisFlag &axis_flag,
GHOST_Rect &bounds);
GHOST_Rect &bounds,
bool &use_software_cursor);
/**
* Return true when a software cursor should be used.
*/
bool getCursorGrabUseSoftwareDisplay();
/**
* Sets the progress bar value displayed in the window/application icon

View File

@ -492,6 +492,11 @@ GHOST_TSuccess GHOST_WindowWayland::setWindowCursorShape(GHOST_TStandardCursor s
return ok;
}
bool GHOST_WindowWayland::getCursorGrabUseSoftwareDisplay()
{
return m_system->getCursorGrabUseSoftwareDisplay(m_cursorGrab);
}
GHOST_TSuccess GHOST_WindowWayland::setWindowCustomCursorShape(
uint8_t *bitmap, uint8_t *mask, int sizex, int sizey, int hotX, int hotY, bool canInvertColor)
{

View File

@ -52,6 +52,7 @@ class GHOST_WindowWayland : public GHOST_Window {
int hotX,
int hotY,
bool canInvertColor) override;
bool getCursorGrabUseSoftwareDisplay() override;
void setTitle(const char *title) override;

View File

@ -158,9 +158,13 @@ static bool wm_software_cursor_needed_for_window(const wmWindow *win, struct Gra
if (GHOST_GetCursorVisibility(win->ghostwin)) {
/* NOTE: The value in `win->grabcursor` can't be used as it
* doesn't always match GHOST's value in the case of tablet events. */
GHOST_GetCursorGrabState(
win->ghostwin, &grab_state->mode, &grab_state->wrap_axis, grab_state->bounds);
if (grab_state->mode == GHOST_kGrabWrap) {
bool use_software_cursor;
GHOST_GetCursorGrabState(win->ghostwin,
&grab_state->mode,
&grab_state->wrap_axis,
grab_state->bounds,
&use_software_cursor);
if (use_software_cursor) {
return true;
}
}