Cleanup: move internal Wayland cursor API's into their own doxy section
Also resolve building when USE_EVENT_BACKGROUND_THREAD is disabled.
This commit is contained in:
parent
224658266f
commit
46a9530a75
@ -120,6 +120,11 @@ static void gwl_seat_capability_pointer_disable(GWL_Seat *seat);
|
||||
static void gwl_seat_capability_keyboard_disable(GWL_Seat *seat);
|
||||
static void gwl_seat_capability_touch_disable(GWL_Seat *seat);
|
||||
|
||||
static void gwl_seat_cursor_anim_begin(GWL_Seat *seat);
|
||||
static void gwl_seat_cursor_anim_begin_if_needed(GWL_Seat *seat);
|
||||
static void gwl_seat_cursor_anim_end(GWL_Seat *seat);
|
||||
static void gwl_seat_cursor_anim_reset(GWL_Seat *seat);
|
||||
|
||||
static bool gwl_registry_entry_remove_by_name(GWL_Display *display,
|
||||
uint32_t name,
|
||||
int *r_interface_slot);
|
||||
@ -135,11 +140,6 @@ static void gwl_display_event_thread_destroy(GWL_Display *display);
|
||||
|
||||
static void ghost_wl_display_lock_without_input(wl_display *wl_display, std::mutex *server_mutex);
|
||||
|
||||
static void cursor_anim_begin_if_needed(GWL_Seat *seat);
|
||||
static void cursor_anim_begin(GWL_Seat *seat);
|
||||
static void cursor_anim_end(GWL_Seat *seat);
|
||||
static void cursor_anim_reset(GWL_Seat *seat);
|
||||
|
||||
/** Default size for pending event vector. */
|
||||
constexpr size_t events_pending_default_size = 4096 / sizeof(void *);
|
||||
|
||||
@ -1336,7 +1336,7 @@ static void gwl_display_destroy(GWL_Display *display)
|
||||
|
||||
/* Stop all animated cursors (freeing their #GWL_Cursor_AnimHandle). */
|
||||
for (GWL_Seat *seat : display->seats) {
|
||||
cursor_anim_end(seat);
|
||||
gwl_seat_cursor_anim_end(seat);
|
||||
}
|
||||
|
||||
/* For typical WAYLAND use this will always be set.
|
||||
@ -2026,6 +2026,7 @@ static const char *ghost_wl_mime_send[] = {
|
||||
"text/plain",
|
||||
};
|
||||
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
static void pthread_set_min_priority(pthread_t handle)
|
||||
{
|
||||
int policy;
|
||||
@ -2046,6 +2047,7 @@ static void thread_set_min_priority(std::thread &thread)
|
||||
* This cast might be avoided with clever template use. */
|
||||
pthread_set_min_priority(reinterpret_cast<pthread_t>(thread.native_handle()));
|
||||
}
|
||||
#endif /* USE_EVENT_BACKGROUND_THREAD */
|
||||
|
||||
static int memfd_create_sealed(const char *name)
|
||||
{
|
||||
@ -2383,6 +2385,321 @@ static char *read_file_as_buffer(const int fd, const bool nil_terminate, size_t
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Private Cursor API
|
||||
* \{ */
|
||||
|
||||
static void cursor_buffer_set_surface_impl(const wl_cursor_image *wl_image,
|
||||
wl_buffer *buffer,
|
||||
wl_surface *wl_surface,
|
||||
const int scale)
|
||||
{
|
||||
const int32_t image_size_x = int32_t(wl_image->width);
|
||||
const int32_t image_size_y = int32_t(wl_image->height);
|
||||
GHOST_ASSERT((image_size_x % scale) == 0 && (image_size_y % scale) == 0,
|
||||
"The size must be a multiple of the scale!");
|
||||
|
||||
wl_surface_set_buffer_scale(wl_surface, scale);
|
||||
wl_surface_attach(wl_surface, buffer, 0, 0);
|
||||
wl_surface_damage(wl_surface, 0, 0, image_size_x, image_size_y);
|
||||
wl_surface_commit(wl_surface);
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed to ensure the cursor size is always a multiple of scale.
|
||||
*/
|
||||
static int cursor_buffer_compatible_scale_from_image(const wl_cursor_image *wl_image, int scale)
|
||||
{
|
||||
const int32_t image_size_x = int32_t(wl_image->width);
|
||||
const int32_t image_size_y = int32_t(wl_image->height);
|
||||
while (scale > 1) {
|
||||
if ((image_size_x % scale) == 0 && (image_size_y % scale) == 0) {
|
||||
break;
|
||||
}
|
||||
scale -= 1;
|
||||
}
|
||||
return scale;
|
||||
}
|
||||
|
||||
static const wl_cursor *gwl_seat_cursor_find_from_shape(GWL_Seat *seat,
|
||||
const GHOST_TStandardCursor shape,
|
||||
const char **r_cursor_name)
|
||||
{
|
||||
/* Caller must lock `server_mutex`. */
|
||||
GWL_Cursor *cursor = &seat->cursor;
|
||||
wl_cursor *wl_cursor = nullptr;
|
||||
|
||||
const char *cursor_name = ghost_wl_cursors.names[shape];
|
||||
if (cursor_name[0] != '\0') {
|
||||
if (!cursor->wl.theme) {
|
||||
/* The cursor wl_surface hasn't entered an output yet. Initialize theme with scale 1. */
|
||||
cursor->wl.theme = wl_cursor_theme_load(
|
||||
cursor->theme_name.c_str(), cursor->theme_size, seat->system->wl_shm_get());
|
||||
}
|
||||
|
||||
if (cursor->wl.theme) {
|
||||
wl_cursor = wl_cursor_theme_get_cursor(cursor->wl.theme, cursor_name);
|
||||
if (!wl_cursor) {
|
||||
GHOST_PRINT("cursor '" << cursor_name << "' does not exist" << std::endl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (r_cursor_name && wl_cursor) {
|
||||
*r_cursor_name = cursor_name;
|
||||
}
|
||||
return wl_cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the buffer defined by #gwl_seat_cursor_buffer_set without changing anything else,
|
||||
* so #gwl_seat_cursor_buffer_hide can be used to display it again.
|
||||
*
|
||||
* The caller is responsible for setting `seat->cursor.visible`.
|
||||
*/
|
||||
static void gwl_seat_cursor_buffer_show(GWL_Seat *seat)
|
||||
{
|
||||
const GWL_Cursor *cursor = &seat->cursor;
|
||||
|
||||
if (seat->wl.pointer) {
|
||||
const int scale = cursor->is_custom ? cursor->custom_scale : seat->pointer.theme_scale;
|
||||
const int32_t hotspot_x = int32_t(cursor->wl.image.hotspot_x) / scale;
|
||||
const int32_t hotspot_y = int32_t(cursor->wl.image.hotspot_y) / scale;
|
||||
wl_pointer_set_cursor(
|
||||
seat->wl.pointer, seat->pointer.serial, cursor->wl.surface_cursor, hotspot_x, hotspot_y);
|
||||
}
|
||||
|
||||
if (!seat->wp.tablet_tools.empty()) {
|
||||
const int scale = cursor->is_custom ? cursor->custom_scale : seat->tablet.theme_scale;
|
||||
const int32_t hotspot_x = int32_t(cursor->wl.image.hotspot_x) / scale;
|
||||
const int32_t hotspot_y = int32_t(cursor->wl.image.hotspot_y) / scale;
|
||||
for (zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : seat->wp.tablet_tools) {
|
||||
GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(
|
||||
zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2));
|
||||
zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2,
|
||||
seat->tablet.serial,
|
||||
tablet_tool->wl.surface_cursor,
|
||||
hotspot_x,
|
||||
hotspot_y);
|
||||
#ifdef USE_KDE_TABLET_HIDDEN_CURSOR_HACK
|
||||
wl_surface_commit(tablet_tool->wl.surface_cursor);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
gwl_seat_cursor_anim_reset(seat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the buffer defined by #gwl_seat_cursor_buffer_set without changing anything else,
|
||||
* so #gwl_seat_cursor_buffer_show can be used to display it again.
|
||||
*
|
||||
* The caller is responsible for setting `seat->cursor.visible`.
|
||||
*/
|
||||
static void gwl_seat_cursor_buffer_hide(GWL_Seat *seat)
|
||||
{
|
||||
gwl_seat_cursor_anim_end(seat);
|
||||
|
||||
wl_pointer_set_cursor(seat->wl.pointer, seat->pointer.serial, nullptr, 0, 0);
|
||||
for (zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : seat->wp.tablet_tools) {
|
||||
zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, seat->tablet.serial, nullptr, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void gwl_seat_cursor_buffer_set(const GWL_Seat *seat,
|
||||
const wl_cursor_image *wl_image,
|
||||
wl_buffer *buffer)
|
||||
{
|
||||
const GWL_Cursor *cursor = &seat->cursor;
|
||||
const bool visible = (cursor->visible && cursor->is_hardware);
|
||||
|
||||
/* This is a requirement of WAYLAND, when this isn't the case,
|
||||
* it causes Blender's window to close intermittently. */
|
||||
if (seat->wl.pointer) {
|
||||
const int scale = cursor_buffer_compatible_scale_from_image(
|
||||
wl_image, cursor->is_custom ? cursor->custom_scale : seat->pointer.theme_scale);
|
||||
const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale;
|
||||
const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale;
|
||||
cursor_buffer_set_surface_impl(wl_image, buffer, cursor->wl.surface_cursor, scale);
|
||||
wl_pointer_set_cursor(seat->wl.pointer,
|
||||
seat->pointer.serial,
|
||||
visible ? cursor->wl.surface_cursor : nullptr,
|
||||
hotspot_x,
|
||||
hotspot_y);
|
||||
}
|
||||
|
||||
/* Set the cursor for all tablet tools as well. */
|
||||
if (!seat->wp.tablet_tools.empty()) {
|
||||
const int scale = cursor_buffer_compatible_scale_from_image(
|
||||
wl_image, cursor->is_custom ? cursor->custom_scale : seat->tablet.theme_scale);
|
||||
const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale;
|
||||
const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale;
|
||||
for (zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : seat->wp.tablet_tools) {
|
||||
GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(
|
||||
zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2));
|
||||
cursor_buffer_set_surface_impl(wl_image, buffer, tablet_tool->wl.surface_cursor, scale);
|
||||
zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2,
|
||||
seat->tablet.serial,
|
||||
visible ? tablet_tool->wl.surface_cursor : nullptr,
|
||||
hotspot_x,
|
||||
hotspot_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gwl_seat_cursor_buffer_set_current(GWL_Seat *seat)
|
||||
{
|
||||
const GWL_Cursor *cursor = &seat->cursor;
|
||||
gwl_seat_cursor_anim_end(seat);
|
||||
gwl_seat_cursor_buffer_set(seat, &cursor->wl.image, cursor->wl.buffer);
|
||||
gwl_seat_cursor_anim_begin_if_needed(seat);
|
||||
}
|
||||
|
||||
enum eCursorSetMode {
|
||||
CURSOR_VISIBLE_ALWAYS_SET = 1,
|
||||
CURSOR_VISIBLE_ONLY_HIDE,
|
||||
CURSOR_VISIBLE_ONLY_SHOW,
|
||||
};
|
||||
|
||||
static void gwl_seat_cursor_visible_set(GWL_Seat *seat,
|
||||
const bool visible,
|
||||
const bool is_hardware,
|
||||
const enum eCursorSetMode set_mode)
|
||||
{
|
||||
GWL_Cursor *cursor = &seat->cursor;
|
||||
const bool was_visible = cursor->is_hardware && cursor->visible;
|
||||
const bool use_visible = is_hardware && visible;
|
||||
|
||||
if (set_mode == CURSOR_VISIBLE_ALWAYS_SET) {
|
||||
/* Pass. */
|
||||
}
|
||||
else if (set_mode == CURSOR_VISIBLE_ONLY_SHOW) {
|
||||
if (!use_visible) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (set_mode == CURSOR_VISIBLE_ONLY_HIDE) {
|
||||
if (use_visible) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_visible) {
|
||||
if (!was_visible) {
|
||||
gwl_seat_cursor_buffer_show(seat);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (was_visible) {
|
||||
gwl_seat_cursor_buffer_hide(seat);
|
||||
}
|
||||
}
|
||||
cursor->visible = visible;
|
||||
cursor->is_hardware = is_hardware;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Private Cursor Animation API
|
||||
* \{ */
|
||||
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
|
||||
static bool gwl_seat_cursor_anim_check(GWL_Seat *seat)
|
||||
{
|
||||
const wl_cursor *wl_cursor = seat->cursor.wl.theme_cursor;
|
||||
if (!wl_cursor) {
|
||||
return false;
|
||||
}
|
||||
/* NOTE: return true to stress test animated cursor,
|
||||
* to ensure (otherwise rare) issues are triggered more frequently. */
|
||||
// return true;
|
||||
|
||||
return wl_cursor->image_count > 1;
|
||||
}
|
||||
|
||||
static void gwl_seat_cursor_anim_begin(GWL_Seat *seat)
|
||||
{
|
||||
/* Caller must lock `server_mutex`. */
|
||||
GHOST_ASSERT(seat->cursor.anim_handle == nullptr, "Must be cleared");
|
||||
|
||||
/* Callback for updating the cursor animation. */
|
||||
auto cursor_anim_frame_step_fn =
|
||||
[](GWL_Seat *seat, GWL_Cursor_AnimHandle *anim_handle, int delay) {
|
||||
/* It's possible the `wl_cursor` is reloaded while the cursor is animating.
|
||||
* Don't access outside the lock, that's why the `delay` is passed in. */
|
||||
std::mutex *server_mutex = seat->system->server_mutex;
|
||||
int frame = 0;
|
||||
while (!anim_handle->exit_pending.load()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
|
||||
if (!anim_handle->exit_pending.load()) {
|
||||
std::lock_guard lock_server_guard{*server_mutex};
|
||||
if (!anim_handle->exit_pending.load()) {
|
||||
const struct wl_cursor *wl_cursor = seat->cursor.wl.theme_cursor;
|
||||
frame = (frame + 1) % wl_cursor->image_count;
|
||||
wl_cursor_image *image = wl_cursor->images[frame];
|
||||
wl_buffer *buffer = wl_cursor_image_get_buffer(image);
|
||||
gwl_seat_cursor_buffer_set(seat, image, buffer);
|
||||
delay = wl_cursor->images[frame]->delay;
|
||||
/* Without this the cursor won't update when other processes are occupied. */
|
||||
wl_display_flush(seat->system->wl_display_get());
|
||||
}
|
||||
}
|
||||
}
|
||||
delete anim_handle;
|
||||
};
|
||||
|
||||
/* Allocate so this can be set before the thread begins. */
|
||||
GWL_Cursor_AnimHandle *anim_handle = new GWL_Cursor_AnimHandle;
|
||||
seat->cursor.anim_handle = anim_handle;
|
||||
|
||||
const int delay = seat->cursor.wl.theme_cursor->images[0]->delay;
|
||||
std::thread cursor_anim_thread(cursor_anim_frame_step_fn, seat, anim_handle, delay);
|
||||
/* Application logic should take priority. */
|
||||
thread_set_min_priority(cursor_anim_thread);
|
||||
cursor_anim_thread.detach();
|
||||
}
|
||||
|
||||
static void gwl_seat_cursor_anim_begin_if_needed(GWL_Seat *seat)
|
||||
{
|
||||
if (gwl_seat_cursor_anim_check(seat)) {
|
||||
gwl_seat_cursor_anim_begin(seat);
|
||||
}
|
||||
}
|
||||
|
||||
static void gwl_seat_cursor_anim_end(GWL_Seat *seat)
|
||||
{
|
||||
GWL_Cursor *cursor = &seat->cursor;
|
||||
if (cursor->anim_handle) {
|
||||
GWL_Cursor_AnimHandle *anim_handle = cursor->anim_handle;
|
||||
cursor->anim_handle = nullptr;
|
||||
anim_handle->exit_pending.store(true);
|
||||
}
|
||||
}
|
||||
|
||||
static void gwl_seat_cursor_anim_reset(GWL_Seat *seat)
|
||||
{
|
||||
gwl_seat_cursor_anim_end(seat);
|
||||
gwl_seat_cursor_anim_begin_if_needed(seat);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* Unfortunately cursor animation requires a background thread. */
|
||||
[[maybe_unused]] static bool gwl_seat_cursor_anim_check(GWL_Seat * /*seat*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
[[maybe_unused]] static void gwl_seat_cursor_anim_begin(GWL_Seat * /*seat*/) {}
|
||||
[[maybe_unused]] static void gwl_seat_cursor_anim_begin_if_needed(GWL_Seat * /*seat*/) {}
|
||||
[[maybe_unused]] static void gwl_seat_cursor_anim_end(GWL_Seat * /*seat*/) {}
|
||||
[[maybe_unused]] static void gwl_seat_cursor_anim_reset(GWL_Seat * /*seat*/) {}
|
||||
|
||||
#endif /* !USE_EVENT_BACKGROUND_THREAD */
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Private Keyboard Depressed Key Tracking
|
||||
*
|
||||
@ -7378,185 +7695,6 @@ GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title,
|
||||
return window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the buffer defined by #cursor_buffer_set without changing anything else,
|
||||
* so #cursor_buffer_hide can be used to display it again.
|
||||
*
|
||||
* The caller is responsible for setting `seat->cursor.visible`.
|
||||
*/
|
||||
static void cursor_buffer_show(GWL_Seat *seat)
|
||||
{
|
||||
const GWL_Cursor *cursor = &seat->cursor;
|
||||
|
||||
if (seat->wl.pointer) {
|
||||
const int scale = cursor->is_custom ? cursor->custom_scale : seat->pointer.theme_scale;
|
||||
const int32_t hotspot_x = int32_t(cursor->wl.image.hotspot_x) / scale;
|
||||
const int32_t hotspot_y = int32_t(cursor->wl.image.hotspot_y) / scale;
|
||||
wl_pointer_set_cursor(
|
||||
seat->wl.pointer, seat->pointer.serial, cursor->wl.surface_cursor, hotspot_x, hotspot_y);
|
||||
}
|
||||
|
||||
if (!seat->wp.tablet_tools.empty()) {
|
||||
const int scale = cursor->is_custom ? cursor->custom_scale : seat->tablet.theme_scale;
|
||||
const int32_t hotspot_x = int32_t(cursor->wl.image.hotspot_x) / scale;
|
||||
const int32_t hotspot_y = int32_t(cursor->wl.image.hotspot_y) / scale;
|
||||
for (zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : seat->wp.tablet_tools) {
|
||||
GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(
|
||||
zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2));
|
||||
zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2,
|
||||
seat->tablet.serial,
|
||||
tablet_tool->wl.surface_cursor,
|
||||
hotspot_x,
|
||||
hotspot_y);
|
||||
#ifdef USE_KDE_TABLET_HIDDEN_CURSOR_HACK
|
||||
wl_surface_commit(tablet_tool->wl.surface_cursor);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
cursor_anim_reset(seat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the buffer defined by #cursor_buffer_set without changing anything else,
|
||||
* so #cursor_buffer_show can be used to display it again.
|
||||
*
|
||||
* The caller is responsible for setting `seat->cursor.visible`.
|
||||
*/
|
||||
static void cursor_buffer_hide(GWL_Seat *seat)
|
||||
{
|
||||
cursor_anim_end(seat);
|
||||
|
||||
wl_pointer_set_cursor(seat->wl.pointer, seat->pointer.serial, nullptr, 0, 0);
|
||||
for (zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : seat->wp.tablet_tools) {
|
||||
zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, seat->tablet.serial, nullptr, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed to ensure the cursor size is always a multiple of scale.
|
||||
*/
|
||||
static int cursor_buffer_compatible_scale_from_image(const wl_cursor_image *wl_image, int scale)
|
||||
{
|
||||
const int32_t image_size_x = int32_t(wl_image->width);
|
||||
const int32_t image_size_y = int32_t(wl_image->height);
|
||||
while (scale > 1) {
|
||||
if ((image_size_x % scale) == 0 && (image_size_y % scale) == 0) {
|
||||
break;
|
||||
}
|
||||
scale -= 1;
|
||||
}
|
||||
return scale;
|
||||
}
|
||||
|
||||
static void cursor_buffer_set_surface_impl(const wl_cursor_image *wl_image,
|
||||
wl_buffer *buffer,
|
||||
wl_surface *wl_surface,
|
||||
const int scale)
|
||||
{
|
||||
const int32_t image_size_x = int32_t(wl_image->width);
|
||||
const int32_t image_size_y = int32_t(wl_image->height);
|
||||
GHOST_ASSERT((image_size_x % scale) == 0 && (image_size_y % scale) == 0,
|
||||
"The size must be a multiple of the scale!");
|
||||
|
||||
wl_surface_set_buffer_scale(wl_surface, scale);
|
||||
wl_surface_attach(wl_surface, buffer, 0, 0);
|
||||
wl_surface_damage(wl_surface, 0, 0, image_size_x, image_size_y);
|
||||
wl_surface_commit(wl_surface);
|
||||
}
|
||||
|
||||
static void cursor_buffer_set(const GWL_Seat *seat,
|
||||
const wl_cursor_image *wl_image,
|
||||
wl_buffer *buffer)
|
||||
{
|
||||
const GWL_Cursor *cursor = &seat->cursor;
|
||||
const bool visible = (cursor->visible && cursor->is_hardware);
|
||||
|
||||
/* This is a requirement of WAYLAND, when this isn't the case,
|
||||
* it causes Blender's window to close intermittently. */
|
||||
if (seat->wl.pointer) {
|
||||
const int scale = cursor_buffer_compatible_scale_from_image(
|
||||
wl_image, cursor->is_custom ? cursor->custom_scale : seat->pointer.theme_scale);
|
||||
const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale;
|
||||
const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale;
|
||||
cursor_buffer_set_surface_impl(wl_image, buffer, cursor->wl.surface_cursor, scale);
|
||||
wl_pointer_set_cursor(seat->wl.pointer,
|
||||
seat->pointer.serial,
|
||||
visible ? cursor->wl.surface_cursor : nullptr,
|
||||
hotspot_x,
|
||||
hotspot_y);
|
||||
}
|
||||
|
||||
/* Set the cursor for all tablet tools as well. */
|
||||
if (!seat->wp.tablet_tools.empty()) {
|
||||
const int scale = cursor_buffer_compatible_scale_from_image(
|
||||
wl_image, cursor->is_custom ? cursor->custom_scale : seat->tablet.theme_scale);
|
||||
const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale;
|
||||
const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale;
|
||||
for (zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : seat->wp.tablet_tools) {
|
||||
GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(
|
||||
zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2));
|
||||
cursor_buffer_set_surface_impl(wl_image, buffer, tablet_tool->wl.surface_cursor, scale);
|
||||
zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2,
|
||||
seat->tablet.serial,
|
||||
visible ? tablet_tool->wl.surface_cursor : nullptr,
|
||||
hotspot_x,
|
||||
hotspot_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cursor_buffer_set_from_seat(GWL_Seat *seat)
|
||||
{
|
||||
const GWL_Cursor *cursor = &seat->cursor;
|
||||
cursor_anim_end(seat);
|
||||
cursor_buffer_set(seat, &cursor->wl.image, cursor->wl.buffer);
|
||||
cursor_anim_begin_if_needed(seat);
|
||||
}
|
||||
|
||||
enum eCursorSetMode {
|
||||
CURSOR_VISIBLE_ALWAYS_SET = 1,
|
||||
CURSOR_VISIBLE_ONLY_HIDE,
|
||||
CURSOR_VISIBLE_ONLY_SHOW,
|
||||
};
|
||||
|
||||
static void cursor_visible_set(GWL_Seat *seat,
|
||||
const bool visible,
|
||||
const bool is_hardware,
|
||||
const enum eCursorSetMode set_mode)
|
||||
{
|
||||
GWL_Cursor *cursor = &seat->cursor;
|
||||
const bool was_visible = cursor->is_hardware && cursor->visible;
|
||||
const bool use_visible = is_hardware && visible;
|
||||
|
||||
if (set_mode == CURSOR_VISIBLE_ALWAYS_SET) {
|
||||
/* Pass. */
|
||||
}
|
||||
else if (set_mode == CURSOR_VISIBLE_ONLY_SHOW) {
|
||||
if (!use_visible) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (set_mode == CURSOR_VISIBLE_ONLY_HIDE) {
|
||||
if (use_visible) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_visible) {
|
||||
if (!was_visible) {
|
||||
cursor_buffer_show(seat);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (was_visible) {
|
||||
cursor_buffer_hide(seat);
|
||||
}
|
||||
}
|
||||
cursor->visible = visible;
|
||||
cursor->is_hardware = is_hardware;
|
||||
}
|
||||
|
||||
static bool cursor_is_software(const GHOST_TGrabCursorMode mode, const bool use_software_confine)
|
||||
{
|
||||
if (mode == GHOST_kGrabWrap) {
|
||||
@ -7574,114 +7712,6 @@ static bool cursor_is_software(const GHOST_TGrabCursorMode mode, const bool use_
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool cursor_anim_check(GWL_Seat *seat)
|
||||
{
|
||||
const wl_cursor *wl_cursor = seat->cursor.wl.theme_cursor;
|
||||
if (!wl_cursor) {
|
||||
return false;
|
||||
}
|
||||
/* NOTE: return true to stress test animated cursor,
|
||||
* to ensure (otherwise rare) issues are triggered more frequently. */
|
||||
// return true;
|
||||
|
||||
return wl_cursor->image_count > 1;
|
||||
}
|
||||
|
||||
static void cursor_anim_begin(GWL_Seat *seat)
|
||||
{
|
||||
/* Caller must lock `server_mutex`. */
|
||||
GHOST_ASSERT(seat->cursor.anim_handle == nullptr, "Must be cleared");
|
||||
|
||||
/* Callback for updating the cursor animation. */
|
||||
auto cursor_anim_frame_step_fn =
|
||||
[](GWL_Seat *seat, GWL_Cursor_AnimHandle *anim_handle, int delay) {
|
||||
/* It's possible the `wl_cursor` is reloaded while the cursor is animating.
|
||||
* Don't access outside the lock, that's why the `delay` is passed in. */
|
||||
std::mutex *server_mutex = seat->system->server_mutex;
|
||||
int frame = 0;
|
||||
while (!anim_handle->exit_pending.load()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
|
||||
if (!anim_handle->exit_pending.load()) {
|
||||
std::lock_guard lock_server_guard{*server_mutex};
|
||||
if (!anim_handle->exit_pending.load()) {
|
||||
const struct wl_cursor *wl_cursor = seat->cursor.wl.theme_cursor;
|
||||
frame = (frame + 1) % wl_cursor->image_count;
|
||||
wl_cursor_image *image = wl_cursor->images[frame];
|
||||
wl_buffer *buffer = wl_cursor_image_get_buffer(image);
|
||||
cursor_buffer_set(seat, image, buffer);
|
||||
delay = wl_cursor->images[frame]->delay;
|
||||
/* Without this the cursor won't update when other processes are occupied. */
|
||||
wl_display_flush(seat->system->wl_display_get());
|
||||
}
|
||||
}
|
||||
}
|
||||
delete anim_handle;
|
||||
};
|
||||
|
||||
/* Allocate so this can be set before the thread begins. */
|
||||
GWL_Cursor_AnimHandle *anim_handle = new GWL_Cursor_AnimHandle;
|
||||
seat->cursor.anim_handle = anim_handle;
|
||||
|
||||
const int delay = seat->cursor.wl.theme_cursor->images[0]->delay;
|
||||
std::thread cursor_anim_thread(cursor_anim_frame_step_fn, seat, anim_handle, delay);
|
||||
/* Application logic should take priority. */
|
||||
thread_set_min_priority(cursor_anim_thread);
|
||||
cursor_anim_thread.detach();
|
||||
}
|
||||
|
||||
static void cursor_anim_begin_if_needed(GWL_Seat *seat)
|
||||
{
|
||||
if (cursor_anim_check(seat)) {
|
||||
cursor_anim_begin(seat);
|
||||
}
|
||||
}
|
||||
|
||||
static void cursor_anim_end(GWL_Seat *seat)
|
||||
{
|
||||
GWL_Cursor *cursor = &seat->cursor;
|
||||
if (cursor->anim_handle) {
|
||||
GWL_Cursor_AnimHandle *anim_handle = cursor->anim_handle;
|
||||
cursor->anim_handle = nullptr;
|
||||
anim_handle->exit_pending.store(true);
|
||||
}
|
||||
}
|
||||
|
||||
static void cursor_anim_reset(GWL_Seat *seat)
|
||||
{
|
||||
cursor_anim_end(seat);
|
||||
cursor_anim_begin_if_needed(seat);
|
||||
}
|
||||
|
||||
static const wl_cursor *cursor_find_from_shape(GWL_Seat *seat,
|
||||
const GHOST_TStandardCursor shape,
|
||||
const char **r_cursor_name)
|
||||
{
|
||||
/* Caller must lock `server_mutex`. */
|
||||
GWL_Cursor *cursor = &seat->cursor;
|
||||
wl_cursor *wl_cursor = nullptr;
|
||||
|
||||
const char *cursor_name = ghost_wl_cursors.names[shape];
|
||||
if (cursor_name[0] != '\0') {
|
||||
if (!cursor->wl.theme) {
|
||||
/* The cursor wl_surface hasn't entered an output yet. Initialize theme with scale 1. */
|
||||
cursor->wl.theme = wl_cursor_theme_load(
|
||||
cursor->theme_name.c_str(), cursor->theme_size, seat->system->wl_shm_get());
|
||||
}
|
||||
|
||||
if (cursor->wl.theme) {
|
||||
wl_cursor = wl_cursor_theme_get_cursor(cursor->wl.theme, cursor_name);
|
||||
if (!wl_cursor) {
|
||||
GHOST_PRINT("cursor '" << cursor_name << "' does not exist" << std::endl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (r_cursor_name && wl_cursor) {
|
||||
*r_cursor_name = cursor_name;
|
||||
}
|
||||
return wl_cursor;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_SystemWayland::cursor_shape_set(const GHOST_TStandardCursor shape)
|
||||
{
|
||||
/* Caller must lock `server_mutex`. */
|
||||
@ -7692,7 +7722,7 @@ GHOST_TSuccess GHOST_SystemWayland::cursor_shape_set(const GHOST_TStandardCursor
|
||||
}
|
||||
|
||||
const char *cursor_name = nullptr;
|
||||
const wl_cursor *wl_cursor = cursor_find_from_shape(seat, shape, &cursor_name);
|
||||
const wl_cursor *wl_cursor = gwl_seat_cursor_find_from_shape(seat, shape, &cursor_name);
|
||||
if (wl_cursor == nullptr) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
@ -7711,7 +7741,7 @@ GHOST_TSuccess GHOST_SystemWayland::cursor_shape_set(const GHOST_TStandardCursor
|
||||
cursor->wl.theme_cursor = wl_cursor;
|
||||
cursor->wl.theme_cursor_name = cursor_name;
|
||||
|
||||
cursor_buffer_set_from_seat(seat);
|
||||
gwl_seat_cursor_buffer_set_current(seat);
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
@ -7720,7 +7750,7 @@ GHOST_TSuccess GHOST_SystemWayland::cursor_shape_check(const GHOST_TStandardCurs
|
||||
{
|
||||
/* No need to lock `server_mutex`. */
|
||||
GWL_Seat *seat = gwl_display_seat_active_get(display_);
|
||||
const wl_cursor *wl_cursor = cursor_find_from_shape(seat, cursorShape, nullptr);
|
||||
const wl_cursor *wl_cursor = gwl_seat_cursor_find_from_shape(seat, cursorShape, nullptr);
|
||||
if (wl_cursor == nullptr) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
@ -7800,7 +7830,7 @@ GHOST_TSuccess GHOST_SystemWayland::cursor_shape_custom_set(const uint8_t *bitma
|
||||
cursor->wl.theme_cursor = nullptr;
|
||||
cursor->wl.theme_cursor_name = nullptr;
|
||||
|
||||
cursor_buffer_set_from_seat(seat);
|
||||
gwl_seat_cursor_buffer_set_current(seat);
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
@ -7840,7 +7870,7 @@ GHOST_TSuccess GHOST_SystemWayland::cursor_visibility_set(const bool visible)
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
cursor_visible_set(seat, visible, seat->cursor.is_hardware, CURSOR_VISIBLE_ALWAYS_SET);
|
||||
gwl_seat_cursor_visible_set(seat, visible, seat->cursor.is_hardware, CURSOR_VISIBLE_ALWAYS_SET);
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
@ -8432,7 +8462,7 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
|
||||
|
||||
/* Only hide so the cursor is not made visible before it's location is restored.
|
||||
* This function is called again at the end of this function which only shows. */
|
||||
cursor_visible_set(seat, use_visible, is_hardware_cursor, CURSOR_VISIBLE_ONLY_HIDE);
|
||||
gwl_seat_cursor_visible_set(seat, use_visible, is_hardware_cursor, CURSOR_VISIBLE_ONLY_HIDE);
|
||||
|
||||
/* Switching from one grab mode to another,
|
||||
* in this case disable the current locks as it makes logic confusing,
|
||||
@ -8574,7 +8604,7 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
|
||||
}
|
||||
|
||||
/* Only show so the cursor is made visible as the last step. */
|
||||
cursor_visible_set(seat, use_visible, is_hardware_cursor, CURSOR_VISIBLE_ONLY_SHOW);
|
||||
gwl_seat_cursor_visible_set(seat, use_visible, is_hardware_cursor, CURSOR_VISIBLE_ONLY_SHOW);
|
||||
|
||||
#ifdef USE_GNOME_CONFINE_HACK
|
||||
seat->use_pointer_software_confine = use_software_confine;
|
||||
|
@ -885,7 +885,7 @@ static void gwl_window_frame_update_from_pending_no_lock(GWL_Window *win)
|
||||
win->frame_pending.size[1] = 0;
|
||||
}
|
||||
|
||||
static void gwl_window_frame_update_from_pending(GWL_Window *win)
|
||||
[[maybe_unused]] static void gwl_window_frame_update_from_pending(GWL_Window *win)
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_frame_guard{win->frame_pending_mutex};
|
||||
|
Loading…
Reference in New Issue
Block a user