GHOST/Wayland: support displaying custom software cursors

Add a method to access the custom cursor from GHOST which is used
for drawing a software cursor. This means the knife tools cursor now
work as expected.

Although non-custom cursors are still not supported.
This commit is contained in:
Campbell Barton 2022-06-18 15:10:03 +10:00
parent 881d1c9bc2
commit 498f079d2c
11 changed files with 175 additions and 22 deletions

View File

@ -364,6 +364,9 @@ extern GHOST_TSuccess GHOST_SetCustomCursorShape(GHOST_WindowHandle windowhandle
int hotY,
bool canInvertColor);
extern GHOST_TSuccess GHOST_GetCursorBitmap(GHOST_WindowHandle windowhandle,
GHOST_CursorBitmapRef *bitmap);
/**
* Returns the visibility state of the cursor.
* \param windowhandle: The handle to the window.

View File

@ -285,6 +285,8 @@ class GHOST_IWindow {
int hotY,
bool canInvertColor) = 0;
virtual GHOST_TSuccess getCursorBitmap(GHOST_CursorBitmapRef *bitmap) = 0;
/**
* Returns the visibility state of the cursor.
* \return The visibility state of the cursor.

View File

@ -44,6 +44,16 @@ GHOST_DECLARE_HANDLE(GHOST_XrContextHandle);
typedef void (*GHOST_TBacktraceFn)(void *file_handle);
/**
* A reference to cursor bitmap data.
*/
typedef struct {
/** `RGBA` bytes. */
const uint8_t *data;
int data_size[2];
int hot_spot[2];
} GHOST_CursorBitmapRef;
typedef struct {
int flags;
} GHOST_GLSettings;

View File

@ -326,6 +326,14 @@ GHOST_TSuccess GHOST_SetCustomCursorShape(GHOST_WindowHandle windowhandle,
return window->setCustomCursorShape(bitmap, mask, sizex, sizey, hotX, hotY, canInvertColor);
}
GHOST_TSuccess GHOST_GetCursorBitmap(GHOST_WindowHandle windowhandle,
GHOST_CursorBitmapRef *bitmap)
{
GHOST_IWindow *window = (GHOST_IWindow *)windowhandle;
return window->getCursorBitmap(bitmap);
}
bool GHOST_GetCursorVisibility(GHOST_WindowHandle windowhandle)
{
GHOST_IWindow *window = (GHOST_IWindow *)windowhandle;

View File

@ -96,6 +96,7 @@ struct buffer_t {
struct cursor_t {
bool visible = false;
bool is_custom = false;
struct wl_surface *wl_surface = nullptr;
struct wl_buffer *wl_buffer = nullptr;
struct wl_cursor_image wl_image = {0};
@ -2587,6 +2588,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape)
return GHOST_kFailure;
}
c->is_custom = false;
c->wl_buffer = buffer;
c->wl_image = *image;
@ -2650,6 +2652,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap,
nullptr, cursor->file_buffer->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (cursor->file_buffer->data == MAP_FAILED) {
cursor->file_buffer->data = nullptr;
close(fd);
return GHOST_kFailure;
}
@ -2694,6 +2697,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap,
}
}
cursor->is_custom = true;
cursor->wl_buffer = buffer;
cursor->wl_image.width = uint32_t(sizex);
cursor->wl_image.height = uint32_t(sizey);
@ -2705,6 +2709,27 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap,
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_SystemWayland::getCursorBitmap(GHOST_CursorBitmapRef *bitmap)
{
cursor_t *cursor = &d->inputs[0]->cursor;
if (cursor->file_buffer->data == nullptr) {
return GHOST_kFailure;
}
if (!cursor->is_custom) {
return GHOST_kFailure;
}
bitmap->data_size[0] = cursor->wl_image.width;
bitmap->data_size[1] = cursor->wl_image.height;
bitmap->hot_spot[0] = cursor->wl_image.hotspot_x;
bitmap->hot_spot[1] = cursor->wl_image.hotspot_y;
bitmap->data = (uint8_t *)static_cast<void *>(cursor->file_buffer->data);
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(bool visible)
{
if (d->inputs.empty()) {

View File

@ -122,6 +122,8 @@ class GHOST_SystemWayland : public GHOST_System {
int hotY,
bool canInvertColor);
GHOST_TSuccess getCursorBitmap(GHOST_CursorBitmapRef *bitmap);
GHOST_TSuccess setCursorVisibility(bool visible);
bool supportsCursorWarp();

View File

@ -208,6 +208,12 @@ GHOST_TSuccess GHOST_Window::setCustomCursorShape(
return GHOST_kFailure;
}
GHOST_TSuccess GHOST_Window::getCursorBitmap(GHOST_CursorBitmapRef * /*bitmap*/)
{
/* Sub-classes may override. */
return GHOST_kFailure;
}
void GHOST_Window::setAcceptDragOperation(bool canAccept)
{
m_canAcceptDragOperation = canAccept;

View File

@ -117,6 +117,8 @@ class GHOST_Window : public GHOST_IWindow {
int hotY,
bool canInvertColor);
GHOST_TSuccess getCursorBitmap(GHOST_CursorBitmapRef *bitmap);
/**
* Returns the visibility state of the cursor.
* \return The visibility state of the cursor.

View File

@ -503,6 +503,11 @@ GHOST_TSuccess GHOST_WindowWayland::setWindowCustomCursorShape(
return m_system->setCustomCursorShape(bitmap, mask, sizex, sizey, hotX, hotY, canInvertColor);
}
GHOST_TSuccess GHOST_WindowWayland::getCursorBitmap(GHOST_CursorBitmapRef *bitmap)
{
return m_system->getCursorBitmap(bitmap);
}
void GHOST_WindowWayland::setTitle(const char *title)
{
xdg_toplevel_set_title(w->xdg_toplevel, title);

View File

@ -54,6 +54,8 @@ class GHOST_WindowWayland : public GHOST_Window {
bool canInvertColor) override;
bool getCursorGrabUseSoftwareDisplay() override;
GHOST_TSuccess getCursorBitmap(GHOST_CursorBitmapRef *bitmap) override;
void setTitle(const char *title) override;
std::string getTitle() const override;

View File

@ -41,6 +41,7 @@
#include "GPU_debug.h"
#include "GPU_framebuffer.h"
#include "GPU_immediate.h"
#include "GPU_matrix.h"
#include "GPU_state.h"
#include "GPU_texture.h"
#include "GPU_viewport.h"
@ -193,27 +194,69 @@ static void wm_software_cursor_motion_clear(void)
g_software_cursor.xy[1] = -1;
}
static void wm_software_cursor_draw(wmWindow *win, const struct GrabState *grab_state)
static void wm_software_cursor_draw_bitmap(const int event_xy[2],
const GHOST_CursorBitmapRef *bitmap)
{
int x = win->eventstate->xy[0];
int y = win->eventstate->xy[1];
GPU_blend(GPU_BLEND_ALPHA);
if (grab_state->wrap_axis & GHOST_kAxisX) {
const int min = grab_state->bounds[0];
const int max = grab_state->bounds[2];
if (min != max) {
x = mod_i(x - min, max - min) + min;
}
}
if (grab_state->wrap_axis & GHOST_kGrabAxisY) {
const int height = WM_window_pixels_y(win);
const int min = height - grab_state->bounds[1];
const int max = height - grab_state->bounds[3];
if (min != max) {
y = mod_i(y - max, min - max) + max;
}
}
float gl_matrix[4][4];
GPUTexture *texture = GPU_texture_create_2d(
"softeare_cursor", bitmap->data_size[0], bitmap->data_size[1], 1, GPU_RGBA8, NULL);
GPU_texture_update(texture, GPU_DATA_UBYTE, bitmap->data);
GPU_texture_filter_mode(texture, false);
GPU_matrix_push();
const int scale = (int)U.pixelsize;
unit_m4(gl_matrix);
gl_matrix[3][0] = event_xy[0] - (bitmap->hot_spot[0] * scale);
gl_matrix[3][1] = event_xy[1] - ((bitmap->data_size[1] - bitmap->hot_spot[1]) * scale);
gl_matrix[0][0] = bitmap->data_size[0] * scale;
gl_matrix[1][1] = bitmap->data_size[1] * scale;
GPU_matrix_mul(gl_matrix);
GPUVertFormat *imm_format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(imm_format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
uint texCoord = GPU_vertformat_attr_add(
imm_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
/* Use 3D image for correct display of planar tracked images. */
immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_MODULATE_ALPHA);
immBindTexture("image", texture);
immUniform1f("alpha", 1.0f);
immBegin(GPU_PRIM_TRI_FAN, 4);
immAttr2f(texCoord, 0.0f, 1.0f);
immVertex3f(pos, 0.0f, 0.0f, 0.0f);
immAttr2f(texCoord, 1.0f, 1.0f);
immVertex3f(pos, 1.0f, 0.0f, 0.0f);
immAttr2f(texCoord, 1.0f, 0.0f);
immVertex3f(pos, 1.0f, 1.0f, 0.0f);
immAttr2f(texCoord, 0.0f, 0.0f);
immVertex3f(pos, 0.0f, 1.0f, 0.0f);
immEnd();
immUnbindProgram();
GPU_matrix_pop();
GPU_texture_unbind(texture);
GPU_texture_free(texture);
GPU_blend(GPU_BLEND_NONE);
}
static void wm_software_cursor_draw_crosshair(const int event_xy[2])
{
/* 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. */
@ -226,19 +269,64 @@ static void wm_software_cursor_draw(wmWindow *win, const struct GrabState *grab_
{
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);
immRecti(pos,
event_xy[0] - ofs_line,
event_xy[1] - ofs_size,
event_xy[0] + ofs_line,
event_xy[1] + ofs_size);
immRecti(pos,
event_xy[0] - ofs_size,
event_xy[1] - ofs_line,
event_xy[0] + ofs_size,
event_xy[1] + 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);
immRecti(pos,
event_xy[0] - ofs_line,
event_xy[1] - ofs_size,
event_xy[0] + ofs_line,
event_xy[1] + ofs_size);
immRecti(pos,
event_xy[0] - ofs_size,
event_xy[1] - ofs_line,
event_xy[0] + ofs_size,
event_xy[1] + ofs_line);
}
immUnbindProgram();
}
static void wm_software_cursor_draw(wmWindow *win, const struct GrabState *grab_state)
{
int event_xy[2] = {UNPACK2(win->eventstate->xy)};
if (grab_state->wrap_axis & GHOST_kAxisX) {
const int min = grab_state->bounds[0];
const int max = grab_state->bounds[2];
if (min != max) {
event_xy[0] = mod_i(event_xy[0] - min, max - min) + min;
}
}
if (grab_state->wrap_axis & GHOST_kGrabAxisY) {
const int height = WM_window_pixels_y(win);
const int min = height - grab_state->bounds[1];
const int max = height - grab_state->bounds[3];
if (min != max) {
event_xy[1] = mod_i(event_xy[1] - max, min - max) + max;
}
}
GHOST_CursorBitmapRef bitmap = {0};
if (GHOST_GetCursorBitmap(win->ghostwin, &bitmap) == GHOST_kSuccess) {
wm_software_cursor_draw_bitmap(event_xy, &bitmap);
}
else {
wm_software_cursor_draw_crosshair(event_xy);
}
}
/** \} */
/* -------------------------------------------------------------------- */