1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 */
16
17 /** \file
18 * \ingroup GHOST
19 */
20
21 #include "GHOST_SystemWayland.h"
22 #include "GHOST_Event.h"
23 #include "GHOST_EventButton.h"
24 #include "GHOST_EventCursor.h"
25 #include "GHOST_EventDragnDrop.h"
26 #include "GHOST_EventKey.h"
27 #include "GHOST_EventWheel.h"
28 #include "GHOST_TimerManager.h"
29 #include "GHOST_WindowManager.h"
30
31 #include "GHOST_ContextEGL.h"
32
33 #include <EGL/egl.h>
34 #include <wayland-egl.h>
35
36 #include <algorithm>
37 #include <atomic>
38 #include <stdexcept>
39 #include <thread>
40 #include <unordered_map>
41 #include <unordered_set>
42
43 #include <pointer-constraints-client-protocol.h>
44 #include <relative-pointer-client-protocol.h>
45 #include <wayland-cursor.h>
46 #include <xkbcommon/xkbcommon.h>
47
48 #include <fcntl.h>
49 #include <linux/input-event-codes.h>
50 #include <sys/mman.h>
51 #include <unistd.h>
52
53 #include <cstring>
54
55 struct output_t {
56 struct wl_output *output;
57 int32_t width, height;
58 int transform;
59 int scale;
60 std::string make;
61 std::string model;
62 };
63
64 struct buffer_t {
65 void *data;
66 size_t size;
67 };
68
69 struct cursor_t {
70 bool visible;
71 struct wl_surface *surface = nullptr;
72 struct wl_buffer *buffer;
73 struct wl_cursor_image image;
74 struct buffer_t *file_buffer = nullptr;
75 };
76
77 struct data_offer_t {
78 std::unordered_set<std::string> types;
79 uint32_t source_actions;
80 uint32_t dnd_action;
81 struct wl_data_offer *id;
82 std::atomic<bool> in_use;
83 struct {
84 int x, y;
85 } dnd;
86 };
87
88 struct data_source_t {
89 struct wl_data_source *data_source;
90 /** Last device that was active. */
91 uint32_t source_serial;
92 char *buffer_out;
93 };
94
95 struct key_repeat_payload_t {
96 GHOST_SystemWayland *system;
97 GHOST_IWindow *window;
98 GHOST_TKey key;
99 GHOST_TEventKeyData key_data;
100 };
101
102 struct input_t {
103 GHOST_SystemWayland *system;
104
105 std::string name;
106 struct wl_seat *seat;
107 struct wl_pointer *pointer = nullptr;
108 struct wl_keyboard *keyboard = nullptr;
109
110 uint32_t pointer_serial;
111 int x, y;
112 GHOST_Buttons buttons;
113 struct cursor_t cursor;
114
115 struct zwp_relative_pointer_v1 *relative_pointer;
116 struct zwp_locked_pointer_v1 *locked_pointer;
117
118 struct xkb_context *xkb_context;
119 struct xkb_state *xkb_state;
120 struct {
121 /* Key repetition in character per second. */
122 int32_t rate;
123 /* Time (milliseconds) after which to start repeating keys. */
124 int32_t delay;
125 /* Timer for key repeats. */
126 GHOST_ITimerTask *timer = nullptr;
127 } key_repeat;
128
129 struct wl_surface *focus_pointer = nullptr;
130 struct wl_surface *focus_keyboard = nullptr;
131
132 struct wl_data_device *data_device = nullptr;
133 struct data_offer_t *data_offer_dnd; /* Drag & Drop. */
134 struct data_offer_t *data_offer_copy_paste; /* Copy & Paste. */
135
136 struct data_source_t *data_source;
137 };
138
139 struct display_t {
140 GHOST_SystemWayland *system;
141
142 struct wl_display *display;
143 struct wl_compositor *compositor = nullptr;
144 struct xdg_wm_base *xdg_shell = nullptr;
145 struct wl_shm *shm = nullptr;
146 std::vector<output_t *> outputs;
147 std::vector<input_t *> inputs;
148 struct wl_cursor_theme *cursor_theme = nullptr;
149 struct wl_data_device_manager *data_device_manager = nullptr;
150 struct zwp_relative_pointer_manager_v1 *relative_pointer_manager = nullptr;
151 struct zwp_pointer_constraints_v1 *pointer_constraints = nullptr;
152
153 std::vector<struct wl_surface *> os_surfaces;
154 std::vector<struct wl_egl_window *> os_egl_windows;
155 };
156
display_destroy(display_t * d)157 static void display_destroy(display_t *d)
158 {
159 if (d->data_device_manager) {
160 wl_data_device_manager_destroy(d->data_device_manager);
161 }
162
163 for (output_t *output : d->outputs) {
164 wl_output_destroy(output->output);
165 delete output;
166 }
167
168 for (input_t *input : d->inputs) {
169 if (input->data_source) {
170 free(input->data_source->buffer_out);
171 if (input->data_source->data_source) {
172 wl_data_source_destroy(input->data_source->data_source);
173 }
174 delete input->data_source;
175 }
176 if (input->data_offer_copy_paste) {
177 wl_data_offer_destroy(input->data_offer_copy_paste->id);
178 delete input->data_offer_copy_paste;
179 }
180 if (input->data_device) {
181 wl_data_device_release(input->data_device);
182 }
183 if (input->pointer) {
184 if (input->cursor.file_buffer) {
185 munmap(input->cursor.file_buffer->data, input->cursor.file_buffer->size);
186 delete input->cursor.file_buffer;
187 }
188 if (input->cursor.surface) {
189 wl_surface_destroy(input->cursor.surface);
190 }
191 if (input->pointer) {
192 wl_pointer_destroy(input->pointer);
193 }
194 }
195 if (input->keyboard) {
196 if (input->key_repeat.timer) {
197 delete static_cast<key_repeat_payload_t *>(input->key_repeat.timer->getUserData());
198 input->system->removeTimer(input->key_repeat.timer);
199 input->key_repeat.timer = nullptr;
200 }
201 wl_keyboard_destroy(input->keyboard);
202 }
203 if (input->xkb_state) {
204 xkb_state_unref(input->xkb_state);
205 }
206 if (input->xkb_context) {
207 xkb_context_unref(input->xkb_context);
208 }
209 wl_seat_destroy(input->seat);
210 delete input;
211 }
212
213 if (d->cursor_theme) {
214 wl_cursor_theme_destroy(d->cursor_theme);
215 }
216
217 if (d->shm) {
218 wl_shm_destroy(d->shm);
219 }
220
221 if (d->relative_pointer_manager) {
222 zwp_relative_pointer_manager_v1_destroy(d->relative_pointer_manager);
223 }
224
225 if (d->pointer_constraints) {
226 zwp_pointer_constraints_v1_destroy(d->pointer_constraints);
227 }
228
229 for (wl_egl_window *os_egl_window : d->os_egl_windows) {
230 wl_egl_window_destroy(os_egl_window);
231 }
232
233 for (wl_surface *os_surface : d->os_surfaces) {
234 wl_surface_destroy(os_surface);
235 }
236
237 if (d->compositor) {
238 wl_compositor_destroy(d->compositor);
239 }
240
241 if (d->xdg_shell) {
242 xdg_wm_base_destroy(d->xdg_shell);
243 }
244
245 if (eglGetDisplay) {
246 ::eglTerminate(eglGetDisplay(EGLNativeDisplayType(d->display)));
247 }
248
249 if (d->display) {
250 wl_display_disconnect(d->display);
251 }
252
253 delete d;
254 }
255
xkb_map_gkey(const xkb_keysym_t & sym)256 static GHOST_TKey xkb_map_gkey(const xkb_keysym_t &sym)
257 {
258
259 GHOST_TKey gkey;
260 if (sym >= XKB_KEY_0 && sym <= XKB_KEY_9) {
261 gkey = GHOST_TKey(sym);
262 }
263 else if (sym >= XKB_KEY_KP_0 && sym <= XKB_KEY_KP_9) {
264 gkey = GHOST_TKey(GHOST_kKeyNumpad0 + sym - XKB_KEY_KP_0);
265 }
266 else if (sym >= XKB_KEY_A && sym <= XKB_KEY_Z) {
267 gkey = GHOST_TKey(sym);
268 }
269 else if (sym >= XKB_KEY_a && sym <= XKB_KEY_z) {
270 gkey = GHOST_TKey(sym - XKB_KEY_a + XKB_KEY_A);
271 }
272 else if (sym >= XKB_KEY_F1 && sym <= XKB_KEY_F24) {
273 gkey = GHOST_TKey(GHOST_kKeyF1 + sym - XKB_KEY_F1);
274 }
275 else {
276
277 #define GXMAP(k, x, y) \
278 case x: \
279 k = y; \
280 break
281
282 switch (sym) {
283 GXMAP(gkey, XKB_KEY_BackSpace, GHOST_kKeyBackSpace);
284 GXMAP(gkey, XKB_KEY_Tab, GHOST_kKeyTab);
285 GXMAP(gkey, XKB_KEY_Linefeed, GHOST_kKeyLinefeed);
286 GXMAP(gkey, XKB_KEY_Clear, GHOST_kKeyClear);
287 GXMAP(gkey, XKB_KEY_Return, GHOST_kKeyEnter);
288
289 GXMAP(gkey, XKB_KEY_Escape, GHOST_kKeyEsc);
290 GXMAP(gkey, XKB_KEY_space, GHOST_kKeySpace);
291 GXMAP(gkey, XKB_KEY_apostrophe, GHOST_kKeyQuote);
292 GXMAP(gkey, XKB_KEY_comma, GHOST_kKeyComma);
293 GXMAP(gkey, XKB_KEY_minus, GHOST_kKeyMinus);
294 GXMAP(gkey, XKB_KEY_plus, GHOST_kKeyPlus);
295 GXMAP(gkey, XKB_KEY_period, GHOST_kKeyPeriod);
296 GXMAP(gkey, XKB_KEY_slash, GHOST_kKeySlash);
297
298 GXMAP(gkey, XKB_KEY_semicolon, GHOST_kKeySemicolon);
299 GXMAP(gkey, XKB_KEY_equal, GHOST_kKeyEqual);
300
301 GXMAP(gkey, XKB_KEY_bracketleft, GHOST_kKeyLeftBracket);
302 GXMAP(gkey, XKB_KEY_bracketright, GHOST_kKeyRightBracket);
303 GXMAP(gkey, XKB_KEY_backslash, GHOST_kKeyBackslash);
304 GXMAP(gkey, XKB_KEY_grave, GHOST_kKeyAccentGrave);
305
306 GXMAP(gkey, XKB_KEY_Shift_L, GHOST_kKeyLeftShift);
307 GXMAP(gkey, XKB_KEY_Shift_R, GHOST_kKeyRightShift);
308 GXMAP(gkey, XKB_KEY_Control_L, GHOST_kKeyLeftControl);
309 GXMAP(gkey, XKB_KEY_Control_R, GHOST_kKeyRightControl);
310 GXMAP(gkey, XKB_KEY_Alt_L, GHOST_kKeyLeftAlt);
311 GXMAP(gkey, XKB_KEY_Alt_R, GHOST_kKeyRightAlt);
312 GXMAP(gkey, XKB_KEY_Super_L, GHOST_kKeyOS);
313 GXMAP(gkey, XKB_KEY_Super_R, GHOST_kKeyOS);
314 GXMAP(gkey, XKB_KEY_Menu, GHOST_kKeyApp);
315
316 GXMAP(gkey, XKB_KEY_Caps_Lock, GHOST_kKeyCapsLock);
317 GXMAP(gkey, XKB_KEY_Num_Lock, GHOST_kKeyNumLock);
318 GXMAP(gkey, XKB_KEY_Scroll_Lock, GHOST_kKeyScrollLock);
319
320 GXMAP(gkey, XKB_KEY_Left, GHOST_kKeyLeftArrow);
321 GXMAP(gkey, XKB_KEY_Right, GHOST_kKeyRightArrow);
322 GXMAP(gkey, XKB_KEY_Up, GHOST_kKeyUpArrow);
323 GXMAP(gkey, XKB_KEY_Down, GHOST_kKeyDownArrow);
324
325 GXMAP(gkey, XKB_KEY_Print, GHOST_kKeyPrintScreen);
326 GXMAP(gkey, XKB_KEY_Pause, GHOST_kKeyPause);
327
328 GXMAP(gkey, XKB_KEY_Insert, GHOST_kKeyInsert);
329 GXMAP(gkey, XKB_KEY_Delete, GHOST_kKeyDelete);
330 GXMAP(gkey, XKB_KEY_Home, GHOST_kKeyHome);
331 GXMAP(gkey, XKB_KEY_End, GHOST_kKeyEnd);
332 GXMAP(gkey, XKB_KEY_Page_Up, GHOST_kKeyUpPage);
333 GXMAP(gkey, XKB_KEY_Page_Down, GHOST_kKeyDownPage);
334
335 GXMAP(gkey, XKB_KEY_KP_Decimal, GHOST_kKeyNumpadPeriod);
336 GXMAP(gkey, XKB_KEY_KP_Enter, GHOST_kKeyNumpadEnter);
337 GXMAP(gkey, XKB_KEY_KP_Add, GHOST_kKeyNumpadPlus);
338 GXMAP(gkey, XKB_KEY_KP_Subtract, GHOST_kKeyNumpadMinus);
339 GXMAP(gkey, XKB_KEY_KP_Multiply, GHOST_kKeyNumpadAsterisk);
340 GXMAP(gkey, XKB_KEY_KP_Divide, GHOST_kKeyNumpadSlash);
341
342 GXMAP(gkey, XKB_KEY_XF86AudioPlay, GHOST_kKeyMediaPlay);
343 GXMAP(gkey, XKB_KEY_XF86AudioStop, GHOST_kKeyMediaStop);
344 GXMAP(gkey, XKB_KEY_XF86AudioPrev, GHOST_kKeyMediaFirst);
345 GXMAP(gkey, XKB_KEY_XF86AudioNext, GHOST_kKeyMediaLast);
346 default:
347 GHOST_PRINT("unhandled key: " << std::hex << std::showbase << sym << std::dec << " ("
348 << sym << ")" << std::endl);
349 gkey = GHOST_kKeyUnknown;
350 }
351 #undef GXMAP
352 }
353
354 return gkey;
355 }
356
357 static const int default_cursor_size = 24;
358
359 static const std::unordered_map<GHOST_TStandardCursor, std::string> cursors = {
360 {GHOST_kStandardCursorDefault, "left_ptr"},
361 {GHOST_kStandardCursorRightArrow, "right_ptr"},
362 {GHOST_kStandardCursorLeftArrow, "left_ptr"},
363 {GHOST_kStandardCursorInfo, ""},
364 {GHOST_kStandardCursorDestroy, ""},
365 {GHOST_kStandardCursorHelp, "question_arrow"},
366 {GHOST_kStandardCursorWait, "watch"},
367 {GHOST_kStandardCursorText, "xterm"},
368 {GHOST_kStandardCursorCrosshair, "crosshair"},
369 {GHOST_kStandardCursorCrosshairA, ""},
370 {GHOST_kStandardCursorCrosshairB, ""},
371 {GHOST_kStandardCursorCrosshairC, ""},
372 {GHOST_kStandardCursorPencil, ""},
373 {GHOST_kStandardCursorUpArrow, "sb_up_arrow"},
374 {GHOST_kStandardCursorDownArrow, "sb_down_arrow"},
375 {GHOST_kStandardCursorVerticalSplit, ""},
376 {GHOST_kStandardCursorHorizontalSplit, ""},
377 {GHOST_kStandardCursorEraser, ""},
378 {GHOST_kStandardCursorKnife, ""},
379 {GHOST_kStandardCursorEyedropper, ""},
380 {GHOST_kStandardCursorZoomIn, ""},
381 {GHOST_kStandardCursorZoomOut, ""},
382 {GHOST_kStandardCursorMove, "move"},
383 {GHOST_kStandardCursorNSEWScroll, ""},
384 {GHOST_kStandardCursorNSScroll, ""},
385 {GHOST_kStandardCursorEWScroll, ""},
386 {GHOST_kStandardCursorStop, ""},
387 {GHOST_kStandardCursorUpDown, "sb_v_double_arrow"},
388 {GHOST_kStandardCursorLeftRight, "sb_h_double_arrow"},
389 {GHOST_kStandardCursorTopSide, "top_side"},
390 {GHOST_kStandardCursorBottomSide, "bottom_side"},
391 {GHOST_kStandardCursorLeftSide, "left_side"},
392 {GHOST_kStandardCursorRightSide, "right_side"},
393 {GHOST_kStandardCursorTopLeftCorner, "top_left_corner"},
394 {GHOST_kStandardCursorTopRightCorner, "top_right_corner"},
395 {GHOST_kStandardCursorBottomRightCorner, "bottom_right_corner"},
396 {GHOST_kStandardCursorBottomLeftCorner, "bottom_left_corner"},
397 {GHOST_kStandardCursorCopy, "copy"},
398 };
399
400 static constexpr const char *mime_text_plain = "text/plain";
401 static constexpr const char *mime_text_utf8 = "text/plain;charset=utf-8";
402 static constexpr const char *mime_text_uri = "text/uri-list";
403
404 static const std::unordered_map<std::string, GHOST_TDragnDropTypes> mime_dnd = {
405 {mime_text_plain, GHOST_kDragnDropTypeString},
406 {mime_text_utf8, GHOST_kDragnDropTypeString},
407 {mime_text_uri, GHOST_kDragnDropTypeFilenames},
408 };
409
410 static const std::vector<std::string> mime_preference_order = {
411 mime_text_uri,
412 mime_text_utf8,
413 mime_text_plain,
414 };
415
416 static const std::vector<std::string> mime_send = {
417 "UTF8_STRING",
418 "COMPOUND_TEXT",
419 "TEXT",
420 "STRING",
421 "text/plain;charset=utf-8",
422 "text/plain",
423 };
424
425 /* -------------------------------------------------------------------- */
426 /** \name Interface Callbacks
427 *
428 * These callbacks are registered for Wayland interfaces and called when
429 * an event is received from the compositor.
430 * \{ */
431
relative_pointer_relative_motion(void * data,struct zwp_relative_pointer_v1 *,uint32_t,uint32_t,wl_fixed_t dx,wl_fixed_t dy,wl_fixed_t,wl_fixed_t)432 static void relative_pointer_relative_motion(
433 void *data,
434 struct zwp_relative_pointer_v1 * /*zwp_relative_pointer_v1*/,
435 uint32_t /*utime_hi*/,
436 uint32_t /*utime_lo*/,
437 wl_fixed_t dx,
438 wl_fixed_t dy,
439 wl_fixed_t /*dx_unaccel*/,
440 wl_fixed_t /*dy_unaccel*/)
441 {
442 input_t *input = static_cast<input_t *>(data);
443
444 input->x += wl_fixed_to_int(dx);
445 input->y += wl_fixed_to_int(dy);
446
447 GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>(
448 wl_surface_get_user_data(input->focus_pointer));
449
450 input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
451 GHOST_kEventCursorMove,
452 win,
453 input->x,
454 input->y,
455 GHOST_TABLET_DATA_NONE));
456 }
457
458 static const zwp_relative_pointer_v1_listener relative_pointer_listener = {
459 relative_pointer_relative_motion,
460 };
461
dnd_events(const input_t * const input,const GHOST_TEventType event)462 static void dnd_events(const input_t *const input, const GHOST_TEventType event)
463 {
464 const GHOST_TUns64 time = input->system->getMilliSeconds();
465 GHOST_IWindow *const window = static_cast<GHOST_WindowWayland *>(
466 wl_surface_get_user_data(input->focus_pointer));
467 for (const std::string &type : mime_preference_order) {
468 input->system->pushEvent(new GHOST_EventDragnDrop(time,
469 event,
470 mime_dnd.at(type),
471 window,
472 input->data_offer_dnd->dnd.x,
473 input->data_offer_dnd->dnd.y,
474 nullptr));
475 }
476 }
477
read_pipe(data_offer_t * data_offer,const std::string mime_receive)478 static std::string read_pipe(data_offer_t *data_offer, const std::string mime_receive)
479 {
480 int pipefd[2];
481 pipe(pipefd);
482 wl_data_offer_receive(data_offer->id, mime_receive.c_str(), pipefd[1]);
483 close(pipefd[1]);
484
485 std::string data;
486 ssize_t len;
487 char buffer[4096];
488 while ((len = read(pipefd[0], buffer, sizeof(buffer))) > 0) {
489 data.insert(data.end(), buffer, buffer + len);
490 }
491 close(pipefd[0]);
492 data_offer->in_use.store(false);
493
494 return data;
495 }
496
497 /**
498 * A target accepts an offered mime type.
499 *
500 * Sent when a target accepts pointer_focus or motion events. If
501 * a target does not accept any of the offered types, type is NULL.
502 */
data_source_target(void *,struct wl_data_source *,const char *)503 static void data_source_target(void * /*data*/,
504 struct wl_data_source * /*wl_data_source*/,
505 const char * /*mime_type*/)
506 {
507 /* pass */
508 }
509
data_source_send(void * data,struct wl_data_source *,const char *,int32_t fd)510 static void data_source_send(void *data,
511 struct wl_data_source * /*wl_data_source*/,
512 const char * /*mime_type*/,
513 int32_t fd)
514 {
515 const char *const buffer = static_cast<char *>(data);
516 write(fd, buffer, strlen(buffer) + 1);
517 close(fd);
518 }
519
data_source_cancelled(void *,struct wl_data_source * wl_data_source)520 static void data_source_cancelled(void * /*data*/, struct wl_data_source *wl_data_source)
521 {
522 wl_data_source_destroy(wl_data_source);
523 }
524
525 /**
526 * The drag-and-drop operation physically finished.
527 *
528 * The user performed the drop action. This event does not
529 * indicate acceptance, #wl_data_source.cancelled may still be
530 * emitted afterwards if the drop destination does not accept any mime type.
531 */
data_source_dnd_drop_performed(void *,struct wl_data_source *)532 static void data_source_dnd_drop_performed(void * /*data*/,
533 struct wl_data_source * /*wl_data_source*/)
534 {
535 /* pass */
536 }
537
538 /**
539 * The drag-and-drop operation concluded.
540 *
541 * The drop destination finished interoperating with this data
542 * source, so the client is now free to destroy this data source
543 * and free all associated data.
544 */
data_source_dnd_finished(void *,struct wl_data_source *)545 static void data_source_dnd_finished(void * /*data*/, struct wl_data_source * /*wl_data_source*/)
546 {
547 /* pass */
548 }
549
550 /**
551 * Notify the selected action.
552 *
553 * This event indicates the action selected by the compositor
554 * after matching the source/destination side actions. Only one
555 * action (or none) will be offered here.
556 */
data_source_action(void *,struct wl_data_source *,uint32_t)557 static void data_source_action(void * /*data*/,
558 struct wl_data_source * /*wl_data_source*/,
559 uint32_t /*dnd_action*/)
560 {
561 /* pass */
562 }
563
564 static const struct wl_data_source_listener data_source_listener = {
565 data_source_target,
566 data_source_send,
567 data_source_cancelled,
568 data_source_dnd_drop_performed,
569 data_source_dnd_finished,
570 data_source_action,
571 };
572
data_offer_offer(void * data,struct wl_data_offer *,const char * mime_type)573 static void data_offer_offer(void *data,
574 struct wl_data_offer * /*wl_data_offer*/,
575 const char *mime_type)
576 {
577 static_cast<data_offer_t *>(data)->types.insert(mime_type);
578 }
579
data_offer_source_actions(void * data,struct wl_data_offer *,uint32_t source_actions)580 static void data_offer_source_actions(void *data,
581 struct wl_data_offer * /*wl_data_offer*/,
582 uint32_t source_actions)
583 {
584 static_cast<data_offer_t *>(data)->source_actions = source_actions;
585 }
586
data_offer_action(void * data,struct wl_data_offer *,uint32_t dnd_action)587 static void data_offer_action(void *data,
588 struct wl_data_offer * /*wl_data_offer*/,
589 uint32_t dnd_action)
590 {
591 static_cast<data_offer_t *>(data)->dnd_action = dnd_action;
592 }
593
594 static const struct wl_data_offer_listener data_offer_listener = {
595 data_offer_offer,
596 data_offer_source_actions,
597 data_offer_action,
598 };
599
data_device_data_offer(void *,struct wl_data_device *,struct wl_data_offer * id)600 static void data_device_data_offer(void * /*data*/,
601 struct wl_data_device * /*wl_data_device*/,
602 struct wl_data_offer *id)
603 {
604 data_offer_t *data_offer = new data_offer_t;
605 data_offer->id = id;
606 wl_data_offer_add_listener(id, &data_offer_listener, data_offer);
607 }
608
data_device_enter(void * data,struct wl_data_device *,uint32_t serial,struct wl_surface *,wl_fixed_t x,wl_fixed_t y,struct wl_data_offer * id)609 static void data_device_enter(void *data,
610 struct wl_data_device * /*wl_data_device*/,
611 uint32_t serial,
612 struct wl_surface * /*surface*/,
613 wl_fixed_t x,
614 wl_fixed_t y,
615 struct wl_data_offer *id)
616 {
617 input_t *input = static_cast<input_t *>(data);
618 input->data_offer_dnd = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id));
619 data_offer_t *data_offer = input->data_offer_dnd;
620
621 data_offer->in_use.store(true);
622 data_offer->dnd.x = wl_fixed_to_int(x);
623 data_offer->dnd.y = wl_fixed_to_int(y);
624
625 wl_data_offer_set_actions(id,
626 WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY |
627 WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE,
628 WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY);
629
630 for (const std::string &type : mime_preference_order) {
631 wl_data_offer_accept(id, serial, type.c_str());
632 }
633
634 dnd_events(input, GHOST_kEventDraggingEntered);
635 }
636
data_device_leave(void * data,struct wl_data_device *)637 static void data_device_leave(void *data, struct wl_data_device * /*wl_data_device*/)
638 {
639 input_t *input = static_cast<input_t *>(data);
640
641 dnd_events(input, GHOST_kEventDraggingExited);
642
643 if (input->data_offer_dnd && !input->data_offer_dnd->in_use.load()) {
644 wl_data_offer_destroy(input->data_offer_dnd->id);
645 delete input->data_offer_dnd;
646 input->data_offer_dnd = nullptr;
647 }
648 }
649
data_device_motion(void * data,struct wl_data_device *,uint32_t,wl_fixed_t x,wl_fixed_t y)650 static void data_device_motion(void *data,
651 struct wl_data_device * /*wl_data_device*/,
652 uint32_t /*time*/,
653 wl_fixed_t x,
654 wl_fixed_t y)
655 {
656 input_t *input = static_cast<input_t *>(data);
657 input->data_offer_dnd->dnd.x = wl_fixed_to_int(x);
658 input->data_offer_dnd->dnd.y = wl_fixed_to_int(y);
659 dnd_events(input, GHOST_kEventDraggingUpdated);
660 }
661
data_device_drop(void * data,struct wl_data_device *)662 static void data_device_drop(void *data, struct wl_data_device * /*wl_data_device*/)
663 {
664 input_t *input = static_cast<input_t *>(data);
665 data_offer_t *data_offer = input->data_offer_dnd;
666
667 const std::string mime_receive = *std::find_first_of(mime_preference_order.begin(),
668 mime_preference_order.end(),
669 data_offer->types.begin(),
670 data_offer->types.end());
671
672 auto read_uris = [](input_t *const input,
673 data_offer_t *data_offer,
674 const std::string mime_receive) {
675 const int x = data_offer->dnd.x;
676 const int y = data_offer->dnd.y;
677
678 const std::string data = read_pipe(data_offer, mime_receive);
679
680 wl_data_offer_finish(data_offer->id);
681 wl_data_offer_destroy(data_offer->id);
682
683 delete data_offer;
684 data_offer = nullptr;
685
686 GHOST_SystemWayland *const system = input->system;
687
688 if (mime_receive == mime_text_uri) {
689 static constexpr const char *file_proto = "file://";
690 static constexpr const char *crlf = "\r\n";
691
692 std::vector<std::string> uris;
693
694 size_t pos = 0;
695 while (true) {
696 pos = data.find(file_proto, pos);
697 const size_t start = pos + sizeof(file_proto) - 1;
698 pos = data.find(crlf, pos);
699 const size_t end = pos;
700
701 if (pos == std::string::npos) {
702 break;
703 }
704 uris.push_back(data.substr(start, end - start));
705 }
706
707 GHOST_TStringArray *flist = static_cast<GHOST_TStringArray *>(
708 malloc(sizeof(GHOST_TStringArray)));
709 flist->count = int(uris.size());
710 flist->strings = static_cast<GHOST_TUns8 **>(malloc(uris.size() * sizeof(GHOST_TUns8 *)));
711 for (size_t i = 0; i < uris.size(); i++) {
712 flist->strings[i] = static_cast<GHOST_TUns8 *>(
713 malloc((uris[i].size() + 1) * sizeof(GHOST_TUns8)));
714 memcpy(flist->strings[i], uris[i].data(), uris[i].size() + 1);
715 }
716 GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>(
717 wl_surface_get_user_data(input->focus_pointer));
718 system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(),
719 GHOST_kEventDraggingDropDone,
720 GHOST_kDragnDropTypeFilenames,
721 win,
722 x,
723 y,
724 flist));
725 }
726 else if (mime_receive == mime_text_plain || mime_receive == mime_text_utf8) {
727 /* TODO: enable use of internal functions 'txt_insert_buf' and
728 * 'text_update_edited' to behave like dropped text was pasted. */
729 }
730 wl_display_roundtrip(system->display());
731 };
732
733 std::thread read_thread(read_uris, input, data_offer, mime_receive);
734 read_thread.detach();
735 }
736
data_device_selection(void * data,struct wl_data_device *,struct wl_data_offer * id)737 static void data_device_selection(void *data,
738 struct wl_data_device * /*wl_data_device*/,
739 struct wl_data_offer *id)
740 {
741 input_t *input = static_cast<input_t *>(data);
742 data_offer_t *data_offer = input->data_offer_copy_paste;
743
744 /* Delete old data offer. */
745 if (data_offer != nullptr) {
746 wl_data_offer_destroy(data_offer->id);
747 delete data_offer;
748 data_offer = nullptr;
749 }
750
751 if (id == nullptr) {
752 return;
753 }
754
755 /* Get new data offer. */
756 data_offer = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id));
757 input->data_offer_copy_paste = data_offer;
758
759 std::string mime_receive;
760 for (const std::string &type : {mime_text_utf8, mime_text_plain}) {
761 if (data_offer->types.count(type)) {
762 mime_receive = type;
763 break;
764 }
765 }
766
767 auto read_selection = [](GHOST_SystemWayland *const system,
768 data_offer_t *data_offer,
769 const std::string mime_receive) {
770 const std::string data = read_pipe(data_offer, mime_receive);
771 system->setSelection(data);
772 };
773
774 std::thread read_thread(read_selection, input->system, data_offer, mime_receive);
775 read_thread.detach();
776 }
777
778 static const struct wl_data_device_listener data_device_listener = {
779 data_device_data_offer,
780 data_device_enter,
781 data_device_leave,
782 data_device_motion,
783 data_device_drop,
784 data_device_selection,
785 };
786
cursor_buffer_release(void * data,struct wl_buffer * wl_buffer)787 static void cursor_buffer_release(void *data, struct wl_buffer *wl_buffer)
788 {
789 cursor_t *cursor = static_cast<cursor_t *>(data);
790
791 wl_buffer_destroy(wl_buffer);
792 cursor->buffer = nullptr;
793 }
794
795 const struct wl_buffer_listener cursor_buffer_listener = {
796 cursor_buffer_release,
797 };
798
pointer_enter(void * data,struct wl_pointer *,uint32_t serial,struct wl_surface * surface,wl_fixed_t surface_x,wl_fixed_t surface_y)799 static void pointer_enter(void *data,
800 struct wl_pointer * /*wl_pointer*/,
801 uint32_t serial,
802 struct wl_surface *surface,
803 wl_fixed_t surface_x,
804 wl_fixed_t surface_y)
805 {
806 if (!surface) {
807 return;
808 }
809 input_t *input = static_cast<input_t *>(data);
810 input->pointer_serial = serial;
811 input->x = wl_fixed_to_int(surface_x);
812 input->y = wl_fixed_to_int(surface_y);
813 input->focus_pointer = surface;
814
815 input->system->pushEvent(
816 new GHOST_EventCursor(input->system->getMilliSeconds(),
817 GHOST_kEventCursorMove,
818 static_cast<GHOST_WindowWayland *>(wl_surface_get_user_data(surface)),
819 input->x,
820 input->y,
821 GHOST_TABLET_DATA_NONE));
822 }
823
pointer_leave(void * data,struct wl_pointer *,uint32_t,struct wl_surface * surface)824 static void pointer_leave(void *data,
825 struct wl_pointer * /*wl_pointer*/,
826 uint32_t /*serial*/,
827 struct wl_surface *surface)
828 {
829 if (surface != nullptr) {
830 static_cast<input_t *>(data)->focus_pointer = nullptr;
831 }
832 }
833
pointer_motion(void * data,struct wl_pointer *,uint32_t,wl_fixed_t surface_x,wl_fixed_t surface_y)834 static void pointer_motion(void *data,
835 struct wl_pointer * /*wl_pointer*/,
836 uint32_t /*time*/,
837 wl_fixed_t surface_x,
838 wl_fixed_t surface_y)
839 {
840 input_t *input = static_cast<input_t *>(data);
841
842 GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>(
843 wl_surface_get_user_data(input->focus_pointer));
844
845 if (!win) {
846 return;
847 }
848
849 input->x = wl_fixed_to_int(surface_x);
850 input->y = wl_fixed_to_int(surface_y);
851
852 input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
853 GHOST_kEventCursorMove,
854 win,
855 wl_fixed_to_int(surface_x),
856 wl_fixed_to_int(surface_y),
857 GHOST_TABLET_DATA_NONE));
858 }
859
pointer_button(void * data,struct wl_pointer *,uint32_t serial,uint32_t,uint32_t button,uint32_t state)860 static void pointer_button(void *data,
861 struct wl_pointer * /*wl_pointer*/,
862 uint32_t serial,
863 uint32_t /*time*/,
864 uint32_t button,
865 uint32_t state)
866 {
867 GHOST_TEventType etype = GHOST_kEventUnknown;
868 switch (state) {
869 case WL_POINTER_BUTTON_STATE_RELEASED:
870 etype = GHOST_kEventButtonUp;
871 break;
872 case WL_POINTER_BUTTON_STATE_PRESSED:
873 etype = GHOST_kEventButtonDown;
874 break;
875 }
876
877 GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft;
878 switch (button) {
879 case BTN_LEFT:
880 ebutton = GHOST_kButtonMaskLeft;
881 break;
882 case BTN_MIDDLE:
883 ebutton = GHOST_kButtonMaskMiddle;
884 break;
885 case BTN_RIGHT:
886 ebutton = GHOST_kButtonMaskRight;
887 break;
888 }
889
890 input_t *input = static_cast<input_t *>(data);
891 GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>(
892 wl_surface_get_user_data(input->focus_pointer));
893 input->data_source->source_serial = serial;
894 input->buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED);
895 input->system->pushEvent(new GHOST_EventButton(
896 input->system->getMilliSeconds(), etype, win, ebutton, GHOST_TABLET_DATA_NONE));
897 }
898
pointer_axis(void * data,struct wl_pointer *,uint32_t,uint32_t axis,wl_fixed_t value)899 static void pointer_axis(void *data,
900 struct wl_pointer * /*wl_pointer*/,
901 uint32_t /*time*/,
902 uint32_t axis,
903 wl_fixed_t value)
904 {
905 if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) {
906 return;
907 }
908 input_t *input = static_cast<input_t *>(data);
909 GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>(
910 wl_surface_get_user_data(input->focus_pointer));
911 input->system->pushEvent(
912 new GHOST_EventWheel(input->system->getMilliSeconds(), win, std::signbit(value) ? +1 : -1));
913 }
914
915 static const struct wl_pointer_listener pointer_listener = {
916 pointer_enter,
917 pointer_leave,
918 pointer_motion,
919 pointer_button,
920 pointer_axis,
921 };
922
keyboard_keymap(void * data,struct wl_keyboard *,uint32_t format,int32_t fd,uint32_t size)923 static void keyboard_keymap(
924 void *data, struct wl_keyboard * /*wl_keyboard*/, uint32_t format, int32_t fd, uint32_t size)
925 {
926 input_t *input = static_cast<input_t *>(data);
927
928 if ((!data) || (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)) {
929 close(fd);
930 return;
931 }
932
933 char *map_str = static_cast<char *>(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0));
934 if (map_str == MAP_FAILED) {
935 close(fd);
936 throw std::runtime_error("keymap mmap failed: " + std::string(std::strerror(errno)));
937 }
938
939 struct xkb_keymap *keymap = xkb_keymap_new_from_string(
940 input->xkb_context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
941 munmap(map_str, size);
942 close(fd);
943
944 if (!keymap) {
945 return;
946 }
947
948 input->xkb_state = xkb_state_new(keymap);
949
950 xkb_keymap_unref(keymap);
951 }
952
953 /**
954 * Enter event.
955 *
956 * Notification that this seat's keyboard focus is on a certain
957 * surface.
958 */
keyboard_enter(void * data,struct wl_keyboard *,uint32_t,struct wl_surface * surface,struct wl_array *)959 static void keyboard_enter(void *data,
960 struct wl_keyboard * /*wl_keyboard*/,
961 uint32_t /*serial*/,
962 struct wl_surface *surface,
963 struct wl_array * /*keys*/)
964 {
965 if (surface != nullptr) {
966 static_cast<input_t *>(data)->focus_keyboard = surface;
967 }
968 }
969
970 /**
971 * Leave event.
972 *
973 * Notification that this seat's keyboard focus is no longer on a
974 * certain surface.
975 */
keyboard_leave(void * data,struct wl_keyboard *,uint32_t,struct wl_surface * surface)976 static void keyboard_leave(void *data,
977 struct wl_keyboard * /*wl_keyboard*/,
978 uint32_t /*serial*/,
979 struct wl_surface *surface)
980 {
981 if (surface != nullptr) {
982 static_cast<input_t *>(data)->focus_keyboard = nullptr;
983 }
984 }
985
986 /**
987 * A version of #xkb_state_key_get_one_sym which returns the key without any modifiers pressed.
988 * Needed because #GHOST_TKey uses these values as key-codes.
989 */
xkb_state_key_get_one_sym_without_modifiers(struct xkb_state * xkb_state,xkb_keycode_t key)990 static xkb_keysym_t xkb_state_key_get_one_sym_without_modifiers(struct xkb_state *xkb_state,
991 xkb_keycode_t key)
992 {
993 /* Use an empty keyboard state to access key symbol without modifiers. */
994 xkb_state_get_keymap(xkb_state);
995 struct xkb_keymap *keymap = xkb_state_get_keymap(xkb_state);
996 struct xkb_state *xkb_state_empty = xkb_state_new(keymap);
997
998 /* Enable number-lock. */
999 {
1000 const xkb_mod_index_t mod2 = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_NUM);
1001 const xkb_mod_index_t num = xkb_keymap_mod_get_index(keymap, "NumLock");
1002 if (num != XKB_MOD_INVALID && mod2 != XKB_MOD_INVALID) {
1003 xkb_state_update_mask(xkb_state_empty, (1 << mod2), 0, (1 << num), 0, 0, 0);
1004 }
1005 }
1006
1007 const xkb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state_empty, key);
1008 xkb_state_unref(xkb_state_empty);
1009 return sym;
1010 }
1011
keyboard_key(void * data,struct wl_keyboard *,uint32_t serial,uint32_t,uint32_t key,uint32_t state)1012 static void keyboard_key(void *data,
1013 struct wl_keyboard * /*wl_keyboard*/,
1014 uint32_t serial,
1015 uint32_t /*time*/,
1016 uint32_t key,
1017 uint32_t state)
1018 {
1019 input_t *input = static_cast<input_t *>(data);
1020
1021 GHOST_TEventType etype = GHOST_kEventUnknown;
1022 switch (state) {
1023 case WL_KEYBOARD_KEY_STATE_RELEASED:
1024 etype = GHOST_kEventKeyUp;
1025 break;
1026 case WL_KEYBOARD_KEY_STATE_PRESSED:
1027 etype = GHOST_kEventKeyDown;
1028 break;
1029 }
1030
1031 const xkb_keysym_t sym = xkb_state_key_get_one_sym_without_modifiers(input->xkb_state, key + 8);
1032
1033 if (sym == XKB_KEY_NoSymbol) {
1034 return;
1035 }
1036 const GHOST_TKey gkey = xkb_map_gkey(sym);
1037
1038 /* Delete previous timer. */
1039 if (xkb_keymap_key_repeats(xkb_state_get_keymap(input->xkb_state), key + 8) &&
1040 input->key_repeat.timer) {
1041 delete static_cast<key_repeat_payload_t *>(input->key_repeat.timer->getUserData());
1042 input->system->removeTimer(input->key_repeat.timer);
1043 input->key_repeat.timer = nullptr;
1044 }
1045
1046 GHOST_TEventKeyData key_data;
1047
1048 if (etype == GHOST_kEventKeyDown) {
1049 xkb_state_key_get_utf8(
1050 input->xkb_state, key + 8, key_data.utf8_buf, sizeof(GHOST_TEventKeyData::utf8_buf));
1051 }
1052 else {
1053 key_data.utf8_buf[0] = '\0';
1054 }
1055
1056 input->data_source->source_serial = serial;
1057
1058 GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>(
1059 wl_surface_get_user_data(input->focus_keyboard));
1060 input->system->pushEvent(new GHOST_EventKey(
1061 input->system->getMilliSeconds(), etype, win, gkey, '\0', key_data.utf8_buf, false));
1062
1063 /* Start timer for repeating key, if applicable. */
1064 if (input->key_repeat.rate > 0 &&
1065 xkb_keymap_key_repeats(xkb_state_get_keymap(input->xkb_state), key + 8) &&
1066 etype == GHOST_kEventKeyDown) {
1067
1068 key_repeat_payload_t *payload = new key_repeat_payload_t({
1069 .system = input->system,
1070 .window = win,
1071 .key = gkey,
1072 .key_data = key_data,
1073 });
1074
1075 auto cb = [](GHOST_ITimerTask *task, GHOST_TUns64 /*time*/) {
1076 struct key_repeat_payload_t *payload = static_cast<key_repeat_payload_t *>(
1077 task->getUserData());
1078 payload->system->pushEvent(new GHOST_EventKey(payload->system->getMilliSeconds(),
1079 GHOST_kEventKeyDown,
1080 payload->window,
1081 payload->key,
1082 '\0',
1083 payload->key_data.utf8_buf,
1084 true));
1085 };
1086 input->key_repeat.timer = input->system->installTimer(
1087 input->key_repeat.delay, 1000 / input->key_repeat.rate, cb, payload);
1088 }
1089 }
1090
keyboard_modifiers(void * data,struct wl_keyboard *,uint32_t,uint32_t mods_depressed,uint32_t mods_latched,uint32_t mods_locked,uint32_t group)1091 static void keyboard_modifiers(void *data,
1092 struct wl_keyboard * /*wl_keyboard*/,
1093 uint32_t /*serial*/,
1094 uint32_t mods_depressed,
1095 uint32_t mods_latched,
1096 uint32_t mods_locked,
1097 uint32_t group)
1098 {
1099 xkb_state_update_mask(static_cast<input_t *>(data)->xkb_state,
1100 mods_depressed,
1101 mods_latched,
1102 mods_locked,
1103 0,
1104 0,
1105 group);
1106 }
1107
keyboard_repeat_info(void * data,struct wl_keyboard *,int32_t rate,int32_t delay)1108 static void keyboard_repeat_info(void *data,
1109 struct wl_keyboard * /*wl_keyboard*/,
1110 int32_t rate,
1111 int32_t delay)
1112 {
1113 input_t *input = static_cast<input_t *>(data);
1114
1115 input->key_repeat.rate = rate;
1116 input->key_repeat.delay = delay;
1117 }
1118
1119 static const struct wl_keyboard_listener keyboard_listener = {
1120 keyboard_keymap,
1121 keyboard_enter,
1122 keyboard_leave,
1123 keyboard_key,
1124 keyboard_modifiers,
1125 keyboard_repeat_info,
1126 };
1127
seat_capabilities(void * data,struct wl_seat * wl_seat,uint32_t capabilities)1128 static void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities)
1129 {
1130 input_t *input = static_cast<input_t *>(data);
1131 input->pointer = nullptr;
1132 input->keyboard = nullptr;
1133
1134 if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
1135 input->pointer = wl_seat_get_pointer(wl_seat);
1136 input->cursor.surface = wl_compositor_create_surface(input->system->compositor());
1137 input->cursor.visible = true;
1138 input->cursor.buffer = nullptr;
1139 input->cursor.file_buffer = new buffer_t;
1140 wl_pointer_add_listener(input->pointer, &pointer_listener, data);
1141 }
1142
1143 if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
1144 input->keyboard = wl_seat_get_keyboard(wl_seat);
1145 wl_keyboard_add_listener(input->keyboard, &keyboard_listener, data);
1146 }
1147 }
1148
seat_name(void * data,struct wl_seat *,const char * name)1149 static void seat_name(void *data, struct wl_seat * /*wl_seat*/, const char *name)
1150 {
1151 static_cast<input_t *>(data)->name = std::string(name);
1152 }
1153
1154 static const struct wl_seat_listener seat_listener = {
1155 seat_capabilities,
1156 seat_name,
1157 };
1158
output_geometry(void * data,struct wl_output *,int32_t,int32_t,int32_t,int32_t,int32_t,const char * make,const char * model,int32_t transform)1159 static void output_geometry(void *data,
1160 struct wl_output * /*wl_output*/,
1161 int32_t /*x*/,
1162 int32_t /*y*/,
1163 int32_t /*physical_width*/,
1164 int32_t /*physical_height*/,
1165 int32_t /*subpixel*/,
1166 const char *make,
1167 const char *model,
1168 int32_t transform)
1169 {
1170 output_t *output = static_cast<output_t *>(data);
1171 output->transform = transform;
1172 output->make = std::string(make);
1173 output->model = std::string(model);
1174 }
1175
output_mode(void * data,struct wl_output *,uint32_t,int32_t width,int32_t height,int32_t)1176 static void output_mode(void *data,
1177 struct wl_output * /*wl_output*/,
1178 uint32_t /*flags*/,
1179 int32_t width,
1180 int32_t height,
1181 int32_t /*refresh*/)
1182 {
1183 output_t *output = static_cast<output_t *>(data);
1184 output->width = width;
1185 output->height = height;
1186 }
1187
1188 /**
1189 * Sent all information about output.
1190 *
1191 * This event is sent after all other properties have been sent
1192 * after binding to the output object and after any other property
1193 * changes done after that. This allows changes to the output
1194 * properties to be seen as atomic, even if they happen via multiple events.
1195 */
output_done(void *,struct wl_output *)1196 static void output_done(void * /*data*/, struct wl_output * /*wl_output*/)
1197 {
1198 }
1199
output_scale(void * data,struct wl_output *,int32_t factor)1200 static void output_scale(void *data, struct wl_output * /*wl_output*/, int32_t factor)
1201 {
1202 static_cast<output_t *>(data)->scale = factor;
1203 }
1204
1205 static const struct wl_output_listener output_listener = {
1206 output_geometry,
1207 output_mode,
1208 output_done,
1209 output_scale,
1210 };
1211
shell_ping(void *,struct xdg_wm_base * xdg_wm_base,uint32_t serial)1212 static void shell_ping(void * /*data*/, struct xdg_wm_base *xdg_wm_base, uint32_t serial)
1213 {
1214 xdg_wm_base_pong(xdg_wm_base, serial);
1215 }
1216
1217 static const struct xdg_wm_base_listener shell_listener = {
1218 shell_ping,
1219 };
1220
global_add(void * data,struct wl_registry * wl_registry,uint32_t name,const char * interface,uint32_t)1221 static void global_add(void *data,
1222 struct wl_registry *wl_registry,
1223 uint32_t name,
1224 const char *interface,
1225 uint32_t /*version*/)
1226 {
1227 struct display_t *display = static_cast<struct display_t *>(data);
1228 if (!strcmp(interface, wl_compositor_interface.name)) {
1229 display->compositor = static_cast<wl_compositor *>(
1230 wl_registry_bind(wl_registry, name, &wl_compositor_interface, 1));
1231 }
1232 else if (!strcmp(interface, xdg_wm_base_interface.name)) {
1233 display->xdg_shell = static_cast<xdg_wm_base *>(
1234 wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 1));
1235 xdg_wm_base_add_listener(display->xdg_shell, &shell_listener, nullptr);
1236 }
1237 else if (!strcmp(interface, wl_output_interface.name)) {
1238 output_t *output = new output_t;
1239 output->scale = 1;
1240 output->output = static_cast<wl_output *>(
1241 wl_registry_bind(wl_registry, name, &wl_output_interface, 2));
1242 display->outputs.push_back(output);
1243 wl_output_add_listener(output->output, &output_listener, output);
1244 }
1245 else if (!strcmp(interface, wl_seat_interface.name)) {
1246 input_t *input = new input_t;
1247 input->system = display->system;
1248 input->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
1249 input->xkb_state = nullptr;
1250 input->data_offer_dnd = nullptr;
1251 input->data_offer_copy_paste = nullptr;
1252 input->data_source = new data_source_t;
1253 input->data_source->data_source = nullptr;
1254 input->data_source->buffer_out = nullptr;
1255 input->relative_pointer = nullptr;
1256 input->locked_pointer = nullptr;
1257 input->seat = static_cast<wl_seat *>(
1258 wl_registry_bind(wl_registry, name, &wl_seat_interface, 4));
1259 display->inputs.push_back(input);
1260 wl_seat_add_listener(input->seat, &seat_listener, input);
1261 }
1262 else if (!strcmp(interface, wl_shm_interface.name)) {
1263 display->shm = static_cast<wl_shm *>(
1264 wl_registry_bind(wl_registry, name, &wl_shm_interface, 1));
1265 }
1266 else if (!strcmp(interface, wl_data_device_manager_interface.name)) {
1267 display->data_device_manager = static_cast<wl_data_device_manager *>(
1268 wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, 1));
1269 }
1270 else if (!strcmp(interface, zwp_relative_pointer_manager_v1_interface.name)) {
1271 display->relative_pointer_manager = static_cast<zwp_relative_pointer_manager_v1 *>(
1272 wl_registry_bind(wl_registry, name, &zwp_relative_pointer_manager_v1_interface, 1));
1273 }
1274 else if (!strcmp(interface, zwp_pointer_constraints_v1_interface.name)) {
1275 display->pointer_constraints = static_cast<zwp_pointer_constraints_v1 *>(
1276 wl_registry_bind(wl_registry, name, &zwp_pointer_constraints_v1_interface, 1));
1277 }
1278 }
1279
1280 /**
1281 * Announce removal of global object.
1282 *
1283 * Notify the client of removed global objects.
1284 *
1285 * This event notifies the client that the global identified by
1286 * name is no longer available. If the client bound to the global
1287 * using the bind request, the client should now destroy that object.
1288 */
global_remove(void *,struct wl_registry *,uint32_t)1289 static void global_remove(void * /*data*/, struct wl_registry * /*wl_registry*/, uint32_t /*name*/)
1290 {
1291 }
1292
1293 static const struct wl_registry_listener registry_listener = {
1294 global_add,
1295 global_remove,
1296 };
1297
1298 /** \} */
1299
1300 /* -------------------------------------------------------------------- */
1301 /** \name Ghost Implementation
1302 *
1303 * Wayland specific implementation of the GHOST_System interface.
1304 * \{ */
1305
GHOST_SystemWayland()1306 GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new display_t)
1307 {
1308 d->system = this;
1309 /* Connect to the Wayland server. */
1310 d->display = wl_display_connect(nullptr);
1311 if (!d->display) {
1312 display_destroy(d);
1313 throw std::runtime_error("Wayland: unable to connect to display!");
1314 }
1315
1316 /* Register interfaces. */
1317 struct wl_registry *registry = wl_display_get_registry(d->display);
1318 wl_registry_add_listener(registry, ®istry_listener, d);
1319 /* Call callback for registry listener. */
1320 wl_display_roundtrip(d->display);
1321 /* Call callbacks for registered listeners. */
1322 wl_display_roundtrip(d->display);
1323 wl_registry_destroy(registry);
1324
1325 if (!d->xdg_shell) {
1326 display_destroy(d);
1327 throw std::runtime_error("Wayland: unable to access xdg_shell!");
1328 }
1329
1330 /* Register data device per seat for IPC between Wayland clients. */
1331 if (d->data_device_manager) {
1332 for (input_t *input : d->inputs) {
1333 input->data_device = wl_data_device_manager_get_data_device(d->data_device_manager,
1334 input->seat);
1335 wl_data_device_add_listener(input->data_device, &data_device_listener, input);
1336 }
1337 }
1338
1339 const char *theme = std::getenv("XCURSOR_THEME");
1340 const char *size = std::getenv("XCURSOR_SIZE");
1341 const int sizei = size ? std::stoi(size) : default_cursor_size;
1342
1343 d->cursor_theme = wl_cursor_theme_load(theme, sizei, d->shm);
1344 if (!d->cursor_theme) {
1345 display_destroy(d);
1346 throw std::runtime_error("Wayland: unable to access cursor themes!");
1347 }
1348 }
1349
~GHOST_SystemWayland()1350 GHOST_SystemWayland::~GHOST_SystemWayland()
1351 {
1352 display_destroy(d);
1353 }
1354
processEvents(bool waitForEvent)1355 bool GHOST_SystemWayland::processEvents(bool waitForEvent)
1356 {
1357 const bool fired = getTimerManager()->fireTimers(getMilliSeconds());
1358
1359 if (waitForEvent) {
1360 wl_display_dispatch(d->display);
1361 }
1362 else {
1363 wl_display_roundtrip(d->display);
1364 }
1365
1366 return fired || (getEventManager()->getNumEvents() > 0);
1367 }
1368
toggleConsole(int)1369 int GHOST_SystemWayland::toggleConsole(int /*action*/)
1370 {
1371 return 0;
1372 }
1373
getModifierKeys(GHOST_ModifierKeys & keys) const1374 GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) const
1375 {
1376 if (!d->inputs.empty()) {
1377 static const xkb_state_component mods_all = xkb_state_component(
1378 XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_MODS_LOCKED |
1379 XKB_STATE_MODS_EFFECTIVE);
1380
1381 keys.set(GHOST_kModifierKeyLeftShift,
1382 xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_SHIFT, mods_all) ==
1383 1);
1384 keys.set(GHOST_kModifierKeyRightShift,
1385 xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_SHIFT, mods_all) ==
1386 1);
1387 keys.set(GHOST_kModifierKeyLeftAlt,
1388 xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "LAlt", mods_all) == 1);
1389 keys.set(GHOST_kModifierKeyRightAlt,
1390 xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "RAlt", mods_all) == 1);
1391 keys.set(GHOST_kModifierKeyLeftControl,
1392 xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "LControl", mods_all) == 1);
1393 keys.set(GHOST_kModifierKeyRightControl,
1394 xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "RControl", mods_all) == 1);
1395 keys.set(GHOST_kModifierKeyOS,
1396 xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "Super", mods_all) == 1);
1397 keys.set(GHOST_kModifierKeyNumMasks,
1398 xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "NumLock", mods_all) == 1);
1399
1400 return GHOST_kSuccess;
1401 }
1402 return GHOST_kFailure;
1403 }
1404
getButtons(GHOST_Buttons & buttons) const1405 GHOST_TSuccess GHOST_SystemWayland::getButtons(GHOST_Buttons &buttons) const
1406 {
1407 if (!d->inputs.empty()) {
1408 buttons = d->inputs[0]->buttons;
1409 return GHOST_kSuccess;
1410 }
1411 return GHOST_kFailure;
1412 }
1413
getClipboard(bool) const1414 GHOST_TUns8 *GHOST_SystemWayland::getClipboard(bool /*selection*/) const
1415 {
1416 GHOST_TUns8 *clipboard = static_cast<GHOST_TUns8 *>(malloc((selection.size() + 1)));
1417 memcpy(clipboard, selection.data(), selection.size() + 1);
1418 return clipboard;
1419 }
1420
putClipboard(GHOST_TInt8 * buffer,bool) const1421 void GHOST_SystemWayland::putClipboard(GHOST_TInt8 *buffer, bool /*selection*/) const
1422 {
1423 if (!d->data_device_manager || d->inputs.empty()) {
1424 return;
1425 }
1426
1427 data_source_t *data_source = d->inputs[0]->data_source;
1428
1429 /* Copy buffer. */
1430 data_source->buffer_out = static_cast<char *>(malloc(strlen(buffer) + 1));
1431 std::strcpy(data_source->buffer_out, buffer);
1432
1433 data_source->data_source = wl_data_device_manager_create_data_source(d->data_device_manager);
1434
1435 wl_data_source_add_listener(
1436 data_source->data_source, &data_source_listener, data_source->buffer_out);
1437
1438 for (const std::string &type : mime_send) {
1439 wl_data_source_offer(data_source->data_source, type.c_str());
1440 }
1441
1442 if (!d->inputs.empty() && d->inputs[0]->data_device) {
1443 wl_data_device_set_selection(
1444 d->inputs[0]->data_device, data_source->data_source, data_source->source_serial);
1445 }
1446 }
1447
getNumDisplays() const1448 GHOST_TUns8 GHOST_SystemWayland::getNumDisplays() const
1449 {
1450 return d ? GHOST_TUns8(d->outputs.size()) : 0;
1451 }
1452
getCursorPosition(GHOST_TInt32 & x,GHOST_TInt32 & y) const1453 GHOST_TSuccess GHOST_SystemWayland::getCursorPosition(GHOST_TInt32 &x, GHOST_TInt32 &y) const
1454 {
1455 if (!d->inputs.empty() && (d->inputs[0]->focus_pointer != nullptr)) {
1456 x = d->inputs[0]->x;
1457 y = d->inputs[0]->y;
1458 return GHOST_kSuccess;
1459 }
1460 else {
1461 return GHOST_kFailure;
1462 }
1463 }
1464
setCursorPosition(GHOST_TInt32,GHOST_TInt32)1465 GHOST_TSuccess GHOST_SystemWayland::setCursorPosition(GHOST_TInt32 /*x*/, GHOST_TInt32 /*y*/)
1466 {
1467 return GHOST_kFailure;
1468 }
1469
getMainDisplayDimensions(GHOST_TUns32 & width,GHOST_TUns32 & height) const1470 void GHOST_SystemWayland::getMainDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const
1471 {
1472 if (getNumDisplays() > 0) {
1473 /* We assume first output as main. */
1474 width = uint32_t(d->outputs[0]->width);
1475 height = uint32_t(d->outputs[0]->height);
1476 }
1477 }
1478
getAllDisplayDimensions(GHOST_TUns32 & width,GHOST_TUns32 & height) const1479 void GHOST_SystemWayland::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const
1480 {
1481 getMainDisplayDimensions(width, height);
1482 }
1483
createOffscreenContext(GHOST_GLSettings glSettings)1484 GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings glSettings)
1485 {
1486 /* Create new off-screen window. */
1487 wl_surface *os_surface = wl_compositor_create_surface(compositor());
1488 wl_egl_window *os_egl_window = wl_egl_window_create(os_surface, int(1), int(1));
1489
1490 d->os_surfaces.push_back(os_surface);
1491 d->os_egl_windows.push_back(os_egl_window);
1492
1493 GHOST_Context *context = new GHOST_ContextEGL(false,
1494 EGLNativeWindowType(os_egl_window),
1495 EGLNativeDisplayType(d->display),
1496 EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
1497 3,
1498 3,
1499 GHOST_OPENGL_EGL_CONTEXT_FLAGS,
1500 GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
1501 EGL_OPENGL_API);
1502
1503 if (context->initializeDrawingContext()) {
1504 return context;
1505 }
1506 else {
1507 delete context;
1508 }
1509
1510 GHOST_PRINT("Cannot create off-screen EGL context" << std::endl);
1511
1512 return nullptr;
1513 }
1514
disposeContext(GHOST_IContext * context)1515 GHOST_TSuccess GHOST_SystemWayland::disposeContext(GHOST_IContext *context)
1516 {
1517 delete context;
1518 return GHOST_kSuccess;
1519 }
1520
createWindow(const char * title,GHOST_TInt32 left,GHOST_TInt32 top,GHOST_TUns32 width,GHOST_TUns32 height,GHOST_TWindowState state,GHOST_TDrawingContextType type,GHOST_GLSettings glSettings,const bool exclusive,const bool is_dialog,const GHOST_IWindow * parentWindow)1521 GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title,
1522 GHOST_TInt32 left,
1523 GHOST_TInt32 top,
1524 GHOST_TUns32 width,
1525 GHOST_TUns32 height,
1526 GHOST_TWindowState state,
1527 GHOST_TDrawingContextType type,
1528 GHOST_GLSettings glSettings,
1529 const bool exclusive,
1530 const bool is_dialog,
1531 const GHOST_IWindow *parentWindow)
1532 {
1533 GHOST_WindowWayland *window = new GHOST_WindowWayland(
1534 this,
1535 title,
1536 left,
1537 top,
1538 width,
1539 height,
1540 state,
1541 parentWindow,
1542 type,
1543 is_dialog,
1544 ((glSettings.flags & GHOST_glStereoVisual) != 0),
1545 exclusive);
1546
1547 if (window) {
1548 if (window->getValid()) {
1549 m_windowManager->addWindow(window);
1550 m_windowManager->setActiveWindow(window);
1551 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
1552 }
1553 else {
1554 delete window;
1555 window = nullptr;
1556 }
1557 }
1558
1559 return window;
1560 }
1561
display()1562 wl_display *GHOST_SystemWayland::display()
1563 {
1564 return d->display;
1565 }
1566
compositor()1567 wl_compositor *GHOST_SystemWayland::compositor()
1568 {
1569 return d->compositor;
1570 }
1571
shell()1572 xdg_wm_base *GHOST_SystemWayland::shell()
1573 {
1574 return d->xdg_shell;
1575 }
1576
setSelection(const std::string & selection)1577 void GHOST_SystemWayland::setSelection(const std::string &selection)
1578 {
1579 this->selection = selection;
1580 }
1581
set_cursor_buffer(input_t * input,wl_buffer * buffer)1582 static void set_cursor_buffer(input_t *input, wl_buffer *buffer)
1583 {
1584 input->cursor.visible = (buffer != nullptr);
1585
1586 wl_surface_attach(input->cursor.surface, buffer, 0, 0);
1587 wl_surface_commit(input->cursor.surface);
1588
1589 if (input->cursor.visible) {
1590 wl_surface_damage(input->cursor.surface,
1591 0,
1592 0,
1593 int32_t(input->cursor.image.width),
1594 int32_t(input->cursor.image.height));
1595 wl_pointer_set_cursor(input->pointer,
1596 input->pointer_serial,
1597 input->cursor.surface,
1598 int32_t(input->cursor.image.hotspot_x),
1599 int32_t(input->cursor.image.hotspot_y));
1600 }
1601 }
1602
setCursorShape(GHOST_TStandardCursor shape)1603 GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape)
1604 {
1605 if (d->inputs.empty()) {
1606 return GHOST_kFailure;
1607 }
1608 const std::string cursor_name = cursors.count(shape) ? cursors.at(shape) :
1609 cursors.at(GHOST_kStandardCursorDefault);
1610
1611 wl_cursor *cursor = wl_cursor_theme_get_cursor(d->cursor_theme, cursor_name.c_str());
1612
1613 if (!cursor) {
1614 GHOST_PRINT("cursor '" << cursor_name << "' does not exist" << std::endl);
1615 return GHOST_kFailure;
1616 }
1617
1618 struct wl_cursor_image *image = cursor->images[0];
1619 struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
1620 if (!buffer) {
1621 return GHOST_kFailure;
1622 }
1623 cursor_t *c = &d->inputs[0]->cursor;
1624 c->buffer = buffer;
1625 c->image = *image;
1626
1627 set_cursor_buffer(d->inputs[0], buffer);
1628
1629 return GHOST_kSuccess;
1630 }
1631
hasCursorShape(GHOST_TStandardCursor cursorShape)1632 GHOST_TSuccess GHOST_SystemWayland::hasCursorShape(GHOST_TStandardCursor cursorShape)
1633 {
1634 return GHOST_TSuccess(cursors.count(cursorShape) && !cursors.at(cursorShape).empty());
1635 }
1636
setCustomCursorShape(GHOST_TUns8 * bitmap,GHOST_TUns8 * mask,int sizex,int sizey,int hotX,int hotY,bool)1637 GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(GHOST_TUns8 *bitmap,
1638 GHOST_TUns8 *mask,
1639 int sizex,
1640 int sizey,
1641 int hotX,
1642 int hotY,
1643 bool /*canInvertColor*/)
1644 {
1645 if (d->inputs.empty()) {
1646 return GHOST_kFailure;
1647 }
1648
1649 cursor_t *cursor = &d->inputs[0]->cursor;
1650
1651 static const int32_t stride = sizex * 4; /* ARGB */
1652 cursor->file_buffer->size = size_t(stride * sizey);
1653
1654 const int fd = memfd_create("blender-cursor-custom", MFD_CLOEXEC | MFD_ALLOW_SEALING);
1655 fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK);
1656 posix_fallocate(fd, 0, int32_t(cursor->file_buffer->size));
1657
1658 cursor->file_buffer->data = mmap(
1659 nullptr, cursor->file_buffer->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1660
1661 struct wl_shm_pool *pool = wl_shm_create_pool(d->shm, fd, int32_t(cursor->file_buffer->size));
1662
1663 wl_buffer *buffer = wl_shm_pool_create_buffer(
1664 pool, 0, sizex, sizey, stride, WL_SHM_FORMAT_ARGB8888);
1665
1666 wl_shm_pool_destroy(pool);
1667 close(fd);
1668
1669 wl_buffer_add_listener(buffer, &cursor_buffer_listener, cursor);
1670
1671 static constexpr uint32_t black = 0xFF000000;
1672 static constexpr uint32_t white = 0xFFFFFFFF;
1673 static constexpr uint32_t transparent = 0x00000000;
1674
1675 uint8_t datab = 0, maskb = 0;
1676 uint32_t *pixel;
1677
1678 for (int y = 0; y < sizey; ++y) {
1679 pixel = &static_cast<uint32_t *>(cursor->file_buffer->data)[y * sizex];
1680 for (int x = 0; x < sizex; ++x) {
1681 if ((x % 8) == 0) {
1682 datab = *bitmap++;
1683 maskb = *mask++;
1684
1685 /* Reverse bit order. */
1686 datab = uint8_t((datab * 0x0202020202ULL & 0x010884422010ULL) % 1023);
1687 maskb = uint8_t((maskb * 0x0202020202ULL & 0x010884422010ULL) % 1023);
1688 }
1689
1690 if (maskb & 0x80) {
1691 *pixel++ = (datab & 0x80) ? white : black;
1692 }
1693 else {
1694 *pixel++ = (datab & 0x80) ? white : transparent;
1695 }
1696 datab <<= 1;
1697 maskb <<= 1;
1698 }
1699 }
1700
1701 cursor->buffer = buffer;
1702 cursor->image.width = uint32_t(sizex);
1703 cursor->image.height = uint32_t(sizey);
1704 cursor->image.hotspot_x = uint32_t(hotX);
1705 cursor->image.hotspot_y = uint32_t(hotY);
1706
1707 set_cursor_buffer(d->inputs[0], buffer);
1708
1709 return GHOST_kSuccess;
1710 }
1711
setCursorVisibility(bool visible)1712 GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(bool visible)
1713 {
1714 if (d->inputs.empty()) {
1715 return GHOST_kFailure;
1716 }
1717
1718 input_t *input = d->inputs[0];
1719
1720 cursor_t *cursor = &input->cursor;
1721 if (visible) {
1722 if (!cursor->visible) {
1723 set_cursor_buffer(input, cursor->buffer);
1724 }
1725 }
1726 else {
1727 if (cursor->visible) {
1728 set_cursor_buffer(input, nullptr);
1729 }
1730 }
1731
1732 return GHOST_kSuccess;
1733 }
1734
setCursorGrab(const GHOST_TGrabCursorMode mode,wl_surface * surface)1735 GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mode,
1736 wl_surface *surface)
1737 {
1738 if (d->inputs.empty()) {
1739 return GHOST_kFailure;
1740 }
1741
1742 input_t *input = d->inputs[0];
1743
1744 switch (mode) {
1745 case GHOST_kGrabDisable:
1746 if (input->relative_pointer) {
1747 zwp_relative_pointer_v1_destroy(input->relative_pointer);
1748 input->relative_pointer = nullptr;
1749 }
1750 if (input->locked_pointer) {
1751 zwp_locked_pointer_v1_destroy(input->locked_pointer);
1752 input->locked_pointer = nullptr;
1753 }
1754 break;
1755
1756 case GHOST_kGrabNormal:
1757 case GHOST_kGrabWrap:
1758 input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(
1759 d->relative_pointer_manager, input->pointer);
1760 zwp_relative_pointer_v1_add_listener(
1761 input->relative_pointer, &relative_pointer_listener, input);
1762 input->locked_pointer = zwp_pointer_constraints_v1_lock_pointer(
1763 d->pointer_constraints,
1764 surface,
1765 input->pointer,
1766 nullptr,
1767 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
1768 break;
1769
1770 case GHOST_kGrabHide:
1771 setCursorVisibility(false);
1772 break;
1773 }
1774
1775 return GHOST_kSuccess;
1776 }
1777
1778 /** \} */
1779