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:
Diffstat (limited to 'intern/ghost/intern')
-rw-r--r--intern/ghost/intern/GHOST_C-api.cpp31
-rw-r--r--intern/ghost/intern/GHOST_ISystem.cpp12
-rw-r--r--intern/ghost/intern/GHOST_System.cpp10
-rw-r--r--intern/ghost/intern/GHOST_System.h4
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.cpp1733
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.h38
-rw-r--r--intern/ghost/intern/GHOST_Window.cpp21
-rw-r--r--intern/ghost/intern/GHOST_Window.h4
-rw-r--r--intern/ghost/intern/GHOST_WindowWayland.cpp355
-rw-r--r--intern/ghost/intern/GHOST_WindowWayland.h44
-rw-r--r--intern/ghost/intern/GHOST_WindowX11.cpp2
-rw-r--r--intern/ghost/intern/GHOST_Wintab.cpp19
-rw-r--r--intern/ghost/intern/GHOST_Wintab.h2
13 files changed, 1705 insertions, 570 deletions
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp
index 9374d087408..b1a15fdf4d7 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -376,6 +376,20 @@ GHOST_TSuccess GHOST_SetCursorGrab(GHOST_WindowHandle windowhandle,
mode, wrap_axis, bounds ? &bounds_rect : nullptr, mouse_ungrab_xy ? mouse_xy : nullptr);
}
+void GHOST_GetCursorGrabState(GHOST_WindowHandle windowhandle,
+ GHOST_TGrabCursorMode *r_mode,
+ GHOST_TAxisFlag *r_axis_flag,
+ int r_bounds[4])
+{
+ GHOST_IWindow *window = (GHOST_IWindow *)windowhandle;
+ GHOST_Rect bounds_rect;
+ window->getCursorGrabState(*r_mode, *r_axis_flag, bounds_rect);
+ 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;
+}
+
GHOST_TSuccess GHOST_GetModifierKeyState(GHOST_SystemHandle systemhandle,
GHOST_TModifierKeyMask mask,
int *isDown)
@@ -815,6 +829,23 @@ int GHOST_UseNativePixels(void)
return system->useNativePixel();
}
+int GHOST_SupportsCursorWarp(void)
+{
+ GHOST_ISystem *system = GHOST_ISystem::getSystem();
+ return system->supportsCursorWarp();
+}
+
+int GHOST_SupportsWindowPosition(void)
+{
+ GHOST_ISystem *system = GHOST_ISystem::getSystem();
+ return system->supportsWindowPosition();
+}
+
+void GHOST_SetBacktraceHandler(GHOST_TBacktraceFn backtrace_fn)
+{
+ GHOST_ISystem::setBacktraceFn(backtrace_fn);
+}
+
void GHOST_UseWindowFocus(int use_focus)
{
GHOST_ISystem *system = GHOST_ISystem::getSystem();
diff --git a/intern/ghost/intern/GHOST_ISystem.cpp b/intern/ghost/intern/GHOST_ISystem.cpp
index 87111306ec7..745d5faeed4 100644
--- a/intern/ghost/intern/GHOST_ISystem.cpp
+++ b/intern/ghost/intern/GHOST_ISystem.cpp
@@ -31,6 +31,8 @@
GHOST_ISystem *GHOST_ISystem::m_system = nullptr;
+GHOST_TBacktraceFn GHOST_ISystem::m_backtrace_fn = nullptr;
+
GHOST_TSuccess GHOST_ISystem::createSystem()
{
GHOST_TSuccess success;
@@ -89,3 +91,13 @@ GHOST_ISystem *GHOST_ISystem::getSystem()
{
return m_system;
}
+
+GHOST_TBacktraceFn GHOST_ISystem::getBacktraceFn()
+{
+ return GHOST_ISystem::m_backtrace_fn;
+}
+
+void GHOST_ISystem::setBacktraceFn(GHOST_TBacktraceFn backtrace_fn)
+{
+ GHOST_ISystem::m_backtrace_fn = backtrace_fn;
+}
diff --git a/intern/ghost/intern/GHOST_System.cpp b/intern/ghost/intern/GHOST_System.cpp
index 1ddf884bbc5..c8308b3586b 100644
--- a/intern/ghost/intern/GHOST_System.cpp
+++ b/intern/ghost/intern/GHOST_System.cpp
@@ -390,6 +390,16 @@ void GHOST_System::useWindowFocus(const bool use_focus)
m_windowFocus = use_focus;
}
+bool GHOST_System::supportsCursorWarp()
+{
+ return true;
+}
+
+bool GHOST_System::supportsWindowPosition()
+{
+ 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..b60ce09f743 100644
--- a/intern/ghost/intern/GHOST_System.h
+++ b/intern/ghost/intern/GHOST_System.h
@@ -151,10 +151,14 @@ class GHOST_System : public GHOST_ISystem {
bool useNativePixel(void);
bool m_nativePixel;
+ bool supportsCursorWarp(void);
+ bool supportsWindowPosition(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 7e74287d6e3..d8fbe875f67 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.cpp
+++ b/intern/ghost/intern/GHOST_SystemWayland.cpp
@@ -29,7 +29,10 @@
#include "GHOST_WaylandCursorSettings.h"
#include <pointer-constraints-client-protocol.h>
#include <relative-pointer-client-protocol.h>
+#include <tablet-client-protocol.h>
#include <wayland-cursor.h>
+#include <xdg-output-client-protocol.h>
+
#include <xkbcommon/xkbcommon.h>
#include <fcntl.h>
@@ -37,6 +40,13 @@
#include <unistd.h>
#include <cstring>
+#include <mutex>
+
+static GHOST_WindowWayland *window_from_surface(struct wl_surface *surface);
+
+/* -------------------------------------------------------------------- */
+/** \name Private Types & Defines
+ * \{ */
/**
* Selected input event code defines from `linux/input-event-codes.h`
@@ -53,102 +63,155 @@
#define BTN_BACK 0x116
// #define BTN_TASK 0x117 /* UNUSED. */
+/**
+ * Tablet events, also from `linux/input-event-codes.h`.
+ */
+#define BTN_STYLUS 0x14b /* Use as right-mouse. */
+#define BTN_STYLUS2 0x14c /* Use as middle-mouse. */
+/* NOTE(@campbellbarton): Map to an additional button (not sure which hardware uses this). */
+#define BTN_STYLUS3 0x149
+
struct buffer_t {
- void *data;
- size_t size;
+ void *data = nullptr;
+ size_t size = 0;
};
struct cursor_t {
- bool visible;
- struct wl_surface *surface = nullptr;
- struct wl_buffer *buffer;
- struct wl_cursor_image image;
+ bool visible = false;
+ struct wl_surface *wl_surface = nullptr;
+ struct wl_buffer *wl_buffer = nullptr;
+ struct wl_cursor_image wl_image = {0};
+ struct wl_cursor_theme *wl_theme = nullptr;
struct buffer_t *file_buffer = nullptr;
- struct wl_cursor_theme *theme = nullptr;
- int size;
+ int size = 0;
std::string theme_name;
- // outputs on which the cursor is visible
+ /** Outputs on which the cursor is visible. */
std::unordered_set<const output_t *> outputs;
int scale = 1;
};
+/**
+ * A single tablet can have multiple tools (pen, eraser, brush... etc).
+ * WAYLAND exposes tools via #zwp_tablet_tool_v2.
+ * Since are no API's to access properties of the tool, store the values here.
+ */
+struct tablet_tool_input_t {
+ struct input_t *input = nullptr;
+ struct wl_surface *cursor_surface = nullptr;
+
+ GHOST_TabletData data = GHOST_TABLET_DATA_NONE;
+};
+
struct data_offer_t {
std::unordered_set<std::string> types;
- uint32_t source_actions;
- uint32_t dnd_action;
- struct wl_data_offer *id;
- std::atomic<bool> in_use;
+ uint32_t source_actions = 0;
+ uint32_t dnd_action = 0;
+ struct wl_data_offer *id = nullptr;
+ std::atomic<bool> in_use = false;
struct {
- int x, y;
+ /** Compatible with #input_t.xy coordinates. */
+ wl_fixed_t xy[2] = {0, 0};
} dnd;
};
struct data_source_t {
- struct wl_data_source *data_source;
- /** Last device that was active. */
- uint32_t source_serial;
- char *buffer_out;
+ struct wl_data_source *data_source = nullptr;
+ char *buffer_out = nullptr;
};
struct key_repeat_payload_t {
- GHOST_SystemWayland *system;
- GHOST_IWindow *window;
- GHOST_TKey key;
- GHOST_TEventKeyData key_data;
+ GHOST_SystemWayland *system = nullptr;
+ GHOST_IWindow *window = nullptr;
+ GHOST_TEventKeyData key_data = {GHOST_kKeyUnknown};
};
struct input_t {
- GHOST_SystemWayland *system;
+ GHOST_SystemWayland *system = nullptr;
std::string name;
- struct wl_seat *seat;
- struct wl_pointer *pointer = nullptr;
- struct wl_keyboard *keyboard = nullptr;
-
- uint32_t pointer_serial;
- int x, y;
- GHOST_Buttons buttons;
+ struct wl_seat *wl_seat = nullptr;
+ struct wl_pointer *wl_pointer = nullptr;
+ struct wl_keyboard *wl_keyboard = nullptr;
+ struct zwp_tablet_seat_v2 *tablet_seat = nullptr;
+
+ /** All currently active tablet tools (needed for changing the cursor). */
+ std::unordered_set<zwp_tablet_tool_v2 *> tablet_tools;
+
+ uint32_t pointer_serial = 0;
+ uint32_t tablet_serial = 0;
+
+ /** Use to check if the last cursor input was tablet or pointer. */
+ uint32_t cursor_serial = 0;
+
+ /**
+ * High precision mouse coordinates (pointer or tablet).
+ *
+ * The following example converts these values to screen coordinates.
+ * \code{.cc}
+ * const wl_fixed_t scale = win->scale();
+ * const int event_xy[2] = {
+ * wl_fixed_to_int(scale * input->xy[0]),
+ * wl_fixed_to_int(scale * input->xy[1]),
+ * };
+ * \endocde
+ */
+ wl_fixed_t xy[2] = {0, 0};
+ GHOST_Buttons buttons = GHOST_Buttons();
struct cursor_t cursor;
- struct zwp_relative_pointer_v1 *relative_pointer;
- struct zwp_locked_pointer_v1 *locked_pointer;
+ struct zwp_relative_pointer_v1 *relative_pointer = nullptr;
+ struct zwp_locked_pointer_v1 *locked_pointer = nullptr;
+ struct zwp_confined_pointer_v1 *confined_pointer = nullptr;
- struct xkb_context *xkb_context;
- struct xkb_state *xkb_state;
+ struct xkb_context *xkb_context = nullptr;
+ struct xkb_state *xkb_state = nullptr;
struct {
- /* Key repetition in character per second. */
- int32_t rate;
- /* Time (milliseconds) after which to start repeating keys. */
- int32_t delay;
- /* Timer for key repeats. */
+ /** Key repetition in character per second. */
+ int32_t rate = 0;
+ /** Time (milliseconds) after which to start repeating keys. */
+ int32_t delay = 0;
+ /** Timer for key repeats. */
GHOST_ITimerTask *timer = nullptr;
} key_repeat;
+ struct wl_surface *focus_tablet = nullptr;
struct wl_surface *focus_pointer = nullptr;
struct wl_surface *focus_keyboard = nullptr;
+ struct wl_surface *focus_dnd = nullptr;
struct wl_data_device *data_device = nullptr;
- struct data_offer_t *data_offer_dnd; /* Drag & Drop. */
- struct data_offer_t *data_offer_copy_paste; /* Copy & Paste. */
+ /** Drag & Drop. */
+ struct data_offer_t *data_offer_dnd = nullptr;
+ std::mutex data_offer_dnd_mutex;
+
+ /** Copy & Paste. */
+ struct data_offer_t *data_offer_copy_paste = nullptr;
+ std::mutex data_offer_copy_paste_mutex;
+
+ struct data_source_t *data_source = nullptr;
+ std::mutex data_source_mutex;
- struct data_source_t *data_source;
+ /** Last device that was active. */
+ uint32_t data_source_serial = 0;
};
struct display_t {
- GHOST_SystemWayland *system;
+ GHOST_SystemWayland *system = nullptr;
- struct wl_display *display;
+ struct wl_display *display = nullptr;
struct wl_compositor *compositor = nullptr;
struct xdg_wm_base *xdg_shell = nullptr;
struct zxdg_decoration_manager_v1 *xdg_decoration_manager = nullptr;
+ struct zxdg_output_manager_v1 *xdg_output_manager = nullptr;
struct wl_shm *shm = nullptr;
std::vector<output_t *> outputs;
std::vector<input_t *> inputs;
struct {
std::string theme;
- int size;
+ int size = 0;
} cursor;
struct wl_data_device_manager *data_device_manager = nullptr;
+ struct zwp_tablet_manager_v2 *tablet_manager = nullptr;
struct zwp_relative_pointer_manager_v1 *relative_pointer_manager = nullptr;
struct zwp_pointer_constraints_v1 *pointer_constraints = nullptr;
@@ -156,56 +219,105 @@ struct display_t {
std::vector<struct wl_egl_window *> os_egl_windows;
};
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Private Utility Functions
+ * \{ */
+
static GHOST_WindowManager *window_manager = nullptr;
+/** Check this lock before accessing `GHOST_SystemWayland::selection` from a thread. */
+static std::mutex system_selection_mutex;
+
+/**
+ * Callback for WAYLAND to run when there is an error.
+ *
+ * \note It's useful to set a break-point on this function as some errors are fatal
+ * (for all intents and purposes) but don't crash the process.
+ */
+static void ghost_wayland_log_handler(const char *msg, va_list arg)
+{
+ fprintf(stderr, "GHOST/Wayland: ");
+ vfprintf(stderr, msg, arg); /* Includes newline. */
+
+ GHOST_TBacktraceFn backtrace_fn = GHOST_ISystem::getBacktraceFn();
+ if (backtrace_fn) {
+ backtrace_fn(stderr); /* Includes newline. */
+ }
+}
+
static void display_destroy(display_t *d)
{
if (d->data_device_manager) {
wl_data_device_manager_destroy(d->data_device_manager);
}
+ if (d->tablet_manager) {
+ zwp_tablet_manager_v2_destroy(d->tablet_manager);
+ }
+
for (output_t *output : d->outputs) {
- wl_output_destroy(output->output);
+ wl_output_destroy(output->wl_output);
delete output;
}
for (input_t *input : d->inputs) {
- if (input->data_source) {
- free(input->data_source->buffer_out);
- if (input->data_source->data_source) {
- wl_data_source_destroy(input->data_source->data_source);
+
+ /* First handle members that require locking.
+ * While highly unlikely, it's possible they are being used while this function runs. */
+ {
+ std::lock_guard lock{input->data_source_mutex};
+ if (input->data_source) {
+ free(input->data_source->buffer_out);
+ if (input->data_source->data_source) {
+ wl_data_source_destroy(input->data_source->data_source);
+ }
+ delete input->data_source;
}
- delete input->data_source;
}
- if (input->data_offer_copy_paste) {
- wl_data_offer_destroy(input->data_offer_copy_paste->id);
- delete input->data_offer_copy_paste;
+
+ {
+ std::lock_guard lock{input->data_offer_dnd_mutex};
+ if (input->data_offer_dnd) {
+ wl_data_offer_destroy(input->data_offer_dnd->id);
+ delete input->data_offer_dnd;
+ }
}
+
+ {
+ std::lock_guard lock{input->data_offer_copy_paste_mutex};
+ if (input->data_offer_copy_paste) {
+ wl_data_offer_destroy(input->data_offer_copy_paste->id);
+ delete input->data_offer_copy_paste;
+ }
+ }
+
if (input->data_device) {
wl_data_device_release(input->data_device);
}
- if (input->pointer) {
+ if (input->wl_pointer) {
if (input->cursor.file_buffer) {
munmap(input->cursor.file_buffer->data, input->cursor.file_buffer->size);
delete input->cursor.file_buffer;
}
- if (input->cursor.surface) {
- wl_surface_destroy(input->cursor.surface);
+ if (input->cursor.wl_surface) {
+ wl_surface_destroy(input->cursor.wl_surface);
}
- if (input->cursor.theme) {
- wl_cursor_theme_destroy(input->cursor.theme);
+ if (input->cursor.wl_theme) {
+ wl_cursor_theme_destroy(input->cursor.wl_theme);
}
- if (input->pointer) {
- wl_pointer_destroy(input->pointer);
+ if (input->wl_pointer) {
+ wl_pointer_destroy(input->wl_pointer);
}
}
- if (input->keyboard) {
+ if (input->wl_keyboard) {
if (input->key_repeat.timer) {
delete static_cast<key_repeat_payload_t *>(input->key_repeat.timer->getUserData());
input->system->removeTimer(input->key_repeat.timer);
input->key_repeat.timer = nullptr;
}
- wl_keyboard_destroy(input->keyboard);
+ wl_keyboard_destroy(input->wl_keyboard);
}
if (input->xkb_state) {
xkb_state_unref(input->xkb_state);
@@ -213,7 +325,7 @@ static void display_destroy(display_t *d)
if (input->xkb_context) {
xkb_context_unref(input->xkb_context);
}
- wl_seat_destroy(input->seat);
+ wl_seat_destroy(input->wl_seat);
delete input;
}
@@ -361,6 +473,27 @@ static GHOST_TKey xkb_map_gkey(const xkb_keysym_t &sym)
return gkey;
}
+static GHOST_TTabletMode tablet_tool_map_type(enum zwp_tablet_tool_v2_type wl_tablet_tool_type)
+{
+ switch (wl_tablet_tool_type) {
+ case ZWP_TABLET_TOOL_V2_TYPE_ERASER: {
+ return GHOST_kTabletModeEraser;
+ }
+ case ZWP_TABLET_TOOL_V2_TYPE_PEN:
+ case ZWP_TABLET_TOOL_V2_TYPE_BRUSH:
+ case ZWP_TABLET_TOOL_V2_TYPE_PENCIL:
+ case ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH:
+ case ZWP_TABLET_TOOL_V2_TYPE_FINGER:
+ case ZWP_TABLET_TOOL_V2_TYPE_MOUSE:
+ case ZWP_TABLET_TOOL_V2_TYPE_LENS: {
+ return GHOST_kTabletModeStylus;
+ }
+ }
+
+ GHOST_PRINT("unknown tablet tool: " << wl_tablet_tool_type << std::endl);
+ return GHOST_kTabletModeStylus;
+}
+
static const int default_cursor_size = 24;
static const std::unordered_map<GHOST_TStandardCursor, std::string> cursors = {
@@ -429,14 +562,16 @@ static const std::vector<std::string> mime_send = {
"text/plain",
};
+/** \} */
+
/* -------------------------------------------------------------------- */
-/** \name Interface Callbacks
+/** \name Listener (Relative Motion), #zwp_relative_pointer_v1_listener
*
* These callbacks are registered for Wayland interfaces and called when
* an event is received from the compositor.
* \{ */
-static void relative_pointer_relative_motion(
+static void relative_pointer_handle_relative_motion(
void *data,
struct zwp_relative_pointer_v1 * /*zwp_relative_pointer_v1*/,
uint32_t /*utime_hi*/,
@@ -447,42 +582,56 @@ static void relative_pointer_relative_motion(
wl_fixed_t /*dy_unaccel*/)
{
input_t *input = static_cast<input_t *>(data);
-
- input->x += wl_fixed_to_int(dx);
- input->y += wl_fixed_to_int(dy);
-
- GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>(
- wl_surface_get_user_data(input->focus_pointer));
+ GHOST_WindowWayland *win = window_from_surface(input->focus_pointer);
+ if (!win) {
+ return;
+ }
+ const wl_fixed_t scale = win->scale();
+ input->xy[0] += dx / scale;
+ input->xy[1] += dy / scale;
input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
GHOST_kEventCursorMove,
win,
- input->x,
- input->y,
+ wl_fixed_to_int(scale * input->xy[0]),
+ wl_fixed_to_int(scale * input->xy[1]),
GHOST_TABLET_DATA_NONE));
}
static const zwp_relative_pointer_v1_listener relative_pointer_listener = {
- relative_pointer_relative_motion,
+ relative_pointer_handle_relative_motion,
};
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Data Source), #wl_data_source_listener
+ * \{ */
+
static void dnd_events(const input_t *const input, const GHOST_TEventType event)
{
+ /* NOTE: `input->data_offer_dnd_mutex` must already be locked. */
const uint64_t time = input->system->getMilliSeconds();
- GHOST_IWindow *const window = static_cast<GHOST_WindowWayland *>(
- wl_surface_get_user_data(input->focus_pointer));
+ GHOST_WindowWayland *const win = static_cast<GHOST_WindowWayland *>(
+ wl_surface_get_user_data(input->focus_dnd));
+ if (!win) {
+ return;
+ }
+ const wl_fixed_t scale = win->scale();
+ const int event_xy[2] = {
+ wl_fixed_to_int(scale * input->data_offer_dnd->dnd.xy[0]),
+ wl_fixed_to_int(scale * input->data_offer_dnd->dnd.xy[1]),
+ };
+
for (const std::string &type : mime_preference_order) {
- input->system->pushEvent(new GHOST_EventDragnDrop(time,
- event,
- mime_dnd.at(type),
- window,
- input->data_offer_dnd->dnd.x,
- input->data_offer_dnd->dnd.y,
- nullptr));
+ input->system->pushEvent(new GHOST_EventDragnDrop(
+ time, event, mime_dnd.at(type), win, event_xy[0], event_xy[1], nullptr));
}
}
-static std::string read_pipe(data_offer_t *data_offer, const std::string mime_receive)
+static std::string read_pipe(data_offer_t *data_offer,
+ const std::string mime_receive,
+ std::mutex *mutex)
{
int pipefd[2];
if (pipe(pipefd) != 0) {
@@ -491,6 +640,13 @@ static std::string read_pipe(data_offer_t *data_offer, const std::string mime_re
wl_data_offer_receive(data_offer->id, mime_receive.c_str(), pipefd[1]);
close(pipefd[1]);
+ data_offer->in_use.store(false);
+
+ if (mutex) {
+ mutex->unlock();
+ }
+ /* WARNING: `data_offer` may be freed from now on. */
+
std::string data;
ssize_t len;
char buffer[4096];
@@ -498,7 +654,6 @@ static std::string read_pipe(data_offer_t *data_offer, const std::string mime_re
data.insert(data.end(), buffer, buffer + len);
}
close(pipefd[0]);
- data_offer->in_use.store(false);
return data;
}
@@ -509,26 +664,29 @@ static std::string read_pipe(data_offer_t *data_offer, const std::string mime_re
* Sent when a target accepts pointer_focus or motion events. If
* a target does not accept any of the offered types, type is nullptr.
*/
-static void data_source_target(void * /*data*/,
- struct wl_data_source * /*wl_data_source*/,
- const char * /*mime_type*/)
+static void data_source_handle_target(void * /*data*/,
+ struct wl_data_source * /*wl_data_source*/,
+ const char * /*mime_type*/)
{
/* pass */
}
-static void data_source_send(void *data,
- struct wl_data_source * /*wl_data_source*/,
- const char * /*mime_type*/,
- int32_t fd)
+static void data_source_handle_send(void *data,
+ struct wl_data_source * /*wl_data_source*/,
+ const char * /*mime_type*/,
+ int32_t fd)
{
- const char *const buffer = static_cast<char *>(data);
+ input_t *input = static_cast<input_t *>(data);
+ std::lock_guard lock{input->data_source_mutex};
+
+ const char *const buffer = input->data_source->buffer_out;
if (write(fd, buffer, strlen(buffer)) < 0) {
GHOST_PRINT("error writing to clipboard: " << std::strerror(errno) << std::endl);
}
close(fd);
}
-static void data_source_cancelled(void * /*data*/, struct wl_data_source *wl_data_source)
+static void data_source_handle_cancelled(void * /*data*/, struct wl_data_source *wl_data_source)
{
wl_data_source_destroy(wl_data_source);
}
@@ -540,8 +698,8 @@ static void data_source_cancelled(void * /*data*/, struct wl_data_source *wl_dat
* indicate acceptance, #wl_data_source.cancelled may still be
* emitted afterwards if the drop destination does not accept any mime type.
*/
-static void data_source_dnd_drop_performed(void * /*data*/,
- struct wl_data_source * /*wl_data_source*/)
+static void data_source_handle_dnd_drop_performed(void * /*data*/,
+ struct wl_data_source * /*wl_data_source*/)
{
/* pass */
}
@@ -553,7 +711,8 @@ static void data_source_dnd_drop_performed(void * /*data*/,
* source, so the client is now free to destroy this data source
* and free all associated data.
*/
-static void data_source_dnd_finished(void * /*data*/, struct wl_data_source * /*wl_data_source*/)
+static void data_source_handle_dnd_finished(void * /*data*/,
+ struct wl_data_source * /*wl_data_source*/)
{
/* pass */
}
@@ -565,73 +724,87 @@ static void data_source_dnd_finished(void * /*data*/, struct wl_data_source * /*
* after matching the source/destination side actions. Only one
* action (or none) will be offered here.
*/
-static void data_source_action(void * /*data*/,
- struct wl_data_source * /*wl_data_source*/,
- uint32_t /*dnd_action*/)
+static void data_source_handle_action(void * /*data*/,
+ struct wl_data_source * /*wl_data_source*/,
+ uint32_t /*dnd_action*/)
{
/* pass */
}
static const struct wl_data_source_listener data_source_listener = {
- data_source_target,
- data_source_send,
- data_source_cancelled,
- data_source_dnd_drop_performed,
- data_source_dnd_finished,
- data_source_action,
+ data_source_handle_target,
+ data_source_handle_send,
+ data_source_handle_cancelled,
+ data_source_handle_dnd_drop_performed,
+ data_source_handle_dnd_finished,
+ data_source_handle_action,
};
-static void data_offer_offer(void *data,
- struct wl_data_offer * /*wl_data_offer*/,
- const char *mime_type)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Data Offer), #wl_data_offer_listener
+ * \{ */
+
+static void data_offer_handle_offer(void *data,
+ struct wl_data_offer * /*wl_data_offer*/,
+ const char *mime_type)
{
static_cast<data_offer_t *>(data)->types.insert(mime_type);
}
-static void data_offer_source_actions(void *data,
- struct wl_data_offer * /*wl_data_offer*/,
- uint32_t source_actions)
+static void data_offer_handle_source_actions(void *data,
+ struct wl_data_offer * /*wl_data_offer*/,
+ uint32_t source_actions)
{
static_cast<data_offer_t *>(data)->source_actions = source_actions;
}
-static void data_offer_action(void *data,
- struct wl_data_offer * /*wl_data_offer*/,
- uint32_t dnd_action)
+static void data_offer_handle_action(void *data,
+ struct wl_data_offer * /*wl_data_offer*/,
+ uint32_t dnd_action)
{
static_cast<data_offer_t *>(data)->dnd_action = dnd_action;
}
static const struct wl_data_offer_listener data_offer_listener = {
- data_offer_offer,
- data_offer_source_actions,
- data_offer_action,
+ data_offer_handle_offer,
+ data_offer_handle_source_actions,
+ data_offer_handle_action,
};
-static void data_device_data_offer(void * /*data*/,
- struct wl_data_device * /*wl_data_device*/,
- struct wl_data_offer *id)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Data Device), #wl_data_device_listener
+ * \{ */
+
+static void data_device_handle_data_offer(void * /*data*/,
+ struct wl_data_device * /*wl_data_device*/,
+ struct wl_data_offer *id)
{
data_offer_t *data_offer = new data_offer_t;
data_offer->id = id;
wl_data_offer_add_listener(id, &data_offer_listener, data_offer);
}
-static void data_device_enter(void *data,
- struct wl_data_device * /*wl_data_device*/,
- uint32_t serial,
- struct wl_surface * /*surface*/,
- wl_fixed_t x,
- wl_fixed_t y,
- struct wl_data_offer *id)
+static void data_device_handle_enter(void *data,
+ struct wl_data_device * /*wl_data_device*/,
+ uint32_t serial,
+ struct wl_surface *surface,
+ wl_fixed_t x,
+ wl_fixed_t y,
+ struct wl_data_offer *id)
{
input_t *input = static_cast<input_t *>(data);
+ std::lock_guard lock{input->data_offer_dnd_mutex};
+
input->data_offer_dnd = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id));
data_offer_t *data_offer = input->data_offer_dnd;
data_offer->in_use.store(true);
- data_offer->dnd.x = wl_fixed_to_int(x);
- data_offer->dnd.y = wl_fixed_to_int(y);
+ data_offer->dnd.xy[0] = x;
+ data_offer->dnd.xy[1] = y;
wl_data_offer_set_actions(id,
WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY |
@@ -642,14 +815,17 @@ static void data_device_enter(void *data,
wl_data_offer_accept(id, serial, type.c_str());
}
+ input->focus_dnd = surface;
dnd_events(input, GHOST_kEventDraggingEntered);
}
-static void data_device_leave(void *data, struct wl_data_device * /*wl_data_device*/)
+static void data_device_handle_leave(void *data, struct wl_data_device * /*wl_data_device*/)
{
input_t *input = static_cast<input_t *>(data);
+ std::lock_guard lock{input->data_offer_dnd_mutex};
dnd_events(input, GHOST_kEventDraggingExited);
+ input->focus_dnd = nullptr;
if (input->data_offer_dnd && !input->data_offer_dnd->in_use.load()) {
wl_data_offer_destroy(input->data_offer_dnd->id);
@@ -658,21 +834,26 @@ static void data_device_leave(void *data, struct wl_data_device * /*wl_data_devi
}
}
-static void data_device_motion(void *data,
- struct wl_data_device * /*wl_data_device*/,
- uint32_t /*time*/,
- wl_fixed_t x,
- wl_fixed_t y)
+static void data_device_handle_motion(void *data,
+ struct wl_data_device * /*wl_data_device*/,
+ uint32_t /*time*/,
+ wl_fixed_t x,
+ wl_fixed_t y)
{
input_t *input = static_cast<input_t *>(data);
- input->data_offer_dnd->dnd.x = wl_fixed_to_int(x);
- input->data_offer_dnd->dnd.y = wl_fixed_to_int(y);
+ std::lock_guard lock{input->data_offer_dnd_mutex};
+
+ input->data_offer_dnd->dnd.xy[0] = x;
+ input->data_offer_dnd->dnd.xy[1] = y;
+
dnd_events(input, GHOST_kEventDraggingUpdated);
}
-static void data_device_drop(void *data, struct wl_data_device * /*wl_data_device*/)
+static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_data_device*/)
{
input_t *input = static_cast<input_t *>(data);
+ std::lock_guard lock{input->data_offer_dnd_mutex};
+
data_offer_t *data_offer = input->data_offer_dnd;
const std::string mime_receive = *std::find_first_of(mime_preference_order.begin(),
@@ -680,13 +861,13 @@ static void data_device_drop(void *data, struct wl_data_device * /*wl_data_devic
data_offer->types.begin(),
data_offer->types.end());
- auto read_uris = [](input_t *const input,
- data_offer_t *data_offer,
- const std::string mime_receive) {
- const int x = data_offer->dnd.x;
- const int y = data_offer->dnd.y;
+ auto read_uris_fn = [](input_t *const input,
+ data_offer_t *data_offer,
+ wl_surface *surface,
+ const std::string mime_receive) {
+ const wl_fixed_t xy[2] = {data_offer->dnd.xy[0], data_offer->dnd.xy[1]};
- const std::string data = read_pipe(data_offer, mime_receive);
+ const std::string data = read_pipe(data_offer, mime_receive, nullptr);
wl_data_offer_finish(data_offer->id);
wl_data_offer_destroy(data_offer->id);
@@ -700,6 +881,9 @@ static void data_device_drop(void *data, struct wl_data_device * /*wl_data_devic
static constexpr const char *file_proto = "file://";
static constexpr const char *crlf = "\r\n";
+ GHOST_WindowWayland *win = window_from_surface(surface);
+ GHOST_ASSERT(win != nullptr, "Unable to find window for drop event from surface");
+
std::vector<std::string> uris;
size_t pos = 0;
@@ -723,14 +907,14 @@ static void data_device_drop(void *data, struct wl_data_device * /*wl_data_devic
flist->strings[i] = static_cast<uint8_t *>(malloc((uris[i].size() + 1) * sizeof(uint8_t)));
memcpy(flist->strings[i], uris[i].data(), uris[i].size() + 1);
}
- GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>(
- wl_surface_get_user_data(input->focus_pointer));
+
+ const wl_fixed_t scale = win->scale();
system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(),
GHOST_kEventDraggingDropDone,
GHOST_kDragnDropTypeFilenames,
win,
- x,
- y,
+ wl_fixed_to_int(scale * xy[0]),
+ wl_fixed_to_int(scale * xy[1]),
flist));
}
else if (mime_receive == mime_text_plain || mime_receive == mime_text_utf8) {
@@ -740,15 +924,20 @@ static void data_device_drop(void *data, struct wl_data_device * /*wl_data_devic
wl_display_roundtrip(system->display());
};
- std::thread read_thread(read_uris, input, data_offer, mime_receive);
+ /* Pass in `input->focus_dnd` instead of accessing it from `input` since the leave callback
+ * (#data_device_leave) will clear the value once this function starts. */
+ std::thread read_thread(read_uris_fn, input, data_offer, input->focus_dnd, mime_receive);
read_thread.detach();
}
-static void data_device_selection(void *data,
- struct wl_data_device * /*wl_data_device*/,
- struct wl_data_offer *id)
+static void data_device_handle_selection(void *data,
+ struct wl_data_device * /*wl_data_device*/,
+ struct wl_data_offer *id)
{
input_t *input = static_cast<input_t *>(data);
+
+ std::lock_guard lock{input->data_offer_copy_paste_mutex};
+
data_offer_t *data_offer = input->data_offer_copy_paste;
/* Delete old data offer. */
@@ -766,59 +955,76 @@ static void data_device_selection(void *data,
data_offer = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id));
input->data_offer_copy_paste = data_offer;
- std::string mime_receive;
- for (const std::string type : {mime_text_utf8, mime_text_plain}) {
- if (data_offer->types.count(type)) {
- mime_receive = type;
- break;
+ auto read_selection_fn = [](input_t *input) {
+ GHOST_SystemWayland *const system = input->system;
+ input->data_offer_copy_paste_mutex.lock();
+
+ data_offer_t *data_offer = input->data_offer_copy_paste;
+ std::string mime_receive;
+ for (const std::string type : {mime_text_utf8, mime_text_plain}) {
+ if (data_offer->types.count(type)) {
+ mime_receive = type;
+ break;
+ }
}
- }
+ const std::string data = read_pipe(
+ data_offer, mime_receive, &input->data_offer_copy_paste_mutex);
- auto read_selection = [](GHOST_SystemWayland *const system,
- data_offer_t *data_offer,
- const std::string mime_receive) {
- const std::string data = read_pipe(data_offer, mime_receive);
- system->setSelection(data);
+ {
+ std::lock_guard lock{system_selection_mutex};
+ system->setSelection(data);
+ }
};
- std::thread read_thread(read_selection, input->system, data_offer, mime_receive);
+ std::thread read_thread(read_selection_fn, input);
read_thread.detach();
}
static const struct wl_data_device_listener data_device_listener = {
- data_device_data_offer,
- data_device_enter,
- data_device_leave,
- data_device_motion,
- data_device_drop,
- data_device_selection,
+ data_device_handle_data_offer,
+ data_device_handle_enter,
+ data_device_handle_leave,
+ data_device_handle_motion,
+ data_device_handle_drop,
+ data_device_handle_selection,
};
-static void cursor_buffer_release(void *data, struct wl_buffer *wl_buffer)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Buffer), #wl_buffer_listener
+ * \{ */
+
+static void cursor_buffer_handle_release(void *data, struct wl_buffer *wl_buffer)
{
cursor_t *cursor = static_cast<cursor_t *>(data);
wl_buffer_destroy(wl_buffer);
- if (wl_buffer == cursor->buffer) {
+ if (wl_buffer == cursor->wl_buffer) {
/* the mapped buffer was from a custom cursor */
- cursor->buffer = nullptr;
+ cursor->wl_buffer = nullptr;
}
}
const struct wl_buffer_listener cursor_buffer_listener = {
- cursor_buffer_release,
+ cursor_buffer_handle_release,
};
-static GHOST_IWindow *get_window(struct wl_surface *surface)
-{
- if (!surface) {
- return nullptr;
- }
+/** \} */
- for (GHOST_IWindow *win : window_manager->getWindows()) {
- if (surface == static_cast<const GHOST_WindowWayland *>(win)->surface()) {
- return win;
+/* -------------------------------------------------------------------- */
+/** \name Listener (Surface), #wl_surface_listener
+ * \{ */
+
+static GHOST_WindowWayland *window_from_surface(struct wl_surface *surface)
+{
+ if (surface) {
+ for (GHOST_IWindow *iwin : window_manager->getWindows()) {
+ GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(iwin);
+ if (surface == win->surface()) {
+ return win;
+ }
}
}
return nullptr;
@@ -835,34 +1041,34 @@ static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm)
if (scale > 0 && cursor.scale != scale) {
cursor.scale = scale;
- wl_surface_set_buffer_scale(cursor.surface, scale);
- wl_cursor_theme_destroy(cursor.theme);
- cursor.theme = wl_cursor_theme_load(cursor.theme_name.c_str(), scale * cursor.size, shm);
+ wl_surface_set_buffer_scale(cursor.wl_surface, scale);
+ wl_cursor_theme_destroy(cursor.wl_theme);
+ cursor.wl_theme = wl_cursor_theme_load(cursor.theme_name.c_str(), scale * cursor.size, shm);
return true;
}
return false;
}
-static void cursor_surface_enter(void *data,
- struct wl_surface * /*wl_surface*/,
- struct wl_output *output)
+static void cursor_surface_handle_enter(void *data,
+ struct wl_surface * /*wl_surface*/,
+ struct wl_output *output)
{
input_t *input = static_cast<input_t *>(data);
for (const output_t *reg_output : input->system->outputs()) {
- if (reg_output->output == output) {
+ if (reg_output->wl_output == output) {
input->cursor.outputs.insert(reg_output);
}
}
update_cursor_scale(input->cursor, input->system->shm());
}
-static void cursor_surface_leave(void *data,
- struct wl_surface * /*wl_surface*/,
- struct wl_output *output)
+static void cursor_surface_handle_leave(void *data,
+ struct wl_surface * /*wl_surface*/,
+ struct wl_output *output)
{
input_t *input = static_cast<input_t *>(data);
for (const output_t *reg_output : input->system->outputs()) {
- if (reg_output->output == output) {
+ if (reg_output->wl_output == output) {
input->cursor.outputs.erase(reg_output);
}
}
@@ -870,19 +1076,24 @@ static void cursor_surface_leave(void *data,
}
struct wl_surface_listener cursor_surface_listener = {
- cursor_surface_enter,
- cursor_surface_leave,
+ cursor_surface_handle_enter,
+ cursor_surface_handle_leave,
};
-static void pointer_enter(void *data,
- struct wl_pointer * /*wl_pointer*/,
- uint32_t serial,
- struct wl_surface *surface,
- wl_fixed_t surface_x,
- wl_fixed_t surface_y)
-{
- GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(surface));
+/** \} */
+/* -------------------------------------------------------------------- */
+/** \name Listener (Pointer), #wl_pointer_listener
+ * \{ */
+
+static void pointer_handle_enter(void *data,
+ struct wl_pointer * /*wl_pointer*/,
+ uint32_t serial,
+ struct wl_surface *surface,
+ wl_fixed_t surface_x,
+ wl_fixed_t surface_y)
+{
+ GHOST_WindowWayland *win = window_from_surface(surface);
if (!win) {
return;
}
@@ -891,27 +1102,28 @@ static void pointer_enter(void *data,
input_t *input = static_cast<input_t *>(data);
input->pointer_serial = serial;
- input->x = win->scale() * wl_fixed_to_int(surface_x);
- input->y = win->scale() * wl_fixed_to_int(surface_y);
+ input->cursor_serial = serial;
+ input->xy[0] = surface_x;
+ input->xy[1] = surface_y;
input->focus_pointer = surface;
win->setCursorShape(win->getCursorShape());
+ const wl_fixed_t scale = win->scale();
input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
GHOST_kEventCursorMove,
static_cast<GHOST_WindowWayland *>(win),
- input->x,
- input->y,
+ wl_fixed_to_int(scale * input->xy[0]),
+ wl_fixed_to_int(scale * input->xy[1]),
GHOST_TABLET_DATA_NONE));
}
-static void pointer_leave(void *data,
- struct wl_pointer * /*wl_pointer*/,
- uint32_t /*serial*/,
- struct wl_surface *surface)
+static void pointer_handle_leave(void *data,
+ struct wl_pointer * /*wl_pointer*/,
+ uint32_t /*serial*/,
+ struct wl_surface *surface)
{
- GHOST_IWindow *win = get_window(surface);
-
+ GHOST_IWindow *win = window_from_surface(surface);
if (!win) {
return;
}
@@ -920,42 +1132,39 @@ static void pointer_leave(void *data,
static_cast<GHOST_WindowWayland *>(win)->deactivate();
}
-static void pointer_motion(void *data,
- struct wl_pointer * /*wl_pointer*/,
- uint32_t /*time*/,
- wl_fixed_t surface_x,
- wl_fixed_t surface_y)
+static void pointer_handle_motion(void *data,
+ struct wl_pointer * /*wl_pointer*/,
+ uint32_t /*time*/,
+ wl_fixed_t surface_x,
+ wl_fixed_t surface_y)
{
input_t *input = static_cast<input_t *>(data);
-
- GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(input->focus_pointer));
-
+ GHOST_WindowWayland *win = window_from_surface(input->focus_pointer);
if (!win) {
return;
}
- input->x = win->scale() * wl_fixed_to_int(surface_x);
- input->y = win->scale() * wl_fixed_to_int(surface_y);
+ input->xy[0] = surface_x;
+ input->xy[1] = surface_y;
+ const wl_fixed_t scale = win->scale();
input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
GHOST_kEventCursorMove,
win,
- input->x,
- input->y,
+ wl_fixed_to_int(scale * input->xy[0]),
+ wl_fixed_to_int(scale * input->xy[1]),
GHOST_TABLET_DATA_NONE));
}
-static void pointer_button(void *data,
- struct wl_pointer * /*wl_pointer*/,
- uint32_t serial,
- uint32_t /*time*/,
- uint32_t button,
- uint32_t state)
+static void pointer_handle_button(void *data,
+ struct wl_pointer * /*wl_pointer*/,
+ uint32_t serial,
+ uint32_t /*time*/,
+ uint32_t button,
+ uint32_t state)
{
input_t *input = static_cast<input_t *>(data);
-
- GHOST_IWindow *win = get_window(input->focus_pointer);
-
+ GHOST_IWindow *win = window_from_surface(input->focus_pointer);
if (!win) {
return;
}
@@ -995,22 +1204,20 @@ static void pointer_button(void *data,
break;
}
- input->data_source->source_serial = serial;
+ input->data_source_serial = serial;
input->buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED);
input->system->pushEvent(new GHOST_EventButton(
input->system->getMilliSeconds(), etype, win, ebutton, GHOST_TABLET_DATA_NONE));
}
-static void pointer_axis(void *data,
- struct wl_pointer * /*wl_pointer*/,
- uint32_t /*time*/,
- uint32_t axis,
- wl_fixed_t value)
+static void pointer_handle_axis(void *data,
+ struct wl_pointer * /*wl_pointer*/,
+ uint32_t /*time*/,
+ uint32_t axis,
+ wl_fixed_t value)
{
input_t *input = static_cast<input_t *>(data);
-
- GHOST_IWindow *win = get_window(input->focus_pointer);
-
+ GHOST_IWindow *win = window_from_surface(input->focus_pointer);
if (!win) {
return;
}
@@ -1024,14 +1231,358 @@ static void pointer_axis(void *data,
}
static const struct wl_pointer_listener pointer_listener = {
- pointer_enter,
- pointer_leave,
- pointer_motion,
- pointer_button,
- pointer_axis,
+ pointer_handle_enter,
+ pointer_handle_leave,
+ pointer_handle_motion,
+ pointer_handle_button,
+ pointer_handle_axis,
};
-static void keyboard_keymap(
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Tablet Tool), #zwp_tablet_tool_v2_listener
+ * \{ */
+
+static void tablet_tool_handle_type(void *data,
+ struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
+ uint32_t tool_type)
+{
+ tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
+
+ tool_input->data.Active = tablet_tool_map_type((enum zwp_tablet_tool_v2_type)tool_type);
+}
+
+static void tablet_tool_handle_hardware_serial(void * /*data*/,
+ struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
+ uint32_t /*hardware_serial_hi*/,
+ uint32_t /*hardware_serial_lo*/)
+{
+}
+
+static void tablet_tool_handle_hardware_id_wacom(
+ void * /*data*/,
+ struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
+ uint32_t /*hardware_id_hi*/,
+ uint32_t /*hardware_id_lo*/)
+{
+}
+
+static void tablet_tool_handle_capability(void * /*data*/,
+ struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
+ uint32_t /*capability*/)
+{
+}
+
+static void tablet_tool_handle_done(void * /*data*/,
+ struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/)
+{
+}
+static void tablet_tool_handle_removed(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2)
+{
+ tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
+ input_t *input = tool_input->input;
+
+ if (tool_input->cursor_surface) {
+ wl_surface_destroy(tool_input->cursor_surface);
+ }
+ input->tablet_tools.erase(zwp_tablet_tool_v2);
+
+ delete tool_input;
+}
+static void tablet_tool_handle_proximity_in(void *data,
+ struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
+ uint32_t serial,
+ struct zwp_tablet_v2 * /*tablet*/,
+ struct wl_surface *surface)
+{
+ tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
+ input_t *input = tool_input->input;
+
+ input->focus_tablet = surface;
+ input->tablet_serial = serial;
+ input->cursor_serial = serial;
+
+ input->data_source_serial = serial;
+
+ /* Update #GHOST_TabletData. */
+ GHOST_TabletData &td = tool_input->data;
+ /* Reset, to avoid using stale tilt/pressure. */
+ td.Xtilt = 0.0f;
+ td.Ytilt = 0.0f;
+ /* In case pressure isn't supported. */
+ td.Pressure = 1.0f;
+
+ GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
+ if (!win) {
+ return;
+ }
+ win->activate();
+
+ win->setCursorShape(win->getCursorShape());
+}
+static void tablet_tool_handle_proximity_out(void *data,
+ struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/)
+{
+ tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
+ input_t *input = tool_input->input;
+ input->focus_tablet = nullptr;
+
+ GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
+ if (!win) {
+ return;
+ }
+ win->setCursorShape(win->getCursorShape());
+}
+
+static void tablet_tool_handle_down(void *data,
+ struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
+ uint32_t serial)
+{
+ tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
+ input_t *input = tool_input->input;
+ GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
+ if (!win) {
+ return;
+ }
+
+ const GHOST_TEventType etype = GHOST_kEventButtonDown;
+ const GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft;
+ input->data_source_serial = serial;
+ input->buttons.set(ebutton, true);
+ input->system->pushEvent(new GHOST_EventButton(
+ input->system->getMilliSeconds(), etype, win, ebutton, tool_input->data));
+}
+
+static void tablet_tool_handle_up(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/)
+{
+ tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
+ input_t *input = tool_input->input;
+ GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
+ if (!win) {
+ return;
+ }
+
+ const GHOST_TEventType etype = GHOST_kEventButtonUp;
+ const GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft;
+ input->buttons.set(ebutton, false);
+ input->system->pushEvent(new GHOST_EventButton(
+ input->system->getMilliSeconds(), etype, win, ebutton, tool_input->data));
+}
+
+static void tablet_tool_handle_motion(void *data,
+ struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
+ wl_fixed_t x,
+ wl_fixed_t y)
+{
+ tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
+ input_t *input = tool_input->input;
+ GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
+ if (!win) {
+ return;
+ }
+
+ input->xy[0] = x;
+ input->xy[1] = y;
+
+ const wl_fixed_t scale = win->scale();
+ input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
+ GHOST_kEventCursorMove,
+ win,
+ wl_fixed_to_int(scale * input->xy[0]),
+ wl_fixed_to_int(scale * input->xy[1]),
+ tool_input->data));
+}
+
+static void tablet_tool_handle_pressure(void *data,
+ struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
+ uint32_t pressure)
+{
+ tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
+ input_t *input = tool_input->input;
+ GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
+ if (!win) {
+ return;
+ }
+
+ GHOST_TabletData &td = tool_input->data;
+ td.Pressure = (float)pressure / 65535;
+}
+static void tablet_tool_handle_distance(void * /*data*/,
+ struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
+ uint32_t /*distance*/)
+{
+}
+static void tablet_tool_handle_tilt(void *data,
+ struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
+ wl_fixed_t tilt_x,
+ wl_fixed_t tilt_y)
+{
+ tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
+ input_t *input = tool_input->input;
+ GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
+ if (!win) {
+ return;
+ }
+
+ GHOST_TabletData &td = tool_input->data;
+ /* Map degrees to `-1.0..1.0`. */
+ td.Xtilt = wl_fixed_to_double(tilt_x) / 90.0f;
+ td.Ytilt = wl_fixed_to_double(tilt_y) / 90.0f;
+ td.Xtilt = td.Xtilt < -1.0f ? -1.0f : (td.Xtilt > 1.0f ? 1.0f : td.Xtilt);
+ td.Ytilt = td.Ytilt < -1.0f ? -1.0f : (td.Ytilt > 1.0f ? 1.0f : td.Ytilt);
+}
+
+static void tablet_tool_handle_rotation(void * /*data*/,
+ struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
+ wl_fixed_t /*degrees*/)
+{
+ /* Pass. */
+}
+
+static void tablet_tool_handle_slider(void * /*data*/,
+ struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
+ int32_t /*position*/)
+{
+ /* Pass. */
+}
+static void tablet_tool_handle_wheel(void *data,
+ struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
+ wl_fixed_t /*degrees*/,
+ int32_t clicks)
+{
+ if (clicks == 0) {
+ return;
+ }
+
+ tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
+ input_t *input = tool_input->input;
+ GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
+ if (!win) {
+ return;
+ }
+
+ input->system->pushEvent(new GHOST_EventWheel(input->system->getMilliSeconds(), win, clicks));
+}
+static void tablet_tool_handle_button(void *data,
+ struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
+ uint32_t serial,
+ uint32_t button,
+ uint32_t state)
+{
+ tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
+ input_t *input = tool_input->input;
+ GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
+ if (!win) {
+ return;
+ }
+
+ GHOST_TEventType etype = GHOST_kEventUnknown;
+ switch (state) {
+ case WL_POINTER_BUTTON_STATE_RELEASED:
+ etype = GHOST_kEventButtonUp;
+ break;
+ case WL_POINTER_BUTTON_STATE_PRESSED:
+ etype = GHOST_kEventButtonDown;
+ break;
+ }
+
+ GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft;
+ switch (button) {
+ case BTN_STYLUS:
+ ebutton = GHOST_kButtonMaskRight;
+ break;
+ case BTN_STYLUS2:
+ ebutton = GHOST_kButtonMaskMiddle;
+ break;
+ case BTN_STYLUS3:
+ ebutton = GHOST_kButtonMaskButton4;
+ break;
+ }
+
+ input->data_source_serial = serial;
+ input->buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED);
+ input->system->pushEvent(new GHOST_EventButton(
+ input->system->getMilliSeconds(), etype, win, ebutton, tool_input->data));
+}
+static void tablet_tool_handle_frame(void * /*data*/,
+ struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
+ uint32_t /*time*/)
+{
+}
+
+static const struct zwp_tablet_tool_v2_listener tablet_tool_listner = {
+ tablet_tool_handle_type,
+ tablet_tool_handle_hardware_serial,
+ tablet_tool_handle_hardware_id_wacom,
+ tablet_tool_handle_capability,
+ tablet_tool_handle_done,
+ tablet_tool_handle_removed,
+ tablet_tool_handle_proximity_in,
+ tablet_tool_handle_proximity_out,
+ tablet_tool_handle_down,
+ tablet_tool_handle_up,
+ tablet_tool_handle_motion,
+ tablet_tool_handle_pressure,
+ tablet_tool_handle_distance,
+ tablet_tool_handle_tilt,
+ tablet_tool_handle_rotation,
+ tablet_tool_handle_slider,
+ tablet_tool_handle_wheel,
+ tablet_tool_handle_button,
+ tablet_tool_handle_frame,
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Table Seat), #zwp_tablet_seat_v2_listener
+ * \{ */
+
+static void tablet_seat_handle_tablet_added(void * /*data*/,
+ struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/,
+ struct zwp_tablet_v2 * /*id*/)
+{
+ /* Pass. */
+}
+
+static void tablet_seat_handle_tool_added(void *data,
+ struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/,
+ struct zwp_tablet_tool_v2 *id)
+{
+ input_t *input = static_cast<input_t *>(data);
+ tablet_tool_input_t *tool_input = new tablet_tool_input_t();
+ tool_input->input = input;
+
+ /* Every tool has it's own cursor surface. */
+ tool_input->cursor_surface = wl_compositor_create_surface(input->system->compositor());
+ wl_surface_add_listener(tool_input->cursor_surface, &cursor_surface_listener, (void *)input);
+
+ zwp_tablet_tool_v2_add_listener(id, &tablet_tool_listner, tool_input);
+
+ input->tablet_tools.insert(id);
+}
+
+static void tablet_seat_handle_pad_added(void * /*data*/,
+ struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/,
+ struct zwp_tablet_pad_v2 * /*id*/)
+{
+ /* Pass. */
+}
+
+const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
+ tablet_seat_handle_tablet_added,
+ tablet_seat_handle_tool_added,
+ tablet_seat_handle_pad_added,
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Keyboard), #wl_keyboard_listener
+ * \{ */
+
+static void keyboard_handle_keymap(
void *data, struct wl_keyboard * /*wl_keyboard*/, uint32_t format, int32_t fd, uint32_t size)
{
input_t *input = static_cast<input_t *>(data);
@@ -1056,8 +1607,13 @@ static void keyboard_keymap(
return;
}
- input->xkb_state = xkb_state_new(keymap);
-
+ struct xkb_state *xkb_state_next = xkb_state_new(keymap);
+ if (xkb_state_next) {
+ if (input->xkb_state) {
+ xkb_state_unref(input->xkb_state);
+ }
+ input->xkb_state = xkb_state_next;
+ }
xkb_keymap_unref(keymap);
}
@@ -1067,11 +1623,11 @@ static void keyboard_keymap(
* Notification that this seat's keyboard focus is on a certain
* surface.
*/
-static void keyboard_enter(void *data,
- struct wl_keyboard * /*wl_keyboard*/,
- uint32_t /*serial*/,
- struct wl_surface *surface,
- struct wl_array * /*keys*/)
+static void keyboard_handle_enter(void *data,
+ struct wl_keyboard * /*wl_keyboard*/,
+ uint32_t /*serial*/,
+ struct wl_surface *surface,
+ struct wl_array * /*keys*/)
{
if (surface != nullptr) {
static_cast<input_t *>(data)->focus_keyboard = surface;
@@ -1084,10 +1640,10 @@ static void keyboard_enter(void *data,
* Notification that this seat's keyboard focus is no longer on a
* certain surface.
*/
-static void keyboard_leave(void *data,
- struct wl_keyboard * /*wl_keyboard*/,
- uint32_t /*serial*/,
- struct wl_surface *surface)
+static void keyboard_handle_leave(void *data,
+ struct wl_keyboard * /*wl_keyboard*/,
+ uint32_t /*serial*/,
+ struct wl_surface *surface)
{
if (surface != nullptr) {
static_cast<input_t *>(data)->focus_keyboard = nullptr;
@@ -1120,12 +1676,12 @@ static xkb_keysym_t xkb_state_key_get_one_sym_without_modifiers(struct xkb_state
return sym;
}
-static void keyboard_key(void *data,
- struct wl_keyboard * /*wl_keyboard*/,
- uint32_t serial,
- uint32_t /*time*/,
- uint32_t key,
- uint32_t state)
+static void keyboard_handle_key(void *data,
+ struct wl_keyboard * /*wl_keyboard*/,
+ uint32_t serial,
+ uint32_t /*time*/,
+ uint32_t key,
+ uint32_t state)
{
input_t *input = static_cast<input_t *>(data);
@@ -1144,7 +1700,6 @@ static void keyboard_key(void *data,
if (sym == XKB_KEY_NoSymbol) {
return;
}
- const GHOST_TKey gkey = xkb_map_gkey(sym);
/* Delete previous timer. */
if (xkb_keymap_key_repeats(xkb_state_get_keymap(input->xkb_state), key + 8) &&
@@ -1154,7 +1709,9 @@ static void keyboard_key(void *data,
input->key_repeat.timer = nullptr;
}
- GHOST_TEventKeyData key_data;
+ GHOST_TEventKeyData key_data = {
+ .key = xkb_map_gkey(sym),
+ };
if (etype == GHOST_kEventKeyDown) {
xkb_state_key_get_utf8(
@@ -1164,12 +1721,12 @@ static void keyboard_key(void *data,
key_data.utf8_buf[0] = '\0';
}
- input->data_source->source_serial = serial;
+ input->data_source_serial = serial;
GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>(
wl_surface_get_user_data(input->focus_keyboard));
input->system->pushEvent(new GHOST_EventKey(
- input->system->getMilliSeconds(), etype, win, gkey, '\0', key_data.utf8_buf, false));
+ input->system->getMilliSeconds(), etype, win, key_data.key, '\0', key_data.utf8_buf, false));
/* Start timer for repeating key, if applicable. */
if (input->key_repeat.rate > 0 &&
@@ -1179,33 +1736,32 @@ static void keyboard_key(void *data,
key_repeat_payload_t *payload = new key_repeat_payload_t({
.system = input->system,
.window = win,
- .key = gkey,
.key_data = key_data,
});
- auto cb = [](GHOST_ITimerTask *task, uint64_t /*time*/) {
+ auto key_repeat_fn = [](GHOST_ITimerTask *task, uint64_t /*time*/) {
struct key_repeat_payload_t *payload = static_cast<key_repeat_payload_t *>(
task->getUserData());
payload->system->pushEvent(new GHOST_EventKey(payload->system->getMilliSeconds(),
GHOST_kEventKeyDown,
payload->window,
- payload->key,
+ payload->key_data.key,
'\0',
payload->key_data.utf8_buf,
true));
};
input->key_repeat.timer = input->system->installTimer(
- input->key_repeat.delay, 1000 / input->key_repeat.rate, cb, payload);
+ input->key_repeat.delay, 1000 / input->key_repeat.rate, key_repeat_fn, payload);
}
}
-static void keyboard_modifiers(void *data,
- struct wl_keyboard * /*wl_keyboard*/,
- uint32_t /*serial*/,
- uint32_t mods_depressed,
- uint32_t mods_latched,
- uint32_t mods_locked,
- uint32_t group)
+static void keyboard_handle_modifiers(void *data,
+ struct wl_keyboard * /*wl_keyboard*/,
+ uint32_t /*serial*/,
+ uint32_t mods_depressed,
+ uint32_t mods_latched,
+ uint32_t mods_locked,
+ uint32_t group)
{
xkb_state_update_mask(static_cast<input_t *>(data)->xkb_state,
mods_depressed,
@@ -1216,10 +1772,10 @@ static void keyboard_modifiers(void *data,
group);
}
-static void keyboard_repeat_info(void *data,
- struct wl_keyboard * /*wl_keyboard*/,
- int32_t rate,
- int32_t delay)
+static void keyboard_repeat_handle_info(void *data,
+ struct wl_keyboard * /*wl_keyboard*/,
+ int32_t rate,
+ int32_t delay)
{
input_t *input = static_cast<input_t *>(data);
@@ -1228,79 +1784,174 @@ static void keyboard_repeat_info(void *data,
}
static const struct wl_keyboard_listener keyboard_listener = {
- keyboard_keymap,
- keyboard_enter,
- keyboard_leave,
- keyboard_key,
- keyboard_modifiers,
- keyboard_repeat_info,
+ keyboard_handle_keymap,
+ keyboard_handle_enter,
+ keyboard_handle_leave,
+ keyboard_handle_key,
+ keyboard_handle_modifiers,
+ keyboard_repeat_handle_info,
};
-static void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Seat), #wl_seat_listener
+ * \{ */
+
+static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities)
{
input_t *input = static_cast<input_t *>(data);
- input->pointer = nullptr;
- input->keyboard = nullptr;
+ input->wl_pointer = nullptr;
+ input->wl_keyboard = nullptr;
if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
- input->pointer = wl_seat_get_pointer(wl_seat);
- input->cursor.surface = wl_compositor_create_surface(input->system->compositor());
+ input->wl_pointer = wl_seat_get_pointer(wl_seat);
+ input->cursor.wl_surface = wl_compositor_create_surface(input->system->compositor());
input->cursor.visible = true;
- input->cursor.buffer = nullptr;
+ input->cursor.wl_buffer = nullptr;
input->cursor.file_buffer = new buffer_t;
if (!get_cursor_settings(input->cursor.theme_name, input->cursor.size)) {
input->cursor.theme_name = std::string();
input->cursor.size = default_cursor_size;
}
- wl_pointer_add_listener(input->pointer, &pointer_listener, data);
- wl_surface_add_listener(input->cursor.surface, &cursor_surface_listener, data);
+ wl_pointer_add_listener(input->wl_pointer, &pointer_listener, data);
+ wl_surface_add_listener(input->cursor.wl_surface, &cursor_surface_listener, data);
}
if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
- input->keyboard = wl_seat_get_keyboard(wl_seat);
- wl_keyboard_add_listener(input->keyboard, &keyboard_listener, data);
+ input->wl_keyboard = wl_seat_get_keyboard(wl_seat);
+ wl_keyboard_add_listener(input->wl_keyboard, &keyboard_listener, data);
}
}
-static void seat_name(void *data, struct wl_seat * /*wl_seat*/, const char *name)
+static void seat_handle_name(void *data, struct wl_seat * /*wl_seat*/, const char *name)
{
static_cast<input_t *>(data)->name = std::string(name);
}
static const struct wl_seat_listener seat_listener = {
- seat_capabilities,
- seat_name,
+ seat_handle_capabilities,
+ seat_handle_name,
};
-static void output_geometry(void *data,
- struct wl_output * /*wl_output*/,
- int32_t /*x*/,
- int32_t /*y*/,
- int32_t physical_width,
- int32_t physical_height,
- int32_t /*subpixel*/,
- const char *make,
- const char *model,
- int32_t transform)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (XDG Output), #zxdg_output_v1_listener
+ * \{ */
+
+static void xdg_output_handle_logical_position(void *data,
+ struct zxdg_output_v1 * /*xdg_output*/,
+ int32_t x,
+ int32_t y)
+{
+ output_t *output = static_cast<output_t *>(data);
+ output->position_logical[0] = x;
+ output->position_logical[1] = y;
+ output->has_position_logical = true;
+}
+
+static void xdg_output_handle_logical_size(void *data,
+ struct zxdg_output_v1 * /*xdg_output*/,
+ int32_t width,
+ int32_t height)
+{
+ output_t *output = static_cast<output_t *>(data);
+
+ if (output->size_logical[0] != 0 && output->size_logical[1] != 0) {
+ /* Original comment from SDL. */
+ /* FIXME: GNOME has a bug where the logical size does not account for
+ * scale, resulting in bogus viewport sizes.
+ *
+ * Until this is fixed, validate that _some_ kind of scaling is being
+ * done (we can't match exactly because fractional scaling can't be
+ * detected otherwise), then override if necessary.
+ * -flibit
+ */
+ 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");
+ return;
+ }
+ }
+
+ output->size_logical[0] = width;
+ output->size_logical[1] = height;
+ output->has_size_logical = true;
+}
+
+static void xdg_output_handle_done(void * /*data*/, struct zxdg_output_v1 * /*xdg_output*/)
+{
+ /* NOTE: `xdg-output.done` events are deprecated and only apply below version 3 of the protocol.
+ * `wl-output.done` event will be emitted in version 3 or higher. */
+}
+
+static void xdg_output_handle_name(void * /*data*/,
+ struct zxdg_output_v1 * /*xdg_output*/,
+ const char * /*name*/)
+{
+ /* Pass. */
+}
+
+static void xdg_output_handle_description(void * /*data*/,
+ struct zxdg_output_v1 * /*xdg_output*/,
+ const char * /*description*/)
+{
+ /* Pass. */
+}
+
+static const struct zxdg_output_v1_listener xdg_output_listener = {
+ xdg_output_handle_logical_position,
+ xdg_output_handle_logical_size,
+ xdg_output_handle_done,
+ xdg_output_handle_name,
+ xdg_output_handle_description,
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Output), #wl_output_listener
+ * \{ */
+
+static void output_handle_geometry(void *data,
+ struct wl_output * /*wl_output*/,
+ int32_t /*x*/,
+ int32_t /*y*/,
+ int32_t physical_width,
+ int32_t physical_height,
+ int32_t /*subpixel*/,
+ const char *make,
+ const char *model,
+ int32_t transform)
{
output_t *output = static_cast<output_t *>(data);
output->transform = transform;
output->make = std::string(make);
output->model = std::string(model);
- output->width_mm = physical_width;
- output->height_mm = physical_height;
+ output->size_mm[0] = physical_width;
+ output->size_mm[1] = physical_height;
}
-static void output_mode(void *data,
- struct wl_output * /*wl_output*/,
- uint32_t /*flags*/,
- int32_t width,
- int32_t height,
- int32_t /*refresh*/)
+static void output_handle_mode(void *data,
+ struct wl_output * /*wl_output*/,
+ uint32_t flags,
+ int32_t width,
+ int32_t height,
+ int32_t /*refresh*/)
{
output_t *output = static_cast<output_t *>(data);
- output->width_pxl = width;
- output->height_pxl = height;
+
+ if (flags & WL_OUTPUT_MODE_CURRENT) {
+ output->size_native[0] = width;
+ output->size_native[1] = height;
+
+ /* Don't rotate this yet, `wl-output` coordinates are transformed in
+ * handle_done and `xdg-output` coordinates are pre-transformed. */
+ if (!output->has_size_logical) {
+ output->size_logical[0] = width;
+ output->size_logical[1] = height;
+ }
+ }
}
/**
@@ -1311,36 +1962,71 @@ static void output_mode(void *data,
* changes done after that. This allows changes to the output
* properties to be seen as atomic, even if they happen via multiple events.
*/
-static void output_done(void * /*data*/, struct wl_output * /*wl_output*/)
+static void output_handle_done(void *data, struct wl_output * /*wl_output*/)
{
+ output_t *output = static_cast<output_t *>(data);
+ int32_t size_native[2];
+ if (output->transform & WL_OUTPUT_TRANSFORM_90) {
+ size_native[0] = output->size_native[1];
+ size_native[1] = output->size_native[1];
+ }
+ else {
+ size_native[0] = output->size_native[0];
+ size_native[1] = output->size_native[1];
+ }
+
+ /* If `xdg-output` is present, calculate the true scale of the desktop */
+ if (output->has_size_logical) {
+
+ /* NOTE: it's not necessary to divide these values by their greatest-common-denominator
+ * as even a 64k screen resolution doesn't approach overflowing an `int32_t`. */
+
+ GHOST_ASSERT(size_native[0] && output->size_logical[0],
+ "Screen size values were not set when they were expected to be.");
+
+ output->scale_fractional = wl_fixed_from_int(size_native[0]) / output->size_logical[0];
+ output->has_scale_fractional = true;
+ }
}
-static void output_scale(void *data, struct wl_output * /*wl_output*/, int32_t factor)
+static void output_handle_scale(void *data, struct wl_output * /*wl_output*/, int32_t factor)
{
static_cast<output_t *>(data)->scale = factor;
}
static const struct wl_output_listener output_listener = {
- output_geometry,
- output_mode,
- output_done,
- output_scale,
+ output_handle_geometry,
+ output_handle_mode,
+ output_handle_done,
+ output_handle_scale,
};
-static void shell_ping(void * /*data*/, struct xdg_wm_base *xdg_wm_base, uint32_t serial)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (XDG WM Base), #xdg_wm_base_listener
+ * \{ */
+
+static void shell_handle_ping(void * /*data*/, struct xdg_wm_base *xdg_wm_base, uint32_t serial)
{
xdg_wm_base_pong(xdg_wm_base, serial);
}
static const struct xdg_wm_base_listener shell_listener = {
- shell_ping,
+ shell_handle_ping,
};
-static void global_add(void *data,
- struct wl_registry *wl_registry,
- uint32_t name,
- const char *interface,
- uint32_t /*version*/)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Registry), #wl_registry_listener
+ * \{ */
+
+static void global_handle_add(void *data,
+ struct wl_registry *wl_registry,
+ uint32_t name,
+ const char *interface,
+ uint32_t /*version*/)
{
struct display_t *display = static_cast<struct display_t *>(data);
if (!strcmp(interface, wl_compositor_interface.name)) {
@@ -1356,30 +2042,37 @@ static void global_add(void *data,
display->xdg_decoration_manager = static_cast<zxdg_decoration_manager_v1 *>(
wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1));
}
+ else if (!strcmp(interface, zxdg_output_manager_v1_interface.name)) {
+ display->xdg_output_manager = static_cast<zxdg_output_manager_v1 *>(
+ wl_registry_bind(wl_registry, name, &zxdg_output_manager_v1_interface, 3));
+ for (output_t *output : display->outputs) {
+ output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager,
+ output->wl_output);
+ zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output);
+ }
+ }
else if (!strcmp(interface, wl_output_interface.name)) {
output_t *output = new output_t;
- output->scale = 1;
- output->output = static_cast<wl_output *>(
+ output->wl_output = static_cast<wl_output *>(
wl_registry_bind(wl_registry, name, &wl_output_interface, 2));
display->outputs.push_back(output);
- wl_output_add_listener(output->output, &output_listener, output);
+ wl_output_add_listener(output->wl_output, &output_listener, output);
+
+ if (display->xdg_output_manager) {
+ output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager,
+ output->wl_output);
+ zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output);
+ }
}
else if (!strcmp(interface, wl_seat_interface.name)) {
input_t *input = new input_t;
input->system = display->system;
input->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
- input->xkb_state = nullptr;
- input->data_offer_dnd = nullptr;
- input->data_offer_copy_paste = nullptr;
input->data_source = new data_source_t;
- input->data_source->data_source = nullptr;
- input->data_source->buffer_out = nullptr;
- input->relative_pointer = nullptr;
- input->locked_pointer = nullptr;
- input->seat = static_cast<wl_seat *>(
+ input->wl_seat = static_cast<wl_seat *>(
wl_registry_bind(wl_registry, name, &wl_seat_interface, 4));
display->inputs.push_back(input);
- wl_seat_add_listener(input->seat, &seat_listener, input);
+ wl_seat_add_listener(input->wl_seat, &seat_listener, input);
}
else if (!strcmp(interface, wl_shm_interface.name)) {
display->shm = static_cast<wl_shm *>(
@@ -1387,7 +2080,11 @@ static void global_add(void *data,
}
else if (!strcmp(interface, wl_data_device_manager_interface.name)) {
display->data_device_manager = static_cast<wl_data_device_manager *>(
- wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, 1));
+ wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, 3));
+ }
+ else if (!strcmp(interface, zwp_tablet_manager_v2_interface.name)) {
+ display->tablet_manager = static_cast<zwp_tablet_manager_v2 *>(
+ wl_registry_bind(wl_registry, name, &zwp_tablet_manager_v2_interface, 1));
}
else if (!strcmp(interface, zwp_relative_pointer_manager_v1_interface.name)) {
display->relative_pointer_manager = static_cast<zwp_relative_pointer_manager_v1 *>(
@@ -1408,13 +2105,15 @@ static void global_add(void *data,
* name is no longer available. If the client bound to the global
* using the bind request, the client should now destroy that object.
*/
-static void global_remove(void * /*data*/, struct wl_registry * /*wl_registry*/, uint32_t /*name*/)
+static void global_handle_remove(void * /*data*/,
+ struct wl_registry * /*wl_registry*/,
+ uint32_t /*name*/)
{
}
static const struct wl_registry_listener registry_listener = {
- global_add,
- global_remove,
+ global_handle_add,
+ global_handle_remove,
};
/** \} */
@@ -1427,6 +2126,8 @@ static const struct wl_registry_listener registry_listener = {
GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new display_t)
{
+ wl_log_set_handler_client(ghost_wayland_log_handler);
+
d->system = this;
/* Connect to the Wayland server. */
d->display = wl_display_connect(nullptr);
@@ -1453,10 +2154,18 @@ GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new display_t)
if (d->data_device_manager) {
for (input_t *input : d->inputs) {
input->data_device = wl_data_device_manager_get_data_device(d->data_device_manager,
- input->seat);
+ input->wl_seat);
wl_data_device_add_listener(input->data_device, &data_device_listener, input);
}
}
+
+ if (d->tablet_manager) {
+ for (input_t *input : d->inputs) {
+ input->tablet_seat = zwp_tablet_manager_v2_get_tablet_seat(d->tablet_manager,
+ input->wl_seat);
+ zwp_tablet_seat_v2_add_listener(input->tablet_seat, &tablet_seat_listener, input);
+ }
+ }
}
GHOST_SystemWayland::~GHOST_SystemWayland()
@@ -1485,42 +2194,47 @@ int GHOST_SystemWayland::setConsoleWindowState(GHOST_TConsoleWindowState /*actio
GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) const
{
- if (!d->inputs.empty()) {
- static const xkb_state_component mods_all = xkb_state_component(
- XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_MODS_LOCKED |
- XKB_STATE_MODS_EFFECTIVE);
-
- keys.set(GHOST_kModifierKeyLeftShift,
- xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_SHIFT, mods_all) ==
- 1);
- keys.set(GHOST_kModifierKeyRightShift,
- xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_SHIFT, mods_all) ==
- 1);
- keys.set(GHOST_kModifierKeyLeftAlt,
- xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "LAlt", mods_all) == 1);
- keys.set(GHOST_kModifierKeyRightAlt,
- xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "RAlt", mods_all) == 1);
- keys.set(GHOST_kModifierKeyLeftControl,
- xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "LControl", mods_all) == 1);
- keys.set(GHOST_kModifierKeyRightControl,
- xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "RControl", mods_all) == 1);
- keys.set(GHOST_kModifierKeyOS,
- xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "Super", mods_all) == 1);
- keys.set(GHOST_kModifierKeyNumMasks,
- xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "NumLock", mods_all) == 1);
-
- return GHOST_kSuccess;
+ if (d->inputs.empty()) {
+ return GHOST_kFailure;
}
- return GHOST_kFailure;
+
+ static const xkb_state_component mods_all = xkb_state_component(
+ XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_MODS_LOCKED |
+ XKB_STATE_MODS_EFFECTIVE);
+
+ bool val;
+
+ /* NOTE: XKB doesn't seem to differentiate between left/right modifiers. */
+
+ val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_SHIFT, mods_all) == 1;
+ keys.set(GHOST_kModifierKeyLeftShift, val);
+ keys.set(GHOST_kModifierKeyRightShift, val);
+
+ val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_ALT, mods_all) == 1;
+ keys.set(GHOST_kModifierKeyLeftAlt, val);
+ keys.set(GHOST_kModifierKeyRightAlt, val);
+
+ val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_CTRL, mods_all) == 1;
+ keys.set(GHOST_kModifierKeyLeftControl, val);
+ keys.set(GHOST_kModifierKeyRightControl, val);
+
+ val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_LOGO, mods_all) == 1;
+ keys.set(GHOST_kModifierKeyOS, val);
+
+ val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_NUM, mods_all) == 1;
+ keys.set(GHOST_kModifierKeyNumMasks, val);
+
+ return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_SystemWayland::getButtons(GHOST_Buttons &buttons) const
{
- if (!d->inputs.empty()) {
- buttons = d->inputs[0]->buttons;
- return GHOST_kSuccess;
+ if (d->inputs.empty()) {
+ return GHOST_kFailure;
}
- return GHOST_kFailure;
+
+ buttons = d->inputs[0]->buttons;
+ return GHOST_kSuccess;
}
char *GHOST_SystemWayland::getClipboard(bool /*selection*/) const
@@ -1536,25 +2250,29 @@ void GHOST_SystemWayland::putClipboard(const char *buffer, bool /*selection*/) c
return;
}
- data_source_t *data_source = d->inputs[0]->data_source;
+ input_t *input = d->inputs[0];
+
+ std::lock_guard lock{input->data_source_mutex};
+
+ data_source_t *data_source = input->data_source;
/* Copy buffer. */
+ free(data_source->buffer_out);
const size_t buffer_size = strlen(buffer) + 1;
data_source->buffer_out = static_cast<char *>(malloc(buffer_size));
std::memcpy(data_source->buffer_out, buffer, buffer_size);
data_source->data_source = wl_data_device_manager_create_data_source(d->data_device_manager);
- wl_data_source_add_listener(
- data_source->data_source, &data_source_listener, data_source->buffer_out);
+ wl_data_source_add_listener(data_source->data_source, &data_source_listener, input);
for (const std::string &type : mime_send) {
wl_data_source_offer(data_source->data_source, type.c_str());
}
- if (!d->inputs.empty() && d->inputs[0]->data_device) {
+ if (input->data_device) {
wl_data_device_set_selection(
- d->inputs[0]->data_device, data_source->data_source, data_source->source_serial);
+ input->data_device, data_source->data_source, input->data_source_serial);
}
}
@@ -1565,12 +2283,30 @@ uint8_t GHOST_SystemWayland::getNumDisplays() const
GHOST_TSuccess GHOST_SystemWayland::getCursorPosition(int32_t &x, int32_t &y) const
{
- if (d->inputs.empty() || (d->inputs[0]->focus_pointer == nullptr)) {
+ if (d->inputs.empty()) {
return GHOST_kFailure;
}
- x = d->inputs[0]->x;
- y = d->inputs[0]->y;
+ input_t *input = d->inputs[0];
+ struct wl_surface *surface = nullptr;
+ if (input->pointer_serial == input->cursor_serial) {
+ surface = input->focus_pointer;
+ }
+ else if (input->tablet_serial == input->cursor_serial) {
+ surface = input->focus_tablet;
+ }
+ if (!surface) {
+ return GHOST_kFailure;
+ }
+
+ GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(wl_surface_get_user_data(surface));
+ if (!win) {
+ return GHOST_kFailure;
+ }
+
+ const wl_fixed_t scale = win->scale();
+ x = wl_fixed_to_int(scale * input->xy[0]);
+ y = wl_fixed_to_int(scale * input->xy[1]);
return GHOST_kSuccess;
}
@@ -1583,8 +2319,8 @@ void GHOST_SystemWayland::getMainDisplayDimensions(uint32_t &width, uint32_t &he
{
if (getNumDisplays() > 0) {
/* We assume first output as main. */
- width = uint32_t(d->outputs[0]->width_pxl) / d->outputs[0]->scale;
- height = uint32_t(d->outputs[0]->height_pxl) / d->outputs[0]->scale;
+ width = uint32_t(d->outputs[0]->size_native[0]) / d->outputs[0]->scale;
+ height = uint32_t(d->outputs[0]->size_native[1]) / d->outputs[0]->scale;
}
}
@@ -1705,12 +2441,12 @@ wl_compositor *GHOST_SystemWayland::compositor()
return d->compositor;
}
-xdg_wm_base *GHOST_SystemWayland::shell()
+xdg_wm_base *GHOST_SystemWayland::xdg_shell()
{
return d->xdg_shell;
}
-zxdg_decoration_manager_v1 *GHOST_SystemWayland::decoration_manager()
+zxdg_decoration_manager_v1 *GHOST_SystemWayland::xdg_decoration_manager()
{
return d->xdg_decoration_manager;
}
@@ -1736,16 +2472,41 @@ static void set_cursor_buffer(input_t *input, wl_buffer *buffer)
c->visible = (buffer != nullptr);
- wl_surface_attach(c->surface, buffer, 0, 0);
+ const int32_t image_size_x = int32_t(c->wl_image.width);
+ const int32_t image_size_y = int32_t(c->wl_image.height);
- wl_surface_damage(c->surface, 0, 0, int32_t(c->image.width), int32_t(c->image.height));
- wl_pointer_set_cursor(input->pointer,
+ const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / c->scale;
+ const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / c->scale;
+
+ wl_surface_attach(c->wl_surface, buffer, 0, 0);
+ wl_surface_damage(c->wl_surface, 0, 0, image_size_x, image_size_y);
+
+ wl_pointer_set_cursor(input->wl_pointer,
input->pointer_serial,
- c->visible ? c->surface : nullptr,
- int32_t(c->image.hotspot_x) / c->scale,
- int32_t(c->image.hotspot_y) / c->scale);
+ c->visible ? c->wl_surface : nullptr,
+ hotspot_x,
+ hotspot_y);
+
+ wl_surface_commit(c->wl_surface);
- wl_surface_commit(c->surface);
+ /* Set the cursor for all tablet tools as well. */
+ for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) {
+ tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(
+ zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2));
+ /* FIXME: for some reason cursor scale is applied twice (when the scale isn't 1x),
+ * this happens both in gnome-shell & KDE. Setting the surface scale here doesn't help. */
+ // wl_surface_set_buffer_scale(tool_input->cursor_surface, 1);
+ wl_surface_attach(tool_input->cursor_surface, buffer, 0, 0);
+ wl_surface_damage(tool_input->cursor_surface, 0, 0, image_size_x, image_size_y);
+
+ zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2,
+ input->tablet_serial,
+ c->visible ? tool_input->cursor_surface : nullptr,
+ hotspot_x,
+ hotspot_y);
+
+ wl_surface_commit(tool_input->cursor_surface);
+ }
}
GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape)
@@ -1759,12 +2520,13 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape)
input_t *input = d->inputs[0];
cursor_t *c = &input->cursor;
- if (!c->theme) {
+ if (!c->wl_theme) {
/* The cursor surface hasn't entered an output yet. Initialize theme with scale 1. */
- c->theme = wl_cursor_theme_load(c->theme_name.c_str(), c->size, d->inputs[0]->system->shm());
+ c->wl_theme = wl_cursor_theme_load(
+ c->theme_name.c_str(), c->size, d->inputs[0]->system->shm());
}
- wl_cursor *cursor = wl_cursor_theme_get_cursor(c->theme, cursor_name.c_str());
+ wl_cursor *cursor = wl_cursor_theme_get_cursor(c->wl_theme, cursor_name.c_str());
if (!cursor) {
GHOST_PRINT("cursor '" << cursor_name << "' does not exist" << std::endl);
@@ -1777,8 +2539,8 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape)
return GHOST_kFailure;
}
- c->buffer = buffer;
- c->image = *image;
+ c->wl_buffer = buffer;
+ c->wl_image = *image;
set_cursor_buffer(input, buffer);
@@ -1884,11 +2646,11 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap,
}
}
- cursor->buffer = buffer;
- cursor->image.width = uint32_t(sizex);
- cursor->image.height = uint32_t(sizey);
- cursor->image.hotspot_x = uint32_t(hotX);
- cursor->image.hotspot_y = uint32_t(hotY);
+ cursor->wl_buffer = buffer;
+ cursor->wl_image.width = uint32_t(sizex);
+ cursor->wl_image.height = uint32_t(sizey);
+ cursor->wl_image.hotspot_x = uint32_t(hotX);
+ cursor->wl_image.hotspot_y = uint32_t(hotY);
set_cursor_buffer(d->inputs[0], buffer);
@@ -1906,7 +2668,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(bool visible)
cursor_t *cursor = &input->cursor;
if (visible) {
if (!cursor->visible) {
- set_cursor_buffer(input, cursor->buffer);
+ set_cursor_buffer(input, cursor->wl_buffer);
}
}
else {
@@ -1918,9 +2680,21 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(bool visible)
return GHOST_kSuccess;
}
+bool GHOST_SystemWayland::supportsCursorWarp()
+{
+ /* WAYLAND doesn't support setting the cursor position directly,
+ * this is an intentional choice, forcing us to use a software cursor in this case. */
+ return false;
+}
+
+bool GHOST_SystemWayland::supportsWindowPosition()
+{
+ /* WAYLAND doesn't support accessing the window position. */
+ return false;
+}
+
GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mode,
const GHOST_TGrabCursorMode mode_current,
-
wl_surface *surface)
{
/* ignore, if the required protocols are not supported */
@@ -1939,42 +2713,123 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mo
input_t *input = d->inputs[0];
- if (mode_current == GHOST_kGrabHide) {
+#define MODE_NEEDS_LOCK(m) ((m) == GHOST_kGrabWrap || (m) == GHOST_kGrabHide)
+#define MODE_NEEDS_HIDE(m) ((m) == GHOST_kGrabHide)
+#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);
+
+ /* 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_confine = MODE_NEEDS_CONFINE(mode_current);
+ const bool use_confine = MODE_NEEDS_CONFINE(mode);
+
+#undef MODE_NEEDS_LOCK
+#undef MODE_NEEDS_HIDE
+#undef MODE_NEEDS_CONFINE
+
+ if (!use_hide) {
setCursorVisibility(true);
}
- if ((mode == GHOST_kGrabDisable) ||
- /* Switching from one grab mode to another,
- * in this case disable the current locks as it makes logic confusing,
- * postpone changing the cursor to avoid flickering. */
- (mode_current != GHOST_kGrabDisable)) {
+ /* Switching from one grab mode to another,
+ * in this case disable the current locks as it makes logic confusing,
+ * postpone changing the cursor to avoid flickering. */
+ if (!use_lock) {
if (input->relative_pointer) {
zwp_relative_pointer_v1_destroy(input->relative_pointer);
input->relative_pointer = nullptr;
}
if (input->locked_pointer) {
+ /* Request location to restore to. */
+ if (mode_current == GHOST_kGrabWrap) {
+ /* The chance this fails is _very_ low. */
+ GHOST_WindowWayland *win = window_from_surface(surface);
+ if (!win) {
+ GHOST_PRINT("could not find window from surface when un-grabbing!" << std::endl);
+ }
+ else {
+ GHOST_Rect bounds;
+ int32_t xy_new[2] = {input->xy[0], input->xy[1]};
+
+ /* Fallback to window bounds. */
+ if (win->getCursorGrabBounds(bounds) == GHOST_kFailure) {
+ win->getClientBounds(bounds);
+ }
+
+ const int scale = win->scale();
+
+ 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.wrapPoint(xy_new[0], xy_new[1], 0, win->getCursorGrabAxis());
+
+ /* Push an event so the new location is registered. */
+ if ((xy_new[0] != input->xy[0]) || (xy_new[1] != input->xy[1])) {
+ input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
+ GHOST_kEventCursorMove,
+ win,
+ wl_fixed_to_int(scale * xy_new[0]),
+ wl_fixed_to_int(scale * xy_new[1]),
+ GHOST_TABLET_DATA_NONE));
+ }
+ input->xy[0] = xy_new[0];
+ input->xy[1] = xy_new[1];
+
+ zwp_locked_pointer_v1_set_cursor_position_hint(
+ input->locked_pointer, xy_new[0], xy_new[1]);
+ wl_surface_commit(surface);
+ }
+ }
+
zwp_locked_pointer_v1_destroy(input->locked_pointer);
input->locked_pointer = nullptr;
}
}
+ if (!use_confine) {
+ if (input->confined_pointer) {
+ zwp_confined_pointer_v1_destroy(input->confined_pointer);
+ input->confined_pointer = nullptr;
+ }
+ }
+
if (mode != GHOST_kGrabDisable) {
- /* TODO(@campbellbarton): As WAYLAND does not support warping the pointer it may not be
- * possible to support #GHOST_kGrabWrap by pragmatically settings it's coordinates.
- * An alternative could be to draw the cursor in software (and hide the real cursor),
- * or just accept a locked cursor on WAYLAND. */
- input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(
- d->relative_pointer_manager, input->pointer);
- zwp_relative_pointer_v1_add_listener(
- input->relative_pointer, &relative_pointer_listener, input);
- input->locked_pointer = zwp_pointer_constraints_v1_lock_pointer(
- d->pointer_constraints,
- surface,
- input->pointer,
- nullptr,
- ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
-
- if (mode == GHOST_kGrabHide) {
+ if (use_lock) {
+ if (!was_lock) {
+ /* TODO(@campbellbarton): As WAYLAND does not support warping the pointer it may not be
+ * possible to support #GHOST_kGrabWrap by pragmatically settings it's coordinates.
+ * An alternative could be to draw the cursor in software (and hide the real cursor),
+ * or just accept a locked cursor on WAYLAND. */
+ input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(
+ d->relative_pointer_manager, input->wl_pointer);
+ zwp_relative_pointer_v1_add_listener(
+ input->relative_pointer, &relative_pointer_listener, input);
+ input->locked_pointer = zwp_pointer_constraints_v1_lock_pointer(
+ d->pointer_constraints,
+ surface,
+ input->wl_pointer,
+ nullptr,
+ ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
+ }
+ }
+ else if (use_confine) {
+ if (!was_confine) {
+ input->confined_pointer = zwp_pointer_constraints_v1_confine_pointer(
+ d->pointer_constraints,
+ surface,
+ input->wl_pointer,
+ nullptr,
+ ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
+ }
+ }
+
+ if (use_hide && !was_hide) {
setCursorVisibility(false);
}
}
diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h
index eeb65eb4fc3..762ceb80e38 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.h
+++ b/intern/ghost/intern/GHOST_SystemWayland.h
@@ -22,11 +22,32 @@ class GHOST_WindowWayland;
struct display_t;
struct output_t {
- struct wl_output *output;
- int32_t width_pxl, height_pxl; /* Dimensions in pixel. */
- int32_t width_mm, height_mm; /* Dimensions in millimeter. */
- int transform;
- int scale;
+ struct wl_output *wl_output = nullptr;
+ struct zxdg_output_v1 *xdg_output = nullptr;
+ /** Dimensions in pixels. */
+ int32_t size_native[2] = {0, 0};
+ /** Dimensions in millimeter. */
+ int32_t size_mm[2] = {0, 0};
+
+ int32_t size_logical[2] = {0, 0};
+ bool has_size_logical = false;
+
+ int32_t position_logical[2] = {0, 0};
+ bool has_position_logical = false;
+
+ int transform = 0;
+ int scale = 1;
+ /**
+ * The integer `scale` value should be used in almost all cases,
+ * as this is what is used for most API calls.
+ * Only use fractional scaling to calculate the DPI.
+ *
+ * \note Internally an #wl_fixed_t is used to store the scale of the display,
+ * so use the same value here (avoid floating point arithmetic in general).
+ */
+ wl_fixed_t scale_fractional = wl_fixed_from_int(1);
+ bool has_scale_fractional = false;
+
std::string make;
std::string model;
};
@@ -79,9 +100,9 @@ class GHOST_SystemWayland : public GHOST_System {
wl_compositor *compositor();
- xdg_wm_base *shell();
+ xdg_wm_base *xdg_shell();
- zxdg_decoration_manager_v1 *decoration_manager();
+ zxdg_decoration_manager_v1 *xdg_decoration_manager();
const std::vector<output_t *> &outputs() const;
@@ -103,6 +124,9 @@ class GHOST_SystemWayland : public GHOST_System {
GHOST_TSuccess setCursorVisibility(bool visible);
+ bool supportsCursorWarp();
+ bool supportsWindowPosition();
+
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..de7c5422d3f 100644
--- a/intern/ghost/intern/GHOST_Window.cpp
+++ b/intern/ghost/intern/GHOST_Window.cpp
@@ -155,10 +155,31 @@ 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;
}
+void GHOST_Window::getCursorGrabState(GHOST_TGrabCursorMode &mode,
+ GHOST_TAxisFlag &wrap_axis,
+ GHOST_Rect &bounds)
+{
+ mode = m_cursorGrab;
+ 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;
+ }
+}
+
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..adbc29eb84e 100644
--- a/intern/ghost/intern/GHOST_Window.h
+++ b/intern/ghost/intern/GHOST_Window.h
@@ -152,6 +152,10 @@ class GHOST_Window : public GHOST_IWindow {
*/
GHOST_TSuccess getCursorGrabBounds(GHOST_Rect &bounds);
+ void getCursorGrabState(GHOST_TGrabCursorMode &mode,
+ 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/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp
index f9f168f772d..21e3793d3b1 100644
--- a/intern/ghost/intern/GHOST_WindowWayland.cpp
+++ b/intern/ghost/intern/GHOST_WindowWayland.cpp
@@ -15,41 +15,117 @@
#include <wayland-egl.h>
+#include <algorithm> /* For `std::find`. */
+
static constexpr size_t base_dpi = 96;
struct window_t {
- GHOST_WindowWayland *w;
- wl_surface *surface;
- /* Outputs on which the window is currently shown on. */
- std::unordered_set<const output_t *> outputs;
- uint16_t dpi = 0;
- int scale = 1;
- struct xdg_surface *xdg_surface;
- struct xdg_toplevel *xdg_toplevel;
+ GHOST_WindowWayland *w = nullptr;
+ struct wl_surface *wl_surface = nullptr;
+ /**
+ * Outputs on which the window is currently shown on.
+ *
+ * This is an ordered set (whoever adds to this is responsible for keeping members unique).
+ * In practice this is rarely manipulated and is limited by the number of physical displays.
+ */
+ std::vector<output_t *> outputs;
+
+ /** The scale value written to #wl_surface_set_buffer_scale. */
+ int scale = 0;
+ /**
+ * The DPI, either:
+ * - `scale * base_dpi`
+ * - `wl_fixed_to_int(scale_fractional * base_dpi)`
+ * When fractional scaling is available.
+ */
+ uint32_t dpi = 0;
+
+ struct xdg_surface *xdg_surface = nullptr;
+ struct xdg_toplevel *xdg_toplevel = nullptr;
struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration = nullptr;
- enum zxdg_toplevel_decoration_v1_mode decoration_mode;
- wl_egl_window *egl_window;
- int32_t pending_width, pending_height;
- bool is_maximised;
- bool is_fullscreen;
- bool is_active;
- bool is_dialog;
- int32_t width, height;
+ enum zxdg_toplevel_decoration_v1_mode decoration_mode = (enum zxdg_toplevel_decoration_v1_mode)0;
+ wl_egl_window *egl_window = nullptr;
+ bool is_maximised = false;
+ bool is_fullscreen = false;
+ bool is_active = false;
+ bool is_dialog = false;
+
+ int32_t size[2] = {0, 0};
+ int32_t size_pending[2] = {0, 0};
};
/* -------------------------------------------------------------------- */
-/** \name Wayland Interface Callbacks
- *
- * These callbacks are registered for Wayland interfaces and called when
- * an event is received from the compositor.
+/** \name Internal Utilities
+ * \{ */
+
+/**
+ * Return -1 if `output_a` has a scale smaller than `output_b`, 0 when there equal, otherwise 1.
+ */
+static int output_scale_cmp(const output_t *output_a, const output_t *output_b)
+{
+ if (output_a->scale < output_b->scale) {
+ return -1;
+ }
+ if (output_a->scale > output_b->scale) {
+ return 1;
+ }
+ if (output_a->has_scale_fractional || output_b->has_scale_fractional) {
+ const wl_fixed_t scale_fractional_a = output_a->has_scale_fractional ?
+ output_a->scale_fractional :
+ wl_fixed_from_int(output_a->scale);
+ const wl_fixed_t scale_fractional_b = output_b->has_scale_fractional ?
+ output_b->scale_fractional :
+ wl_fixed_from_int(output_b->scale);
+ if (scale_fractional_a < scale_fractional_b) {
+ return -1;
+ }
+ if (scale_fractional_a > scale_fractional_b) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int outputs_max_scale_or_default(const std::vector<output_t *> &outputs,
+ const int32_t scale_default,
+ uint32_t *r_dpi)
+{
+ const output_t *output_max = nullptr;
+ for (const output_t *reg_output : outputs) {
+ if (!output_max || (output_scale_cmp(output_max, reg_output) == -1)) {
+ output_max = reg_output;
+ }
+ }
+
+ if (output_max) {
+ if (r_dpi) {
+ *r_dpi = output_max->has_scale_fractional ?
+ /* Fractional DPI. */
+ wl_fixed_to_int(output_max->scale_fractional * base_dpi) :
+ /* Simple non-fractional DPI. */
+ (output_max->scale * base_dpi);
+ }
+ return output_max->scale;
+ }
+
+ if (r_dpi) {
+ *r_dpi = scale_default * base_dpi;
+ }
+ return scale_default;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (XDG Top Level), #xdg_toplevel_listener
* \{ */
-static void toplevel_configure(
+static void xdg_toplevel_handle_configure(
void *data, xdg_toplevel * /*xdg_toplevel*/, int32_t width, int32_t height, wl_array *states)
{
window_t *win = static_cast<window_t *>(data);
- win->pending_width = width;
- win->pending_height = height;
+ win->size_pending[0] = win->scale * width;
+ win->size_pending[1] = win->scale * height;
win->is_maximised = false;
win->is_fullscreen = false;
@@ -77,17 +153,23 @@ static void toplevel_configure(
}
}
-static void toplevel_close(void *data, xdg_toplevel * /*xdg_toplevel*/)
+static void xdg_toplevel_handle_close(void *data, xdg_toplevel * /*xdg_toplevel*/)
{
static_cast<window_t *>(data)->w->close();
}
static const xdg_toplevel_listener toplevel_listener = {
- toplevel_configure,
- toplevel_close,
+ xdg_toplevel_handle_configure,
+ xdg_toplevel_handle_close,
};
-static void toplevel_decoration_configure(
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (XDG Decoration Listener), #zxdg_toplevel_decoration_v1_listener
+ * \{ */
+
+static void xdg_toplevel_decoration_handle_configure(
void *data,
struct zxdg_toplevel_decoration_v1 * /*zxdg_toplevel_decoration_v1*/,
uint32_t mode)
@@ -96,10 +178,16 @@ static void toplevel_decoration_configure(
}
static const zxdg_toplevel_decoration_v1_listener toplevel_decoration_v1_listener = {
- toplevel_decoration_configure,
+ xdg_toplevel_decoration_handle_configure,
};
-static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t serial)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (XDG Surface Handle Configure), #xdg_surface_listener
+ * \{ */
+
+static void xdg_surface_handle_configure(void *data, xdg_surface *xdg_surface, uint32_t serial)
{
window_t *win = static_cast<window_t *>(data);
@@ -107,12 +195,12 @@ static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t ser
return;
}
- if (win->pending_width != 0 && win->pending_height != 0) {
- win->width = win->scale * win->pending_width;
- win->height = win->scale * win->pending_height;
- wl_egl_window_resize(win->egl_window, win->width, win->height, 0, 0);
- win->pending_width = 0;
- win->pending_height = 0;
+ if (win->size_pending[0] != 0 && win->size_pending[1] != 0) {
+ win->size[0] = win->size_pending[0];
+ win->size[1] = win->size_pending[1];
+ wl_egl_window_resize(win->egl_window, win->size[0], win->size[1], 0, 0);
+ win->size_pending[0] = 0;
+ win->size_pending[1] = 0;
win->w->notify_size();
}
@@ -126,55 +214,49 @@ static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t ser
xdg_surface_ack_configure(xdg_surface, serial);
}
-static const xdg_surface_listener surface_listener = {
- surface_configure,
+static const xdg_surface_listener xdg_surface_listener = {
+ xdg_surface_handle_configure,
};
-static bool update_scale(GHOST_WindowWayland *window)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Surface), #wl_surface_listener
+ * \{ */
+
+static void surface_handle_enter(void *data,
+ struct wl_surface * /*wl_surface*/,
+ struct wl_output *output)
{
- int scale = 0;
- for (const output_t *output : window->outputs_active()) {
- if (output->scale > scale) {
- scale = output->scale;
- }
+ GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data);
+ output_t *reg_output = w->output_find_by_wl(output);
+ if (reg_output == nullptr) {
+ return;
}
- if (scale > 0 && window->scale() != scale) {
- window->scale() = scale;
- /* Using the real DPI will cause wrong scaling of the UI
- * use a multiplier for the default DPI as workaround. */
- window->dpi() = scale * base_dpi;
- wl_surface_set_buffer_scale(window->surface(), scale);
- return true;
+ if (w->outputs_enter(reg_output)) {
+ w->outputs_changed_update_scale();
}
- return false;
}
-static void surface_enter(void *data, struct wl_surface * /*wl_surface*/, struct wl_output *output)
+static void surface_handle_leave(void *data,
+ struct wl_surface * /*wl_surface*/,
+ struct wl_output *output)
{
GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data);
- for (const output_t *reg_output : w->outputs()) {
- if (reg_output->output == output) {
- w->outputs_active().insert(reg_output);
- }
+ output_t *reg_output = w->output_find_by_wl(output);
+ if (reg_output == nullptr) {
+ return;
}
- update_scale(w);
-}
-static void surface_leave(void *data, struct wl_surface * /*wl_surface*/, struct wl_output *output)
-{
- GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data);
- for (const output_t *reg_output : w->outputs()) {
- if (reg_output->output == output) {
- w->outputs_active().erase(reg_output);
- }
+ if (w->outputs_leave(reg_output)) {
+ w->outputs_changed_update_scale();
}
- update_scale(w);
}
struct wl_surface_listener wl_surface_listener = {
- surface_enter,
- surface_leave,
+ surface_handle_enter,
+ surface_handle_leave,
};
/** \} */
@@ -208,32 +290,51 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
{
w->w = this;
- w->width = int32_t(width);
- w->height = int32_t(height);
+ w->size[0] = int32_t(width);
+ w->size[1] = int32_t(height);
w->is_dialog = is_dialog;
+ /* NOTE(@campbellbarton): The scale set here to avoid flickering on startup.
+ * When all monitors use the same scale (which is quite common) there aren't any problems.
+ *
+ * When monitors have different scales there may still be a visible window resize on startup.
+ * Ideally it would be possible to know the scale this window will use however that's only
+ * known once #surface_enter callback runs (which isn't guaranteed to run at all).
+ *
+ * Using the maximum scale is best as it results in the window first being smaller,
+ * avoiding a large window flashing before it's made smaller. */
+ w->scale = outputs_max_scale_or_default(this->m_system->outputs(), 1, &w->dpi);
+
/* Window surfaces. */
- w->surface = wl_compositor_create_surface(m_system->compositor());
- wl_surface_add_listener(w->surface, &wl_surface_listener, this);
+ w->wl_surface = wl_compositor_create_surface(m_system->compositor());
+ wl_surface_set_buffer_scale(this->surface(), w->scale);
+
+ wl_surface_add_listener(w->wl_surface, &wl_surface_listener, this);
- w->egl_window = wl_egl_window_create(w->surface, int(width), int(height));
+ w->egl_window = wl_egl_window_create(w->wl_surface, int(w->size[0]), int(w->size[1]));
- w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->shell(), w->surface);
+ w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->xdg_shell(), w->wl_surface);
w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface);
- if (m_system->decoration_manager()) {
+ /* NOTE: The limit is in points (not pixels) so Hi-DPI will limit to larger number of pixels.
+ * This has the advantage that the size limit is the same when moving the window between monitors
+ * with different scales set. If it was important to limit in pixels it could be re-calculated
+ * when the `w->scale` changed. */
+ xdg_toplevel_set_min_size(w->xdg_toplevel, 320, 240);
+
+ if (m_system->xdg_decoration_manager()) {
w->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
- m_system->decoration_manager(), w->xdg_toplevel);
+ m_system->xdg_decoration_manager(), w->xdg_toplevel);
zxdg_toplevel_decoration_v1_add_listener(
w->xdg_toplevel_decoration, &toplevel_decoration_v1_listener, w);
zxdg_toplevel_decoration_v1_set_mode(w->xdg_toplevel_decoration,
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
}
- wl_surface_set_user_data(w->surface, this);
+ wl_surface_set_user_data(w->wl_surface, this);
- xdg_surface_add_listener(w->xdg_surface, &surface_listener, w);
+ xdg_surface_add_listener(w->xdg_surface, &xdg_surface_listener, w);
xdg_toplevel_add_listener(w->xdg_toplevel, &toplevel_listener, w);
if (parentWindow && is_dialog) {
@@ -242,7 +343,7 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
}
/* Call top-level callbacks. */
- wl_surface_commit(w->surface);
+ wl_surface_commit(w->wl_surface);
wl_display_roundtrip(m_system->display());
#ifdef GHOST_OPENGL_ALPHA
@@ -296,32 +397,92 @@ GHOST_TSuccess GHOST_WindowWayland::notify_size()
wl_surface *GHOST_WindowWayland::surface() const
{
- return w->surface;
+ return w->wl_surface;
}
-const std::vector<output_t *> &GHOST_WindowWayland::outputs() const
+const std::vector<output_t *> &GHOST_WindowWayland::outputs()
{
- return m_system->outputs();
+ return w->outputs;
}
-std::unordered_set<const output_t *> &GHOST_WindowWayland::outputs_active()
+output_t *GHOST_WindowWayland::output_find_by_wl(struct wl_output *output)
{
- return w->outputs;
+ for (output_t *reg_output : this->m_system->outputs()) {
+ if (reg_output->wl_output == output) {
+ return reg_output;
+ }
+ }
+ return nullptr;
}
-uint16_t &GHOST_WindowWayland::dpi()
+bool GHOST_WindowWayland::outputs_changed_update_scale()
+{
+ uint32_t dpi_next;
+ const int scale_next = outputs_max_scale_or_default(this->outputs(), 0, &dpi_next);
+ if (scale_next == 0) {
+ return false;
+ }
+
+ window_t *win = this->w;
+ const uint32_t dpi_curr = win->dpi;
+ const int scale_curr = win->scale;
+ bool changed = false;
+
+ if (scale_next != scale_curr) {
+ /* Unlikely but possible there is a pending size change is set. */
+ win->size_pending[0] = (win->size_pending[0] / scale_curr) * scale_next;
+ win->size_pending[1] = (win->size_pending[1] / scale_curr) * scale_next;
+
+ win->scale = scale_next;
+ wl_surface_set_buffer_scale(this->surface(), scale_next);
+ changed = true;
+ }
+
+ if (dpi_next != dpi_curr) {
+ /* Using the real DPI will cause wrong scaling of the UI
+ * use a multiplier for the default DPI as workaround. */
+ win->dpi = dpi_next;
+ changed = true;
+ }
+
+ return changed;
+}
+
+bool GHOST_WindowWayland::outputs_enter(output_t *reg_output)
+{
+ std::vector<output_t *> &outputs = w->outputs;
+ auto it = std::find(outputs.begin(), outputs.end(), reg_output);
+ if (it != outputs.end()) {
+ return false;
+ }
+ outputs.push_back(reg_output);
+ return true;
+}
+
+bool GHOST_WindowWayland::outputs_leave(output_t *reg_output)
+{
+ std::vector<output_t *> &outputs = w->outputs;
+ auto it = std::find(outputs.begin(), outputs.end(), reg_output);
+ if (it == outputs.end()) {
+ return false;
+ }
+ outputs.erase(it);
+ return true;
+}
+
+uint16_t GHOST_WindowWayland::dpi()
{
return w->dpi;
}
-int &GHOST_WindowWayland::scale()
+int GHOST_WindowWayland::scale()
{
return w->scale;
}
GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
{
- return m_system->setCursorGrab(mode, m_cursorGrab, w->surface);
+ return m_system->setCursorGrab(mode, m_cursorGrab, w->wl_surface);
}
GHOST_TSuccess GHOST_WindowWayland::setWindowCursorShape(GHOST_TStandardCursor shape)
@@ -356,22 +517,32 @@ void GHOST_WindowWayland::getWindowBounds(GHOST_Rect &bounds) const
void GHOST_WindowWayland::getClientBounds(GHOST_Rect &bounds) const
{
- bounds.set(0, 0, w->width, w->height);
+ bounds.set(0, 0, w->size[0], w->size[1]);
}
GHOST_TSuccess GHOST_WindowWayland::setClientWidth(uint32_t width)
{
- return setClientSize(width, uint32_t(w->height));
+ return setClientSize(width, uint32_t(w->size[1]));
}
GHOST_TSuccess GHOST_WindowWayland::setClientHeight(uint32_t height)
{
- return setClientSize(uint32_t(w->width), height);
+ return setClientSize(uint32_t(w->size[0]), height);
}
GHOST_TSuccess GHOST_WindowWayland::setClientSize(uint32_t width, uint32_t height)
{
wl_egl_window_resize(w->egl_window, int(width), int(height), 0, 0);
+
+ /* Override any pending size that may be set. */
+ w->size_pending[0] = 0;
+ w->size_pending[1] = 0;
+
+ w->size[0] = width;
+ w->size[1] = height;
+
+ notify_size();
+
return GHOST_kSuccess;
}
@@ -403,7 +574,7 @@ GHOST_WindowWayland::~GHOST_WindowWayland()
}
xdg_toplevel_destroy(w->xdg_toplevel);
xdg_surface_destroy(w->xdg_surface);
- wl_surface_destroy(w->surface);
+ wl_surface_destroy(w->wl_surface);
delete w;
}
@@ -494,7 +665,7 @@ void GHOST_WindowWayland::setOpaque() const
/* Make the window opaque. */
region = wl_compositor_create_region(m_system->compositor());
- wl_region_add(region, 0, 0, w->width, w->height);
+ wl_region_add(region, 0, 0, w->size[0], w->size[1]);
wl_surface_set_opaque_region(w->surface, region);
wl_region_destroy(region);
}
diff --git a/intern/ghost/intern/GHOST_WindowWayland.h b/intern/ghost/intern/GHOST_WindowWayland.h
index d5dd123014b..b6d9fa04079 100644
--- a/intern/ghost/intern/GHOST_WindowWayland.h
+++ b/intern/ghost/intern/GHOST_WindowWayland.h
@@ -17,7 +17,6 @@ class GHOST_SystemWayland;
struct output_t;
struct window_t;
-struct wl_surface;
class GHOST_WindowWayland : public GHOST_Window {
public:
@@ -40,25 +39,8 @@ class GHOST_WindowWayland : public GHOST_Window {
uint16_t getDPIHint() override;
- GHOST_TSuccess close();
-
- GHOST_TSuccess activate();
-
- GHOST_TSuccess deactivate();
-
- GHOST_TSuccess notify_size();
+ /* Ghost API */
- wl_surface *surface() const;
-
- const std::vector<output_t *> &outputs() const;
-
- std::unordered_set<const output_t *> &outputs_active();
-
- uint16_t &dpi();
-
- int &scale();
-
- protected:
GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode) override;
GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor shape) override;
@@ -109,6 +91,30 @@ class GHOST_WindowWayland : public GHOST_Window {
void setOpaque() const;
#endif
+ /* WAYLAND utility functions. */
+
+ GHOST_TSuccess close();
+
+ GHOST_TSuccess activate();
+
+ GHOST_TSuccess deactivate();
+
+ GHOST_TSuccess notify_size();
+
+ struct wl_surface *surface() const;
+
+ output_t *output_find_by_wl(struct wl_output *output);
+
+ const std::vector<output_t *> &outputs();
+
+ bool outputs_enter(output_t *reg_output);
+ bool outputs_leave(output_t *reg_output);
+ bool outputs_changed_update_scale();
+
+ uint16_t dpi();
+
+ int scale();
+
private:
GHOST_SystemWayland *m_system;
struct window_t *w;
diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp
index 3569c9574f1..ac7a476c76f 100644
--- a/intern/ghost/intern/GHOST_WindowX11.cpp
+++ b/intern/ghost/intern/GHOST_WindowX11.cpp
@@ -1711,7 +1711,7 @@ uint16_t GHOST_WindowX11::getDPIHint()
XrmDestroyDatabase(xrdb);
}
- /* Fallback to calculating DPI using X reported DPI, set using xrandr --dpi */
+ /* Fallback to calculating DPI using X reported DPI, set using `xrandr --dpi`. */
XWindowAttributes attr;
if (!XGetWindowAttributes(m_display, m_window, &attr)) {
/* Failed to get window attributes, return X11 default DPI */
diff --git a/intern/ghost/intern/GHOST_Wintab.cpp b/intern/ghost/intern/GHOST_Wintab.cpp
index 974a07db9c3..b136acbe098 100644
--- a/intern/ghost/intern/GHOST_Wintab.cpp
+++ b/intern/ghost/intern/GHOST_Wintab.cpp
@@ -310,7 +310,7 @@ void GHOST_Wintab::getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo)
outWintabInfo.reserve(numPackets);
for (int i = 0; i < numPackets; i++) {
- PACKET pkt = m_pkts[i];
+ const PACKET pkt = m_pkts[i];
GHOST_WintabInfoWin32 out;
/* % 3 for multiple devices ("DualTrack"). */
@@ -367,11 +367,12 @@ void GHOST_Wintab::getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo)
/* Some Wintab libraries don't handle relative button input, so we track button presses
* manually. */
DWORD buttonsChanged = m_buttons ^ pkt.pkButtons;
- WORD buttonIndex = 0;
+ /* We only needed the prior button state to compare to current, so we can overwrite it now. */
+ m_buttons = pkt.pkButtons;
- while (buttonsChanged) {
+ /* Iterate over button flag indices until all flags are clear. */
+ for (WORD buttonIndex = 0; buttonsChanged; buttonIndex++, buttonsChanged >>= 1) {
if (buttonsChanged & 1) {
- /* Find the index for the changed button from the button map. */
GHOST_TButtonMask button = mapWintabToGhostButton(pkt.pkCursor, buttonIndex);
if (button != GHOST_kButtonMaskNone) {
@@ -381,15 +382,11 @@ void GHOST_Wintab::getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo)
}
out.button = button;
- out.type = buttonsChanged & pkt.pkButtons ? GHOST_kEventButtonDown :
- GHOST_kEventButtonUp;
- }
- m_buttons ^= 1 << buttonIndex;
+ DWORD buttonFlag = 1 << buttonIndex;
+ out.type = pkt.pkButtons & buttonFlag ? GHOST_kEventButtonDown : GHOST_kEventButtonUp;
+ }
}
-
- buttonsChanged >>= 1;
- buttonIndex++;
}
outWintabInfo.push_back(out);
diff --git a/intern/ghost/intern/GHOST_Wintab.h b/intern/ghost/intern/GHOST_Wintab.h
index 86a0143ecc0..80eacf1f3fa 100644
--- a/intern/ghost/intern/GHOST_Wintab.h
+++ b/intern/ghost/intern/GHOST_Wintab.h
@@ -187,7 +187,7 @@ class GHOST_Wintab {
bool m_focused = false;
/** Pressed button map. */
- uint8_t m_buttons = 0;
+ DWORD m_buttons = 0;
/** Range of a coordinate space. */
struct Range {