Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <campbell@blender.org>2022-06-08 06:01:31 +0300
committerCampbell Barton <campbell@blender.org>2022-06-08 06:16:14 +0300
commita1d2efd190038c7615bd3bb459dc86c8b3a8ecdc (patch)
tree2f709a3566c84d45ccd352104573626a445cdb41
parentb3e0101a35b2885212aa199335564dca98dc50cd (diff)
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.
-rw-r--r--intern/ghost/GHOST_C-api.h9
-rw-r--r--intern/ghost/GHOST_ISystem.h5
-rw-r--r--intern/ghost/GHOST_IWindow.h4
-rw-r--r--intern/ghost/GHOST_Types.h2
-rw-r--r--intern/ghost/intern/GHOST_C-api.cpp23
-rw-r--r--intern/ghost/intern/GHOST_System.cpp5
-rw-r--r--intern/ghost/intern/GHOST_System.h3
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.cpp10
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.h2
-rw-r--r--intern/ghost/intern/GHOST_Window.cpp23
-rw-r--r--intern/ghost/intern/GHOST_Window.h2
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c124
12 files changed, 210 insertions, 2 deletions
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index ae749eb3b8c..57714083656 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -402,6 +402,10 @@ extern GHOST_TSuccess GHOST_SetCursorPosition(GHOST_SystemHandle systemhandle,
int32_t x,
int32_t y);
+GHOST_TSuccess GHOST_GetCursorGrabState(GHOST_WindowHandle windowhandle,
+ GHOST_TAxisFlag *r_wrap_axis,
+ int r_bounds[4]);
+
/**
* Grabs the cursor for a modal operation, to keep receiving
* events when the mouse is outside the window. X11 only, others
@@ -897,6 +901,11 @@ extern int setConsoleWindowState(GHOST_TConsoleWindowState action);
extern int GHOST_UseNativePixels(void);
/**
+ * Warp the cursor, if supported.
+ */
+extern int GHOST_SupportsCursorWarp(void);
+
+/**
* Focus window after opening, or put them in the background.
*/
extern void GHOST_UseWindowFocus(int use_focus);
diff --git a/intern/ghost/GHOST_ISystem.h b/intern/ghost/GHOST_ISystem.h
index bb91abbadec..8a4412403f7 100644
--- a/intern/ghost/GHOST_ISystem.h
+++ b/intern/ghost/GHOST_ISystem.h
@@ -305,6 +305,11 @@ class GHOST_ISystem {
virtual bool useNativePixel(void) = 0;
/**
+ * Return true when warping the cursor is supported.
+ */
+ virtual bool supportsCursorWarp() = 0;
+
+ /**
* Focus window after opening, or put them in the background.
*/
virtual void useWindowFocus(const bool use_focus) = 0;
diff --git a/intern/ghost/GHOST_IWindow.h b/intern/ghost/GHOST_IWindow.h
index 183e97a4b55..681ad9c5b07 100644
--- a/intern/ghost/GHOST_IWindow.h
+++ b/intern/ghost/GHOST_IWindow.h
@@ -254,6 +254,10 @@ class GHOST_IWindow {
*/
virtual GHOST_TSuccess setCursorShape(GHOST_TStandardCursor cursorShape) = 0;
+ virtual GHOST_TSuccess getCursorGrabBounds(GHOST_Rect &bounds) = 0;
+
+ virtual GHOST_TSuccess getCursorGrabState(GHOST_TAxisFlag &axis_flag, GHOST_Rect &bounds) = 0;
+
/**
* Test if the standard cursor shape is supported by current platform.
* \return Indication of success.
diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h
index e2ed9830e4e..841c0c33ee1 100644
--- a/intern/ghost/GHOST_Types.h
+++ b/intern/ghost/GHOST_Types.h
@@ -403,6 +403,8 @@ typedef enum {
GHOST_kGrabHide,
} GHOST_TGrabCursorMode;
+#define GHOST_GRAB_NEEDS_SOFTWARE_CURSOR_FOR_WARP(grab) ((grab) == GHOST_kGrabWrap)
+
typedef enum {
/** Axis that cursor grab will wrap. */
GHOST_kGrabAxisNone = 0,
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp
index 9374d087408..f4c978a88d4 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -376,6 +376,23 @@ GHOST_TSuccess GHOST_SetCursorGrab(GHOST_WindowHandle windowhandle,
mode, wrap_axis, bounds ? &bounds_rect : nullptr, mouse_ungrab_xy ? mouse_xy : nullptr);
}
+GHOST_TSuccess GHOST_GetCursorGrabState(GHOST_WindowHandle windowhandle,
+ GHOST_TAxisFlag *r_axis_flag,
+ int r_bounds[4])
+{
+ GHOST_IWindow *window = (GHOST_IWindow *)windowhandle;
+ GHOST_Rect bounds_rect;
+ if (!window->getCursorGrabState(*r_axis_flag, bounds_rect)) {
+ return GHOST_kFailure;
+ }
+
+ 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;
+ return GHOST_kSuccess;
+}
+
GHOST_TSuccess GHOST_GetModifierKeyState(GHOST_SystemHandle systemhandle,
GHOST_TModifierKeyMask mask,
int *isDown)
@@ -815,6 +832,12 @@ int GHOST_UseNativePixels(void)
return system->useNativePixel();
}
+int GHOST_SupportsCursorWarp(void)
+{
+ GHOST_ISystem *system = GHOST_ISystem::getSystem();
+ return system->supportsCursorWarp();
+}
+
void GHOST_UseWindowFocus(int use_focus)
{
GHOST_ISystem *system = GHOST_ISystem::getSystem();
diff --git a/intern/ghost/intern/GHOST_System.cpp b/intern/ghost/intern/GHOST_System.cpp
index 1ddf884bbc5..2aad4f2ea29 100644
--- a/intern/ghost/intern/GHOST_System.cpp
+++ b/intern/ghost/intern/GHOST_System.cpp
@@ -390,6 +390,11 @@ void GHOST_System::useWindowFocus(const bool use_focus)
m_windowFocus = use_focus;
}
+bool GHOST_System::supportsCursorWarp()
+{
+ return true;
+}
+
void GHOST_System::initDebug(GHOST_Debug debug)
{
m_is_debug_enabled = debug.flags & GHOST_kDebugDefault;
diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h
index 4a3cded1fbd..9f2fba1a2c6 100644
--- a/intern/ghost/intern/GHOST_System.h
+++ b/intern/ghost/intern/GHOST_System.h
@@ -151,10 +151,13 @@ class GHOST_System : public GHOST_ISystem {
bool useNativePixel(void);
bool m_nativePixel;
+ bool supportsCursorWarp(void);
+
/**
* Focus window after opening, or put them in the background.
*/
void useWindowFocus(const bool use_focus);
+
bool m_windowFocus;
/**
diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp
index f6eabb60b05..7e471d5c7d4 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.cpp
+++ b/intern/ghost/intern/GHOST_SystemWayland.cpp
@@ -1920,6 +1920,11 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(bool visible)
return GHOST_kSuccess;
}
+bool GHOST_SystemWayland::supportsCursorWarp()
+{
+ return false;
+}
+
GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mode,
const GHOST_TGrabCursorMode mode_current,
@@ -1948,8 +1953,9 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mo
const bool was_lock = MODE_NEEDS_LOCK(mode_current);
const bool use_lock = MODE_NEEDS_LOCK(mode);
- const bool was_hide = MODE_NEEDS_HIDE(mode_current);
- const bool use_hide = MODE_NEEDS_HIDE(mode);
+ /* Check for wrap as #supportsCursorWarp isn't supproted. */
+ 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_confine = MODE_NEEDS_CONFINE(mode_current);
const bool use_confine = MODE_NEEDS_CONFINE(mode);
diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h
index eeb65eb4fc3..5b3e4f8ed75 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.h
+++ b/intern/ghost/intern/GHOST_SystemWayland.h
@@ -103,6 +103,8 @@ class GHOST_SystemWayland : public GHOST_System {
GHOST_TSuccess setCursorVisibility(bool visible);
+ bool supportsCursorWarp();
+
GHOST_TSuccess setCursorGrab(const GHOST_TGrabCursorMode mode,
const GHOST_TGrabCursorMode mode_current,
wl_surface *surface);
diff --git a/intern/ghost/intern/GHOST_Window.cpp b/intern/ghost/intern/GHOST_Window.cpp
index 954f0bc244d..12963abf128 100644
--- a/intern/ghost/intern/GHOST_Window.cpp
+++ b/intern/ghost/intern/GHOST_Window.cpp
@@ -155,10 +155,33 @@ GHOST_TSuccess GHOST_Window::setCursorGrab(GHOST_TGrabCursorMode mode,
GHOST_TSuccess GHOST_Window::getCursorGrabBounds(GHOST_Rect &bounds)
{
+ if (m_cursorGrab != GHOST_kGrabWrap) {
+ return GHOST_kFailure;
+ }
bounds = m_cursorGrabBounds;
return (bounds.m_l == -1 && bounds.m_r == -1) ? GHOST_kFailure : GHOST_kSuccess;
}
+GHOST_TSuccess GHOST_Window::getCursorGrabState(GHOST_TAxisFlag &wrap_axis, GHOST_Rect &bounds)
+{
+ if (m_cursorGrab == GHOST_kGrabDisable) {
+ return GHOST_kFailure;
+ }
+
+ if (m_cursorGrab == GHOST_kGrabWrap) {
+ bounds = m_cursorGrabBounds;
+ wrap_axis = m_cursorGrabAxis;
+ }
+ else {
+ bounds.m_l = -1;
+ bounds.m_r = -1;
+ bounds.m_t = -1;
+ bounds.m_b = -1;
+ wrap_axis = GHOST_kGrabAxisNone;
+ }
+ return GHOST_kSuccess;
+}
+
GHOST_TSuccess GHOST_Window::setCursorShape(GHOST_TStandardCursor cursorShape)
{
if (setWindowCursorShape(cursorShape)) {
diff --git a/intern/ghost/intern/GHOST_Window.h b/intern/ghost/intern/GHOST_Window.h
index 794e834d5c7..b0042a33a00 100644
--- a/intern/ghost/intern/GHOST_Window.h
+++ b/intern/ghost/intern/GHOST_Window.h
@@ -152,6 +152,8 @@ class GHOST_Window : public GHOST_IWindow {
*/
GHOST_TSuccess getCursorGrabBounds(GHOST_Rect &bounds);
+ GHOST_TSuccess getCursorGrabState(GHOST_TAxisFlag &axis_flag, GHOST_Rect &bounds);
+
/**
* Sets the progress bar value displayed in the window/application icon
* \param progress: The progress percentage (0.0 to 1.0).
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
@@ -119,6 +119,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