From a1d2efd190038c7615bd3bb459dc86c8b3a8ecdc Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 8 Jun 2022 13:01:31 +1000 Subject: GHOST/Wayland: draw a software-cursor when wrapping cursor motion As Wayland doesn't support moving the cursor, draw a cross-hair cursor at the location used by Blender. Without this, the cursor was locked at the location where grab started, making some actions unusable since the cursor location was invisible. Resolves T77311. --- source/blender/windowmanager/intern/wm_draw.c | 124 ++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) (limited to 'source/blender/windowmanager/intern/wm_draw.c') diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index d2ade7b0376..810cc9001fe 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -118,6 +118,113 @@ static void wm_paintcursor_draw(bContext *C, ScrArea *area, ARegion *region) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Draw Software Cursor + * + * Draw the cursor instead of relying on the graphical environment. + * Needed when setting the cursor position (warping) isn't supported (GHOST/WAYLAND). + * \{ */ + +/** + * Track the state of the last drawn cursor. + */ +static struct { + int8_t enabled; + int winid; + int xy[2]; +} g_software_cursor = { + .enabled = -1, + .winid = -1, +}; + +static bool wm_software_cursor_needed(void) +{ + if (UNLIKELY(g_software_cursor.enabled == -1)) { + g_software_cursor.enabled = !GHOST_SupportsCursorWarp(); + } + return g_software_cursor.enabled; +} + +static bool wm_software_cursor_needed_for_window(const wmWindow *win) +{ + BLI_assert(wm_software_cursor_needed()); + return (win->grabcursor == GHOST_kGrabWrap) && GHOST_GetCursorVisibility(win->ghostwin); +} + +static bool wm_software_cursor_motion_test(const wmWindow *win) +{ + return (g_software_cursor.winid != win->winid) || + (g_software_cursor.xy[0] != win->eventstate->xy[0]) || + (g_software_cursor.xy[1] != win->eventstate->xy[1]); +} + +static void wm_software_cursor_motion_update(const wmWindow *win) +{ + + g_software_cursor.winid = win->winid; + g_software_cursor.xy[0] = win->eventstate->xy[0]; + g_software_cursor.xy[1] = win->eventstate->xy[1]; +} + +static void wm_software_cursor_motion_clear(void) +{ + g_software_cursor.winid = -1; + g_software_cursor.xy[0] = -1; + g_software_cursor.xy[1] = -1; +} + +static void wm_software_cursor_draw(wmWindow *win) +{ + int x = win->eventstate->xy[0]; + int y = win->eventstate->xy[1]; + + int bounds[4]; + GHOST_TAxisFlag wrap_axis = 0; + if (GHOST_GetCursorGrabState(win->ghostwin, &wrap_axis, bounds) != GHOST_kFailure) { + if (wrap_axis & GHOST_kAxisX) { + const int min = bounds[0]; + const int max = bounds[2]; + if (min != max) { + x = mod_i(x - min, max - min) + min; + } + } + if (wrap_axis & GHOST_kGrabAxisY) { + const int height = WM_window_pixels_y(win); + const int min = height - bounds[1]; + const int max = height - bounds[3]; + if (min != max) { + y = mod_i(y - max, min - max) + max; + } + } + } + + /* Draw a primitive cross-hair cursor. + * NOTE: the `win->cursor` could be used for drawing although it's complicated as some cursors + * are set by the operating-system, where the pixel information isn't easily available. */ + const float unit = max_ff(U.dpi_fac, 1.0f); + uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor4f(1, 1, 1, 1); + { + const int ofs_line = (8 * unit); + const int ofs_size = (2 * unit); + immRecti(pos, x - ofs_line, y - ofs_size, x + ofs_line, y + ofs_size); + immRecti(pos, x - ofs_size, y - ofs_line, x + ofs_size, y + ofs_line); + } + immUniformColor4f(0, 0, 0, 1); + { + const int ofs_line = (7 * unit); + const int ofs_size = (1 * unit); + immRecti(pos, x - ofs_line, y - ofs_size, x + ofs_line, y + ofs_size); + immRecti(pos, x - ofs_size, y - ofs_line, x + ofs_size, y + ofs_line); + } + immUnbindProgram(); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Post Draw Region on display handlers * \{ */ @@ -862,6 +969,7 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) /* always draw, not only when screen tagged */ if (win->gesture.first) { wm_gesture_draw(win); + wmWindowViewport(win); } /* Needs pixel coords in screen. */ @@ -870,6 +978,16 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) wmWindowViewport(win); } + if (wm_software_cursor_needed()) { + if (wm_software_cursor_needed_for_window(win)) { + wm_software_cursor_draw(win); + wm_software_cursor_motion_update(win); + } + else { + wm_software_cursor_motion_clear(); + } + } + GPU_debug_group_end(); } @@ -1020,6 +1138,12 @@ static bool wm_draw_update_test_window(Main *bmain, bContext *C, wmWindow *win) return true; } + if (wm_software_cursor_needed()) { + if (wm_software_cursor_needed_for_window(win) && wm_software_cursor_motion_test(win)) { + return true; + } + } + #ifndef WITH_XR_OPENXR UNUSED_VARS(wm); #endif -- cgit v1.2.3