Blender  V3.3
GHOST_SystemWayland.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include "GHOST_SystemWayland.h"
8 #include "GHOST_Event.h"
9 #include "GHOST_EventButton.h"
10 #include "GHOST_EventCursor.h"
11 #include "GHOST_EventDragnDrop.h"
12 #include "GHOST_EventKey.h"
13 #include "GHOST_EventWheel.h"
14 #include "GHOST_TimerManager.h"
15 #include "GHOST_WindowManager.h"
16 #include "GHOST_utildefines.h"
17 
18 #include "GHOST_ContextEGL.h"
19 
20 #ifdef WITH_GHOST_WAYLAND_DYNLOAD
21 # include <wayland_dynload_API.h> /* For `ghost_wl_dynload_libraries`. */
22 #endif
23 
24 #include <EGL/egl.h>
25 
26 #ifdef WITH_GHOST_WAYLAND_DYNLOAD
27 # include <wayland_dynload_egl.h>
28 #endif
29 #include <wayland-egl.h>
30 
31 #include <algorithm>
32 #include <atomic>
33 #include <stdexcept>
34 #include <thread>
35 #include <unordered_map>
36 #include <unordered_set>
37 
38 #ifdef WITH_GHOST_WAYLAND_DYNLOAD
39 # include <wayland_dynload_cursor.h>
40 #endif
41 #include <wayland-cursor.h>
42 
44 
45 #include <xkbcommon/xkbcommon.h>
46 
47 /* Generated by `wayland-scanner`. */
48 #include <pointer-constraints-unstable-v1-client-protocol.h>
49 #include <relative-pointer-unstable-v1-client-protocol.h>
50 #include <tablet-unstable-v2-client-protocol.h>
51 #include <xdg-output-unstable-v1-client-protocol.h>
52 
53 #include <fcntl.h>
54 #include <sys/mman.h>
55 #include <unistd.h>
56 
57 #include <cstring>
58 #include <mutex>
59 
60 /* Logging, use `ghost.wl.*` prefix. */
61 #include "CLG_log.h"
62 
64 
65 static void output_handle_done(void *data, struct wl_output *wl_output);
66 
74 #define USE_GNOME_CONFINE_HACK
79 // #define USE_GNOME_CONFINE_HACK_ALWAYS_ON
80 
81 #ifdef USE_GNOME_CONFINE_HACK
82 static bool use_gnome_confine_hack = false;
83 #endif
84 
85 /* -------------------------------------------------------------------- */
97 #define BTN_LEFT 0x110
98 #define BTN_RIGHT 0x111
99 #define BTN_MIDDLE 0x112
100 #define BTN_SIDE 0x113
101 #define BTN_EXTRA 0x114
102 #define BTN_FORWARD 0x115
103 #define BTN_BACK 0x116
104 // #define BTN_TASK 0x117 /* UNUSED. */
105 
109 #define BTN_STYLUS 0x14b /* Use as right-mouse. */
110 #define BTN_STYLUS2 0x14c /* Use as middle-mouse. */
111 /* NOTE(@campbellbarton): Map to an additional button (not sure which hardware uses this). */
112 #define BTN_STYLUS3 0x149
113 
117 #define KEY_GRAVE 41
118 
121 /* -------------------------------------------------------------------- */
129 #define EVDEV_OFFSET 8
130 
131 struct cursor_t {
132  bool visible = false;
139  bool is_hardware = true;
140  bool is_custom = false;
141  struct wl_surface *wl_surface = nullptr;
142  struct wl_buffer *wl_buffer = nullptr;
143  struct wl_cursor_image wl_image = {0};
144  struct wl_cursor_theme *wl_theme = nullptr;
145  void *custom_data = nullptr;
146  size_t custom_data_size = 0;
147  int size = 0;
148  std::string theme_name;
149 
150  int custom_scale = 1;
151 };
152 
159  struct input_t *input = nullptr;
160  struct wl_surface *cursor_surface = nullptr;
162  bool proximity = false;
163 
165 };
166 
167 struct data_offer_t {
168  std::unordered_set<std::string> types;
171  struct wl_data_offer *id = nullptr;
172  std::atomic<bool> in_use = false;
173  struct {
175  wl_fixed_t xy[2] = {0, 0};
176  } dnd;
177 };
178 
180  struct wl_data_source *data_source = nullptr;
181  char *buffer_out = nullptr;
182 };
183 
193  struct input_t *input = nullptr;
194 
195  xkb_keycode_t key_code;
196 
201  struct {
204 };
205 
208  bool use_lock = false;
209  bool use_confine = false;
210 };
211 
229  wl_fixed_t xy[2] = {0, 0};
230 
232  std::unordered_set<const output_t *> outputs;
233 
234  int theme_scale = 1;
235 
238 
243  struct wl_surface *wl_surface = nullptr;
244 
246 };
247 
254 
259  struct wl_surface *wl_surface = nullptr;
260 };
261 
262 struct input_t {
264 
265  std::string name;
266  struct wl_seat *wl_seat = nullptr;
267  struct wl_pointer *wl_pointer = nullptr;
268  struct wl_keyboard *wl_keyboard = nullptr;
269  struct zwp_tablet_seat_v2 *tablet_seat = nullptr;
270 
272  std::unordered_set<zwp_tablet_tool_v2 *> tablet_tools;
273 
276 
278 
281 
283 
284 #ifdef USE_GNOME_CONFINE_HACK
286 #endif
288  wl_fixed_t grab_lock_xy[2] = {0, 0};
289 
290  struct cursor_t cursor;
291 
292  struct zwp_relative_pointer_v1 *relative_pointer = nullptr;
293  struct zwp_locked_pointer_v1 *locked_pointer = nullptr;
294  struct zwp_confined_pointer_v1 *confined_pointer = nullptr;
295 
296  struct xkb_context *xkb_context = nullptr;
297 
298  struct xkb_state *xkb_state = nullptr;
302  struct xkb_state *xkb_state_empty = nullptr;
308 
309  struct {
317 
318  struct wl_surface *focus_dnd = nullptr;
319 
320  struct wl_data_device *data_device = nullptr;
322  struct data_offer_t *data_offer_dnd = nullptr;
324 
328 
329  struct data_source_t *data_source = nullptr;
331 
334 };
335 
336 struct display_t {
338 
339  struct wl_display *display = nullptr;
340  struct wl_compositor *compositor = nullptr;
341 
342 #ifdef WITH_GHOST_WAYLAND_LIBDECOR
343  struct libdecor *decor_context = nullptr;
344 #else
345  struct xdg_wm_base *xdg_shell = nullptr;
346  struct zxdg_decoration_manager_v1 *xdg_decoration_manager = nullptr;
347 #endif
348 
349  struct zxdg_output_manager_v1 *xdg_output_manager = nullptr;
350  struct wl_shm *shm = nullptr;
351  std::vector<output_t *> outputs;
352  std::vector<input_t *> inputs;
353 
354  struct wl_data_device_manager *data_device_manager = nullptr;
355  struct zwp_tablet_manager_v2 *tablet_manager = nullptr;
356  struct zwp_relative_pointer_manager_v1 *relative_pointer_manager = nullptr;
357  struct zwp_pointer_constraints_v1 *pointer_constraints = nullptr;
358 };
359 
360 #undef LOG
361 
364 /* -------------------------------------------------------------------- */
369 
372 
379 static void ghost_wayland_log_handler(const char *msg, va_list arg)
380 {
381  fprintf(stderr, "GHOST/Wayland: ");
382  vfprintf(stderr, msg, arg); /* Includes newline. */
383 
385  if (backtrace_fn) {
386  backtrace_fn(stderr); /* Includes newline. */
387  }
388 }
389 
391 {
392  if (input->pointer.serial == input->cursor_source_serial) {
393  return &input->pointer;
394  }
395  if (input->tablet.serial == input->cursor_source_serial) {
396  return &input->tablet;
397  }
398  return nullptr;
399 }
400 
402  const wl_surface *wl_surface)
403 {
404  if (ghost_wl_surface_own_cursor_pointer(wl_surface)) {
405  return &input->pointer;
406  }
407  if (ghost_wl_surface_own_cursor_tablet(wl_surface)) {
408  return &input->tablet;
409  }
410  GHOST_ASSERT(0, "Surface found without pointer/tablet tag");
411  return nullptr;
412 }
413 
414 static void display_destroy(display_t *d)
415 {
416  if (d->data_device_manager) {
417  wl_data_device_manager_destroy(d->data_device_manager);
418  }
419 
420  if (d->tablet_manager) {
421  zwp_tablet_manager_v2_destroy(d->tablet_manager);
422  }
423 
424  for (output_t *output : d->outputs) {
425  wl_output_destroy(output->wl_output);
426  delete output;
427  }
428 
429  for (input_t *input : d->inputs) {
430 
431  /* First handle members that require locking.
432  * While highly unlikely, it's possible they are being used while this function runs. */
433  {
434  std::lock_guard lock{input->data_source_mutex};
435  if (input->data_source) {
436  free(input->data_source->buffer_out);
437  if (input->data_source->data_source) {
438  wl_data_source_destroy(input->data_source->data_source);
439  }
440  delete input->data_source;
441  }
442  }
443 
444  {
445  std::lock_guard lock{input->data_offer_dnd_mutex};
446  if (input->data_offer_dnd) {
447  wl_data_offer_destroy(input->data_offer_dnd->id);
448  delete input->data_offer_dnd;
449  }
450  }
451 
452  {
453  std::lock_guard lock{input->data_offer_copy_paste_mutex};
454  if (input->data_offer_copy_paste) {
455  wl_data_offer_destroy(input->data_offer_copy_paste->id);
456  delete input->data_offer_copy_paste;
457  }
458  }
459 
460  if (input->data_device) {
461  wl_data_device_release(input->data_device);
462  }
463 
464  if (input->cursor.custom_data) {
465  munmap(input->cursor.custom_data, input->cursor.custom_data_size);
466  }
467 
468  if (input->wl_pointer) {
469  if (input->cursor.wl_surface) {
470  wl_surface_destroy(input->cursor.wl_surface);
471  }
472  if (input->cursor.wl_theme) {
473  wl_cursor_theme_destroy(input->cursor.wl_theme);
474  }
475  if (input->wl_pointer) {
476  wl_pointer_destroy(input->wl_pointer);
477  }
478  }
479  if (input->wl_keyboard) {
480  if (input->key_repeat.timer) {
482  }
483  wl_keyboard_destroy(input->wl_keyboard);
484  }
485 
486  /* Un-referencing checks for NULL case. */
487  xkb_state_unref(input->xkb_state);
488  xkb_state_unref(input->xkb_state_empty);
489  xkb_state_unref(input->xkb_state_empty_with_numlock);
490 
491  xkb_context_unref(input->xkb_context);
492 
493  wl_seat_destroy(input->wl_seat);
494  delete input;
495  }
496 
497  if (d->shm) {
498  wl_shm_destroy(d->shm);
499  }
500 
501  if (d->relative_pointer_manager) {
502  zwp_relative_pointer_manager_v1_destroy(d->relative_pointer_manager);
503  }
504 
505  if (d->pointer_constraints) {
506  zwp_pointer_constraints_v1_destroy(d->pointer_constraints);
507  }
508 
509  if (d->compositor) {
510  wl_compositor_destroy(d->compositor);
511  }
512 
513 #ifdef WITH_GHOST_WAYLAND_LIBDECOR
514  if (d->decor_context) {
515  libdecor_unref(d->decor_context);
516  }
517 #else
518  if (d->xdg_decoration_manager) {
519  zxdg_decoration_manager_v1_destroy(d->xdg_decoration_manager);
520  }
521 
522  if (d->xdg_shell) {
523  xdg_wm_base_destroy(d->xdg_shell);
524  }
525 #endif /* !WITH_GHOST_WAYLAND_LIBDECOR */
526 
527  if (eglGetDisplay) {
528  ::eglTerminate(eglGetDisplay(EGLNativeDisplayType(d->display)));
529  }
530 
531  if (d->display) {
532  wl_display_disconnect(d->display);
533  }
534 
535  delete d;
536 }
537 
538 static GHOST_TKey xkb_map_gkey(const xkb_keysym_t sym)
539 {
540 
541  GHOST_TKey gkey;
542  if (sym >= XKB_KEY_0 && sym <= XKB_KEY_9) {
543  gkey = GHOST_TKey(sym);
544  }
545  else if (sym >= XKB_KEY_KP_0 && sym <= XKB_KEY_KP_9) {
546  gkey = GHOST_TKey(GHOST_kKeyNumpad0 + sym - XKB_KEY_KP_0);
547  }
548  else if (sym >= XKB_KEY_A && sym <= XKB_KEY_Z) {
549  gkey = GHOST_TKey(sym);
550  }
551  else if (sym >= XKB_KEY_a && sym <= XKB_KEY_z) {
552  gkey = GHOST_TKey(sym - XKB_KEY_a + XKB_KEY_A);
553  }
554  else if (sym >= XKB_KEY_F1 && sym <= XKB_KEY_F24) {
555  gkey = GHOST_TKey(GHOST_kKeyF1 + sym - XKB_KEY_F1);
556  }
557  else {
558 
559 #define GXMAP(k, x, y) \
560  case x: \
561  k = y; \
562  break
563 
564  switch (sym) {
565  GXMAP(gkey, XKB_KEY_BackSpace, GHOST_kKeyBackSpace);
566  GXMAP(gkey, XKB_KEY_Tab, GHOST_kKeyTab);
567  GXMAP(gkey, XKB_KEY_Linefeed, GHOST_kKeyLinefeed);
568  GXMAP(gkey, XKB_KEY_Clear, GHOST_kKeyClear);
569  GXMAP(gkey, XKB_KEY_Return, GHOST_kKeyEnter);
570 
571  GXMAP(gkey, XKB_KEY_Escape, GHOST_kKeyEsc);
572  GXMAP(gkey, XKB_KEY_space, GHOST_kKeySpace);
573  GXMAP(gkey, XKB_KEY_apostrophe, GHOST_kKeyQuote);
574  GXMAP(gkey, XKB_KEY_comma, GHOST_kKeyComma);
575  GXMAP(gkey, XKB_KEY_minus, GHOST_kKeyMinus);
576  GXMAP(gkey, XKB_KEY_plus, GHOST_kKeyPlus);
577  GXMAP(gkey, XKB_KEY_period, GHOST_kKeyPeriod);
578  GXMAP(gkey, XKB_KEY_slash, GHOST_kKeySlash);
579 
580  GXMAP(gkey, XKB_KEY_semicolon, GHOST_kKeySemicolon);
581  GXMAP(gkey, XKB_KEY_equal, GHOST_kKeyEqual);
582 
583  GXMAP(gkey, XKB_KEY_bracketleft, GHOST_kKeyLeftBracket);
584  GXMAP(gkey, XKB_KEY_bracketright, GHOST_kKeyRightBracket);
585  GXMAP(gkey, XKB_KEY_backslash, GHOST_kKeyBackslash);
586  GXMAP(gkey, XKB_KEY_grave, GHOST_kKeyAccentGrave);
587 
588  GXMAP(gkey, XKB_KEY_Shift_L, GHOST_kKeyLeftShift);
589  GXMAP(gkey, XKB_KEY_Shift_R, GHOST_kKeyRightShift);
590  GXMAP(gkey, XKB_KEY_Control_L, GHOST_kKeyLeftControl);
591  GXMAP(gkey, XKB_KEY_Control_R, GHOST_kKeyRightControl);
592  GXMAP(gkey, XKB_KEY_Alt_L, GHOST_kKeyLeftAlt);
593  GXMAP(gkey, XKB_KEY_Alt_R, GHOST_kKeyRightAlt);
594  GXMAP(gkey, XKB_KEY_Super_L, GHOST_kKeyOS);
595  GXMAP(gkey, XKB_KEY_Super_R, GHOST_kKeyOS);
596  GXMAP(gkey, XKB_KEY_Menu, GHOST_kKeyApp);
597 
598  GXMAP(gkey, XKB_KEY_Caps_Lock, GHOST_kKeyCapsLock);
599  GXMAP(gkey, XKB_KEY_Num_Lock, GHOST_kKeyNumLock);
600  GXMAP(gkey, XKB_KEY_Scroll_Lock, GHOST_kKeyScrollLock);
601 
602  GXMAP(gkey, XKB_KEY_Left, GHOST_kKeyLeftArrow);
603  GXMAP(gkey, XKB_KEY_Right, GHOST_kKeyRightArrow);
604  GXMAP(gkey, XKB_KEY_Up, GHOST_kKeyUpArrow);
605  GXMAP(gkey, XKB_KEY_Down, GHOST_kKeyDownArrow);
606 
607  GXMAP(gkey, XKB_KEY_Print, GHOST_kKeyPrintScreen);
608  GXMAP(gkey, XKB_KEY_Pause, GHOST_kKeyPause);
609 
610  GXMAP(gkey, XKB_KEY_Insert, GHOST_kKeyInsert);
611  GXMAP(gkey, XKB_KEY_Delete, GHOST_kKeyDelete);
612  GXMAP(gkey, XKB_KEY_Home, GHOST_kKeyHome);
613  GXMAP(gkey, XKB_KEY_End, GHOST_kKeyEnd);
614  GXMAP(gkey, XKB_KEY_Page_Up, GHOST_kKeyUpPage);
615  GXMAP(gkey, XKB_KEY_Page_Down, GHOST_kKeyDownPage);
616 
617  GXMAP(gkey, XKB_KEY_KP_Decimal, GHOST_kKeyNumpadPeriod);
618  GXMAP(gkey, XKB_KEY_KP_Enter, GHOST_kKeyNumpadEnter);
619  GXMAP(gkey, XKB_KEY_KP_Add, GHOST_kKeyNumpadPlus);
620  GXMAP(gkey, XKB_KEY_KP_Subtract, GHOST_kKeyNumpadMinus);
621  GXMAP(gkey, XKB_KEY_KP_Multiply, GHOST_kKeyNumpadAsterisk);
622  GXMAP(gkey, XKB_KEY_KP_Divide, GHOST_kKeyNumpadSlash);
623 
624  GXMAP(gkey, XKB_KEY_XF86AudioPlay, GHOST_kKeyMediaPlay);
625  GXMAP(gkey, XKB_KEY_XF86AudioStop, GHOST_kKeyMediaStop);
626  GXMAP(gkey, XKB_KEY_XF86AudioPrev, GHOST_kKeyMediaFirst);
627  GXMAP(gkey, XKB_KEY_XF86AudioNext, GHOST_kKeyMediaLast);
628  default:
629  /* Rely on #xkb_map_gkey_or_scan_code to report when no key can be found. */
630  gkey = GHOST_kKeyUnknown;
631  }
632 #undef GXMAP
633  }
634 
635  return gkey;
636 }
637 
645 static GHOST_TKey xkb_map_gkey_or_scan_code(const xkb_keysym_t sym, const uint32_t key)
646 {
647  GHOST_TKey gkey = xkb_map_gkey(sym);
648 
649  if (UNLIKELY(gkey == GHOST_kKeyUnknown)) {
650  /* Fall back to physical location for keys that would otherwise do nothing. */
651  switch (key) {
652  case KEY_GRAVE: {
653  gkey = GHOST_kKeyAccentGrave;
654  break;
655  }
656  default: {
657  GHOST_PRINT(
658  /* Key-code. */
659  "unhandled key: " << std::hex << std::showbase << sym << /* Hex. */
660  std::dec << " (" << sym << "), " << /* Decimal. */
661  /* Scan-code. */
662  "scan-code: " << std::hex << std::showbase << key << /* Hex. */
663  std::dec << " (" << key << ")" << /* Decimal. */
664  std::endl);
665  break;
666  }
667  }
668  }
669 
670  return gkey;
671 }
672 
673 static GHOST_TTabletMode tablet_tool_map_type(enum zwp_tablet_tool_v2_type wl_tablet_tool_type)
674 {
675  switch (wl_tablet_tool_type) {
676  case ZWP_TABLET_TOOL_V2_TYPE_ERASER: {
678  }
679  case ZWP_TABLET_TOOL_V2_TYPE_PEN:
680  case ZWP_TABLET_TOOL_V2_TYPE_BRUSH:
681  case ZWP_TABLET_TOOL_V2_TYPE_PENCIL:
682  case ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH:
683  case ZWP_TABLET_TOOL_V2_TYPE_FINGER:
684  case ZWP_TABLET_TOOL_V2_TYPE_MOUSE:
685  case ZWP_TABLET_TOOL_V2_TYPE_LENS: {
687  }
688  }
689 
690  GHOST_PRINT("unknown tablet tool: " << wl_tablet_tool_type << std::endl);
692 }
693 
694 static const int default_cursor_size = 24;
695 
696 static const std::unordered_map<GHOST_TStandardCursor, const char *> cursors = {
697  {GHOST_kStandardCursorDefault, "left_ptr"},
698  {GHOST_kStandardCursorRightArrow, "right_ptr"},
699  {GHOST_kStandardCursorLeftArrow, "left_ptr"},
701  {GHOST_kStandardCursorDestroy, "pirate"},
702  {GHOST_kStandardCursorHelp, "question_arrow"},
703  {GHOST_kStandardCursorWait, "watch"},
704  {GHOST_kStandardCursorText, "xterm"},
705  {GHOST_kStandardCursorCrosshair, "crosshair"},
709  {GHOST_kStandardCursorPencil, "pencil"},
710  {GHOST_kStandardCursorUpArrow, "sb_up_arrow"},
711  {GHOST_kStandardCursorDownArrow, "sb_down_arrow"},
716  {GHOST_kStandardCursorEyedropper, "color-picker"},
717  {GHOST_kStandardCursorZoomIn, "zoom-in"},
718  {GHOST_kStandardCursorZoomOut, "zoom-out"},
719  {GHOST_kStandardCursorMove, "move"},
720  {GHOST_kStandardCursorNSEWScroll, "size_all"}, /* Not an exact match. */
721  {GHOST_kStandardCursorNSScroll, "size_ver"}, /* Not an exact match. */
722  {GHOST_kStandardCursorEWScroll, "size_hor"}, /* Not an exact match. */
723  {GHOST_kStandardCursorStop, "not-allowed"},
724  {GHOST_kStandardCursorUpDown, "sb_v_double_arrow"},
725  {GHOST_kStandardCursorLeftRight, "sb_h_double_arrow"},
726  {GHOST_kStandardCursorTopSide, "top_side"},
727  {GHOST_kStandardCursorBottomSide, "bottom_side"},
728  {GHOST_kStandardCursorLeftSide, "left_side"},
729  {GHOST_kStandardCursorRightSide, "right_side"},
730  {GHOST_kStandardCursorTopLeftCorner, "top_left_corner"},
731  {GHOST_kStandardCursorTopRightCorner, "top_right_corner"},
732  {GHOST_kStandardCursorBottomRightCorner, "bottom_right_corner"},
733  {GHOST_kStandardCursorBottomLeftCorner, "bottom_left_corner"},
734  {GHOST_kStandardCursorCopy, "copy"},
735 };
736 
737 static constexpr const char *mime_text_plain = "text/plain";
738 static constexpr const char *mime_text_utf8 = "text/plain;charset=utf-8";
739 static constexpr const char *mime_text_uri = "text/uri-list";
740 
741 static const std::unordered_map<std::string, GHOST_TDragnDropTypes> mime_dnd = {
745 };
746 
747 static const std::vector<std::string> mime_preference_order = {
751 };
752 
753 static const std::vector<std::string> mime_send = {
754  "UTF8_STRING",
755  "COMPOUND_TEXT",
756  "TEXT",
757  "STRING",
758  "text/plain;charset=utf-8",
759  "text/plain",
760 };
761 
762 static int memfd_create_sealed(const char *name)
763 {
764 #ifdef HAVE_MEMFD_CREATE
765  const int fd = memfd_create(name, MFD_CLOEXEC | MFD_ALLOW_SEALING);
766  if (fd >= 0) {
767  fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
768  }
769  return fd;
770 #else /* HAVE_MEMFD_CREATE */
771  char *path = getenv("XDG_RUNTIME_DIR");
772  if (!path) {
773  errno = ENOENT;
774  return -1;
775  }
776  char *tmpname;
777  asprintf(&tmpname, "%s/%s-XXXXXX", path, name);
778  const int fd = mkostemp(tmpname, O_CLOEXEC);
779  if (fd >= 0) {
780  unlink(tmpname);
781  }
782  free(tmpname);
783  return fd;
784 #endif /* !HAVE_MEMFD_CREATE */
785 }
786 
787 static size_t ghost_wl_shm_format_as_size(enum wl_shm_format format)
788 {
789  switch (format) {
790  case WL_SHM_FORMAT_ARGB8888: {
791  return 4;
792  }
793  default: {
794  /* Support other formats as needed. */
795  GHOST_ASSERT(0, "Unexpected format passed in!");
796  return 4;
797  }
798  }
799 }
800 
808 static wl_buffer *ghost_wl_buffer_create_for_image(struct wl_shm *shm,
809  const int32_t size_xy[2],
810  enum wl_shm_format format,
811  void **r_buffer_data,
812  size_t *r_buffer_data_size)
813 {
814  const int fd = memfd_create_sealed("ghost-wl-buffer");
815  wl_buffer *buffer = nullptr;
816  if (fd >= 0) {
817  const int32_t buffer_stride = size_xy[0] * ghost_wl_shm_format_as_size(format);
818  const int32_t buffer_size = buffer_stride * size_xy[1];
819  if (posix_fallocate(fd, 0, buffer_size) == 0) {
820  void *buffer_data = mmap(nullptr, buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
821  if (buffer_data != MAP_FAILED) {
822  struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, buffer_size);
823  buffer = wl_shm_pool_create_buffer(pool, 0, UNPACK2(size_xy), buffer_stride, format);
824  wl_shm_pool_destroy(pool);
825  if (buffer) {
826  *r_buffer_data = buffer_data;
827  *r_buffer_data_size = (size_t)buffer_size;
828  }
829  else {
830  /* Highly unlikely. */
831  munmap(buffer_data, buffer_size);
832  }
833  }
834  }
835  close(fd);
836  }
837  return buffer;
838 }
839 
842 /* -------------------------------------------------------------------- */
849 static CLG_LogRef LOG_WL_RELATIVE_POINTER = {"ghost.wl.handle.relative_pointer"};
850 #define LOG (&LOG_WL_RELATIVE_POINTER)
851 
856  GHOST_WindowWayland *win,
857  const wl_fixed_t xy[2])
858 {
859  const wl_fixed_t scale = win->scale();
860 
861  input->pointer.xy[0] = xy[0];
862  input->pointer.xy[1] = xy[1];
863 
864 #ifdef USE_GNOME_CONFINE_HACK
865  if (input->use_pointer_software_confine) {
867  win->getClientBounds(bounds);
868  /* Needed or the cursor is considered outside the window and doesn't restore the location. */
869  bounds.m_r -= 1;
870  bounds.m_b -= 1;
871 
872  bounds.m_l = wl_fixed_from_int(bounds.m_l) / scale;
873  bounds.m_t = wl_fixed_from_int(bounds.m_t) / scale;
874  bounds.m_r = wl_fixed_from_int(bounds.m_r) / scale;
875  bounds.m_b = wl_fixed_from_int(bounds.m_b) / scale;
876  bounds.clampPoint(UNPACK2(input->pointer.xy));
877  }
878 #endif
879  input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
881  win,
882  wl_fixed_to_int(scale * input->pointer.xy[0]),
883  wl_fixed_to_int(scale * input->pointer.xy[1]),
885 }
886 
888  void *data,
889  struct zwp_relative_pointer_v1 * /*zwp_relative_pointer_v1*/,
890  const uint32_t /*utime_hi*/,
891  const uint32_t /*utime_lo*/,
892  const wl_fixed_t dx,
893  const wl_fixed_t dy,
894  const wl_fixed_t /*dx_unaccel*/,
895  const wl_fixed_t /*dy_unaccel*/)
896 {
897  input_t *input = static_cast<input_t *>(data);
898  if (wl_surface *focus_surface = input->pointer.wl_surface) {
899  CLOG_INFO(LOG, 2, "relative_motion");
900  GHOST_WindowWayland *win = ghost_wl_surface_user_data(focus_surface);
901  const wl_fixed_t scale = win->scale();
902  const wl_fixed_t xy_next[2] = {
903  input->pointer.xy[0] + (dx / scale),
904  input->pointer.xy[1] + (dy / scale),
905  };
907  }
908  else {
909  CLOG_INFO(LOG, 2, "relative_motion (skipped)");
910  }
911 }
912 
913 static const zwp_relative_pointer_v1_listener relative_pointer_listener = {
915 };
916 
917 #undef LOG
918 
921 /* -------------------------------------------------------------------- */
925 static CLG_LogRef LOG_WL_DATA_SOURCE = {"ghost.wl.handle.data_source"};
926 #define LOG (&LOG_WL_DATA_SOURCE)
927 
928 static void dnd_events(const input_t *const input, const GHOST_TEventType event)
929 {
930  /* NOTE: `input->data_offer_dnd_mutex` must already be locked. */
931  if (wl_surface *focus_surface = input->focus_dnd) {
932  GHOST_WindowWayland *win = ghost_wl_surface_user_data(focus_surface);
933  const wl_fixed_t scale = win->scale();
934  const int event_xy[2] = {
935  wl_fixed_to_int(scale * input->data_offer_dnd->dnd.xy[0]),
936  wl_fixed_to_int(scale * input->data_offer_dnd->dnd.xy[1]),
937  };
938 
939  const uint64_t time = input->system->getMilliSeconds();
940  for (const std::string &type : mime_preference_order) {
941  input->system->pushEvent(new GHOST_EventDragnDrop(
942  time, event, mime_dnd.at(type), win, UNPACK2(event_xy), nullptr));
943  }
944  }
945 }
946 
947 static std::string read_pipe(data_offer_t *data_offer,
948  const std::string mime_receive,
949  std::mutex *mutex)
950 {
951  int pipefd[2];
952  if (UNLIKELY(pipe(pipefd) != 0)) {
953  return {};
954  }
955  wl_data_offer_receive(data_offer->id, mime_receive.c_str(), pipefd[1]);
956  close(pipefd[1]);
957 
958  data_offer->in_use.store(false);
959 
960  if (mutex) {
961  mutex->unlock();
962  }
963  /* WARNING: `data_offer` may be freed from now on. */
964 
965  std::string data;
966  ssize_t len;
967  char buffer[4096];
968  while ((len = read(pipefd[0], buffer, sizeof(buffer))) > 0) {
969  data.insert(data.end(), buffer, buffer + len);
970  }
971  close(pipefd[0]);
972 
973  return data;
974 }
975 
982 static void data_source_handle_target(void * /*data*/,
983  struct wl_data_source * /*wl_data_source*/,
984  const char * /*mime_type*/)
985 {
986  CLOG_INFO(LOG, 2, "target");
987 }
988 
989 static void data_source_handle_send(void *data,
990  struct wl_data_source * /*wl_data_source*/,
991  const char * /*mime_type*/,
992  const int32_t fd)
993 {
994  input_t *input = static_cast<input_t *>(data);
995  std::lock_guard lock{input->data_source_mutex};
996 
997  CLOG_INFO(LOG, 2, "send");
998 
999  const char *const buffer = input->data_source->buffer_out;
1000  if (write(fd, buffer, strlen(buffer)) < 0) {
1001  GHOST_PRINT("error writing to clipboard: " << std::strerror(errno) << std::endl);
1002  }
1003  close(fd);
1004 }
1005 
1006 static void data_source_handle_cancelled(void * /*data*/, struct wl_data_source *wl_data_source)
1007 {
1008  CLOG_INFO(LOG, 2, "cancelled");
1009  wl_data_source_destroy(wl_data_source);
1010 }
1011 
1019 static void data_source_handle_dnd_drop_performed(void * /*data*/,
1020  struct wl_data_source * /*wl_data_source*/)
1021 {
1022  CLOG_INFO(LOG, 2, "dnd_drop_performed");
1023 }
1024 
1032 static void data_source_handle_dnd_finished(void * /*data*/,
1033  struct wl_data_source * /*wl_data_source*/)
1034 {
1035  CLOG_INFO(LOG, 2, "dnd_finished");
1036 }
1037 
1045 static void data_source_handle_action(void * /*data*/,
1046  struct wl_data_source * /*wl_data_source*/,
1047  const uint32_t dnd_action)
1048 {
1049  CLOG_INFO(LOG, 2, "handle_action (dnd_action=%u)", dnd_action);
1050 }
1051 
1052 static const struct wl_data_source_listener data_source_listener = {
1059 };
1060 
1061 #undef LOG
1062 
1065 /* -------------------------------------------------------------------- */
1069 static CLG_LogRef LOG_WL_DATA_OFFER = {"ghost.wl.handle.data_offer"};
1070 #define LOG (&LOG_WL_DATA_OFFER)
1071 
1072 static void data_offer_handle_offer(void *data,
1073  struct wl_data_offer * /*wl_data_offer*/,
1074  const char *mime_type)
1075 {
1076  CLOG_INFO(LOG, 2, "offer (mime_type=%s)", mime_type);
1077  static_cast<data_offer_t *>(data)->types.insert(mime_type);
1078 }
1079 
1081  struct wl_data_offer * /*wl_data_offer*/,
1082  const uint32_t source_actions)
1083 {
1084  CLOG_INFO(LOG, 2, "source_actions (%u)", source_actions);
1085  static_cast<data_offer_t *>(data)->source_actions = source_actions;
1086 }
1087 
1088 static void data_offer_handle_action(void *data,
1089  struct wl_data_offer * /*wl_data_offer*/,
1090  const uint32_t dnd_action)
1091 {
1092  CLOG_INFO(LOG, 2, "actions (%u)", dnd_action);
1093  static_cast<data_offer_t *>(data)->dnd_action = dnd_action;
1094 }
1095 
1096 static const struct wl_data_offer_listener data_offer_listener = {
1100 };
1101 
1102 #undef LOG
1103 
1106 /* -------------------------------------------------------------------- */
1110 static CLG_LogRef LOG_WL_DATA_DEVICE = {"ghost.wl.handle.data_device"};
1111 #define LOG (&LOG_WL_DATA_DEVICE)
1112 
1113 static void data_device_handle_data_offer(void * /*data*/,
1114  struct wl_data_device * /*wl_data_device*/,
1115  struct wl_data_offer *id)
1116 {
1117  CLOG_INFO(LOG, 2, "data_offer");
1118 
1119  data_offer_t *data_offer = new data_offer_t;
1120  data_offer->id = id;
1121  wl_data_offer_add_listener(id, &data_offer_listener, data_offer);
1122 }
1123 
1124 static void data_device_handle_enter(void *data,
1125  struct wl_data_device * /*wl_data_device*/,
1126  const uint32_t serial,
1127  struct wl_surface *surface,
1128  const wl_fixed_t x,
1129  const wl_fixed_t y,
1130  struct wl_data_offer *id)
1131 {
1132  if (!ghost_wl_surface_own(surface)) {
1133  CLOG_INFO(LOG, 2, "enter (skipped)");
1134  return;
1135  }
1136  CLOG_INFO(LOG, 2, "enter");
1137 
1138  input_t *input = static_cast<input_t *>(data);
1139  std::lock_guard lock{input->data_offer_dnd_mutex};
1140 
1141  input->data_offer_dnd = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id));
1142  data_offer_t *data_offer = input->data_offer_dnd;
1143 
1144  data_offer->in_use.store(true);
1145  data_offer->dnd.xy[0] = x;
1146  data_offer->dnd.xy[1] = y;
1147 
1148  wl_data_offer_set_actions(id,
1149  WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY |
1150  WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE,
1151  WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY);
1152 
1153  for (const std::string &type : mime_preference_order) {
1154  wl_data_offer_accept(id, serial, type.c_str());
1155  }
1156 
1157  input->focus_dnd = surface;
1159 }
1160 
1161 static void data_device_handle_leave(void *data, struct wl_data_device * /*wl_data_device*/)
1162 {
1163  input_t *input = static_cast<input_t *>(data);
1164  std::lock_guard lock{input->data_offer_dnd_mutex};
1165 
1166  CLOG_INFO(LOG, 2, "leave");
1167 
1169  input->focus_dnd = nullptr;
1170 
1171  if (input->data_offer_dnd && !input->data_offer_dnd->in_use.load()) {
1172  wl_data_offer_destroy(input->data_offer_dnd->id);
1173  delete input->data_offer_dnd;
1174  input->data_offer_dnd = nullptr;
1175  }
1176 }
1177 
1179  struct wl_data_device * /*wl_data_device*/,
1180  const uint32_t /*time*/,
1181  const wl_fixed_t x,
1182  const wl_fixed_t y)
1183 {
1184  input_t *input = static_cast<input_t *>(data);
1185  std::lock_guard lock{input->data_offer_dnd_mutex};
1186 
1187  CLOG_INFO(LOG, 2, "motion");
1188 
1189  input->data_offer_dnd->dnd.xy[0] = x;
1190  input->data_offer_dnd->dnd.xy[1] = y;
1191 
1193 }
1194 
1195 static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_data_device*/)
1196 {
1197  input_t *input = static_cast<input_t *>(data);
1198  std::lock_guard lock{input->data_offer_dnd_mutex};
1199 
1200  CLOG_INFO(LOG, 2, "drop");
1201 
1202  data_offer_t *data_offer = input->data_offer_dnd;
1203 
1204  const std::string mime_receive = *std::find_first_of(mime_preference_order.begin(),
1205  mime_preference_order.end(),
1206  data_offer->types.begin(),
1207  data_offer->types.end());
1208 
1209  auto read_uris_fn = [](input_t *const input,
1210  data_offer_t *data_offer,
1211  wl_surface *surface,
1212  const std::string mime_receive) {
1213  const wl_fixed_t xy[2] = {UNPACK2(data_offer->dnd.xy)};
1214 
1215  const std::string data = read_pipe(data_offer, mime_receive, nullptr);
1216 
1217  wl_data_offer_finish(data_offer->id);
1218  wl_data_offer_destroy(data_offer->id);
1219 
1220  delete data_offer;
1221  data_offer = nullptr;
1222 
1223  GHOST_SystemWayland *const system = input->system;
1224 
1225  if (mime_receive == mime_text_uri) {
1226  static constexpr const char *file_proto = "file://";
1227  static constexpr const char *crlf = "\r\n";
1228 
1230  std::vector<std::string> uris;
1231 
1232  size_t pos = 0;
1233  while (true) {
1234  pos = data.find(file_proto, pos);
1235  const size_t start = pos + sizeof(file_proto) - 1;
1236  pos = data.find(crlf, pos);
1237  const size_t end = pos;
1238 
1239  if (pos == std::string::npos) {
1240  break;
1241  }
1242  uris.push_back(data.substr(start, end - start));
1243  }
1244 
1245  GHOST_TStringArray *flist = static_cast<GHOST_TStringArray *>(
1246  malloc(sizeof(GHOST_TStringArray)));
1247  flist->count = int(uris.size());
1248  flist->strings = static_cast<uint8_t **>(malloc(uris.size() * sizeof(uint8_t *)));
1249  for (size_t i = 0; i < uris.size(); i++) {
1250  flist->strings[i] = static_cast<uint8_t *>(malloc((uris[i].size() + 1) * sizeof(uint8_t)));
1251  memcpy(flist->strings[i], uris[i].data(), uris[i].size() + 1);
1252  }
1253 
1254  const wl_fixed_t scale = win->scale();
1255  system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(),
1258  win,
1259  wl_fixed_to_int(scale * xy[0]),
1260  wl_fixed_to_int(scale * xy[1]),
1261  flist));
1262  }
1263  else if (ELEM(mime_receive, mime_text_plain, mime_text_utf8)) {
1264  /* TODO: enable use of internal functions 'txt_insert_buf' and
1265  * 'text_update_edited' to behave like dropped text was pasted. */
1266  }
1267  wl_display_roundtrip(system->display());
1268  };
1269 
1270  /* Pass in `input->focus_dnd` instead of accessing it from `input` since the leave callback
1271  * (#data_device_leave) will clear the value once this function starts. */
1272  std::thread read_thread(read_uris_fn, input, data_offer, input->focus_dnd, mime_receive);
1273  read_thread.detach();
1274 }
1275 
1277  struct wl_data_device * /*wl_data_device*/,
1278  struct wl_data_offer *id)
1279 {
1280  input_t *input = static_cast<input_t *>(data);
1281 
1282  std::lock_guard lock{input->data_offer_copy_paste_mutex};
1283 
1284  data_offer_t *data_offer = input->data_offer_copy_paste;
1285 
1286  /* Delete old data offer. */
1287  if (data_offer != nullptr) {
1288  wl_data_offer_destroy(data_offer->id);
1289  delete data_offer;
1290  data_offer = nullptr;
1291  }
1292 
1293  if (id == nullptr) {
1294  CLOG_INFO(LOG, 2, "selection: (skipped)");
1295  return;
1296  }
1297  CLOG_INFO(LOG, 2, "selection");
1298 
1299  /* Get new data offer. */
1300  data_offer = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id));
1301  input->data_offer_copy_paste = data_offer;
1302 
1303  auto read_selection_fn = [](input_t *input) {
1304  GHOST_SystemWayland *const system = input->system;
1305  input->data_offer_copy_paste_mutex.lock();
1306 
1307  data_offer_t *data_offer = input->data_offer_copy_paste;
1308  std::string mime_receive;
1309  for (const std::string type : {mime_text_utf8, mime_text_plain}) {
1310  if (data_offer->types.count(type)) {
1311  mime_receive = type;
1312  break;
1313  }
1314  }
1315  const std::string data = read_pipe(
1316  data_offer, mime_receive, &input->data_offer_copy_paste_mutex);
1317 
1318  {
1319  std::lock_guard lock{system_selection_mutex};
1320  system->selection_set(data);
1321  }
1322  };
1323 
1324  std::thread read_thread(read_selection_fn, input);
1325  read_thread.detach();
1326 }
1327 
1328 static const struct wl_data_device_listener data_device_listener = {
1335 };
1336 
1337 #undef LOG
1338 
1341 /* -------------------------------------------------------------------- */
1345 static CLG_LogRef LOG_WL_CURSOR_BUFFER = {"ghost.wl.handle.cursor_buffer"};
1346 #define LOG (&LOG_WL_CURSOR_BUFFER)
1347 
1348 static void cursor_buffer_handle_release(void *data, struct wl_buffer *wl_buffer)
1349 {
1350  CLOG_INFO(LOG, 2, "release");
1351 
1352  cursor_t *cursor = static_cast<cursor_t *>(data);
1353  wl_buffer_destroy(wl_buffer);
1354 
1355  if (wl_buffer == cursor->wl_buffer) {
1356  /* The mapped buffer was from a custom cursor. */
1357  cursor->wl_buffer = nullptr;
1358  }
1359 }
1360 
1361 static const struct wl_buffer_listener cursor_buffer_listener = {
1363 };
1364 
1365 #undef LOG
1366 
1369 /* -------------------------------------------------------------------- */
1373 static CLG_LogRef LOG_WL_CURSOR_SURFACE = {"ghost.wl.handle.cursor_surface"};
1374 #define LOG (&LOG_WL_CURSOR_SURFACE)
1375 
1376 static bool update_cursor_scale(cursor_t &cursor,
1377  wl_shm *shm,
1378  input_state_pointer_t *input_state,
1379  wl_surface *cursor_surface)
1380 {
1381  int scale = 0;
1382  for (const output_t *output : input_state->outputs) {
1383  if (output->scale > scale) {
1384  scale = output->scale;
1385  }
1386  }
1387 
1388  if (scale > 0 && input_state->theme_scale != scale) {
1389  input_state->theme_scale = scale;
1390  if (!cursor.is_custom) {
1391  wl_surface_set_buffer_scale(cursor_surface, scale);
1392  }
1394  cursor.wl_theme = wl_cursor_theme_load(cursor.theme_name.c_str(), scale * cursor.size, shm);
1395  return true;
1396  }
1397  return false;
1398 }
1399 
1401  struct wl_surface *wl_surface,
1402  struct wl_output *output)
1403 {
1404  if (!ghost_wl_output_own(output)) {
1405  CLOG_INFO(LOG, 2, "handle_enter (skipped)");
1406  return;
1407  }
1408  CLOG_INFO(LOG, 2, "handle_enter");
1409 
1410  input_t *input = static_cast<input_t *>(data);
1412  const output_t *reg_output = ghost_wl_output_user_data(output);
1413  input_state->outputs.insert(reg_output);
1414  update_cursor_scale(input->cursor, input->system->shm(), input_state, wl_surface);
1415 }
1416 
1418  struct wl_surface *wl_surface,
1419  struct wl_output *output)
1420 {
1421  if (!(output && ghost_wl_output_own(output))) {
1422  CLOG_INFO(LOG, 2, "handle_leave (skipped)");
1423  return;
1424  }
1425  CLOG_INFO(LOG, 2, "handle_leave");
1426 
1427  input_t *input = static_cast<input_t *>(data);
1429  const output_t *reg_output = ghost_wl_output_user_data(output);
1430  input_state->outputs.erase(reg_output);
1431  update_cursor_scale(input->cursor, input->system->shm(), input_state, wl_surface);
1432 }
1433 
1434 static const struct wl_surface_listener cursor_surface_listener = {
1437 };
1438 
1439 #undef LOG
1440 
1443 /* -------------------------------------------------------------------- */
1447 static CLG_LogRef LOG_WL_POINTER = {"ghost.wl.handle.pointer"};
1448 #define LOG (&LOG_WL_POINTER)
1449 
1450 static void pointer_handle_enter(void *data,
1451  struct wl_pointer * /*wl_pointer*/,
1452  const uint32_t serial,
1453  struct wl_surface *surface,
1454  const wl_fixed_t surface_x,
1455  const wl_fixed_t surface_y)
1456 {
1457  if (!ghost_wl_surface_own(surface)) {
1458  CLOG_INFO(LOG, 2, "enter (skipped)");
1459  return;
1460  }
1461  CLOG_INFO(LOG, 2, "enter");
1462 
1464 
1465  win->activate();
1466 
1467  input_t *input = static_cast<input_t *>(data);
1468  input->cursor_source_serial = serial;
1469  input->pointer.serial = serial;
1470  input->pointer.xy[0] = surface_x;
1471  input->pointer.xy[1] = surface_y;
1472  input->pointer.wl_surface = surface;
1473 
1474  win->setCursorShape(win->getCursorShape());
1475 
1476  const wl_fixed_t scale = win->scale();
1477  input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
1479  win,
1480  wl_fixed_to_int(scale * input->pointer.xy[0]),
1481  wl_fixed_to_int(scale * input->pointer.xy[1]),
1483 }
1484 
1485 static void pointer_handle_leave(void *data,
1486  struct wl_pointer * /*wl_pointer*/,
1487  const uint32_t /*serial*/,
1488  struct wl_surface *surface)
1489 {
1490  /* First clear the `pointer.wl_surface`, since the window won't exist when closing the window. */
1491  static_cast<input_t *>(data)->pointer.wl_surface = nullptr;
1493  CLOG_INFO(LOG, 2, "leave");
1495  win->deactivate();
1496  }
1497  else {
1498  CLOG_INFO(LOG, 2, "leave (skipped)");
1499  }
1500 }
1501 
1502 static void pointer_handle_motion(void *data,
1503  struct wl_pointer * /*wl_pointer*/,
1504  const uint32_t /*time*/,
1505  const wl_fixed_t surface_x,
1506  const wl_fixed_t surface_y)
1507 {
1508  input_t *input = static_cast<input_t *>(data);
1509  input->pointer.xy[0] = surface_x;
1510  input->pointer.xy[1] = surface_y;
1511 
1512  if (wl_surface *focus_surface = input->pointer.wl_surface) {
1513  CLOG_INFO(LOG, 2, "motion");
1514  GHOST_WindowWayland *win = ghost_wl_surface_user_data(focus_surface);
1515  const wl_fixed_t scale = win->scale();
1516  input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
1518  win,
1519  wl_fixed_to_int(scale * input->pointer.xy[0]),
1520  wl_fixed_to_int(scale * input->pointer.xy[1]),
1522  }
1523  else {
1524  CLOG_INFO(LOG, 2, "motion (skipped)");
1525  }
1526 }
1527 
1528 static void pointer_handle_button(void *data,
1529  struct wl_pointer * /*wl_pointer*/,
1530  const uint32_t serial,
1531  const uint32_t /*time*/,
1532  const uint32_t button,
1533  const uint32_t state)
1534 {
1535  CLOG_INFO(LOG, 2, "button (button=%u, state=%u)", button, state);
1536 
1537  input_t *input = static_cast<input_t *>(data);
1539  switch (state) {
1540  case WL_POINTER_BUTTON_STATE_RELEASED:
1541  etype = GHOST_kEventButtonUp;
1542  break;
1543  case WL_POINTER_BUTTON_STATE_PRESSED:
1544  etype = GHOST_kEventButtonDown;
1545  break;
1546  }
1547 
1549  switch (button) {
1550  case BTN_LEFT:
1551  ebutton = GHOST_kButtonMaskLeft;
1552  break;
1553  case BTN_MIDDLE:
1554  ebutton = GHOST_kButtonMaskMiddle;
1555  break;
1556  case BTN_RIGHT:
1557  ebutton = GHOST_kButtonMaskRight;
1558  break;
1559  case BTN_SIDE:
1560  ebutton = GHOST_kButtonMaskButton4;
1561  break;
1562  case BTN_EXTRA:
1563  ebutton = GHOST_kButtonMaskButton5;
1564  break;
1565  case BTN_FORWARD:
1566  ebutton = GHOST_kButtonMaskButton6;
1567  break;
1568  case BTN_BACK:
1569  ebutton = GHOST_kButtonMaskButton7;
1570  break;
1571  }
1572 
1573  input->data_source_serial = serial;
1574  input->pointer.buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED);
1575 
1576  if (wl_surface *focus_surface = input->pointer.wl_surface) {
1577  GHOST_WindowWayland *win = ghost_wl_surface_user_data(focus_surface);
1578  input->system->pushEvent(new GHOST_EventButton(
1579  input->system->getMilliSeconds(), etype, win, ebutton, GHOST_TABLET_DATA_NONE));
1580  }
1581 }
1582 
1583 static void pointer_handle_axis(void * /*data*/,
1584  struct wl_pointer * /*wl_pointer*/,
1585  const uint32_t /*time*/,
1586  const uint32_t axis,
1587  const wl_fixed_t value)
1588 {
1589  CLOG_INFO(LOG, 2, "axis (axis=%u, value=%d)", axis, value);
1590 }
1591 
1592 static void pointer_handle_frame(void * /*data*/, struct wl_pointer * /*wl_pointer*/)
1593 {
1594  CLOG_INFO(LOG, 2, "frame");
1595 }
1596 static void pointer_handle_axis_source(void * /*data*/,
1597  struct wl_pointer * /*wl_pointer*/,
1598  uint32_t axis_source)
1599 {
1600  CLOG_INFO(LOG, 2, "axis_source (axis_source=%u)", axis_source);
1601 }
1602 static void pointer_handle_axis_stop(void * /*data*/,
1603  struct wl_pointer * /*wl_pointer*/,
1604  uint32_t /*time*/,
1605  uint32_t axis)
1606 {
1607  CLOG_INFO(LOG, 2, "axis_stop (axis=%u)", axis);
1608 }
1610  struct wl_pointer * /*wl_pointer*/,
1611  uint32_t axis,
1612  int32_t discrete)
1613 {
1614  CLOG_INFO(LOG, 2, "axis_discrete (axis=%u, discrete=%d)", axis, discrete);
1615 
1616  input_t *input = static_cast<input_t *>(data);
1617  if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) {
1618  return;
1619  }
1620 
1621  if (wl_surface *focus_surface = input->pointer.wl_surface) {
1622  GHOST_WindowWayland *win = ghost_wl_surface_user_data(focus_surface);
1623  input->system->pushEvent(new GHOST_EventWheel(
1624  input->system->getMilliSeconds(), win, std::signbit(discrete) ? +1 : -1));
1625  }
1626 }
1627 
1628 static const struct wl_pointer_listener pointer_listener = {
1638 };
1639 
1640 #undef LOG
1641 
1644 /* -------------------------------------------------------------------- */
1648 static CLG_LogRef LOG_WL_TABLET_TOOL = {"ghost.wl.handle.tablet_tool"};
1649 #define LOG (&LOG_WL_TABLET_TOOL)
1650 
1651 static void tablet_tool_handle_type(void *data,
1652  struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
1653  const uint32_t tool_type)
1654 {
1655  CLOG_INFO(LOG, 2, "type (type=%u)", tool_type);
1656 
1657  tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
1658 
1659  tool_input->data.Active = tablet_tool_map_type((enum zwp_tablet_tool_v2_type)tool_type);
1660 }
1661 
1662 static void tablet_tool_handle_hardware_serial(void * /*data*/,
1663  struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
1664  const uint32_t /*hardware_serial_hi*/,
1665  const uint32_t /*hardware_serial_lo*/)
1666 {
1667  CLOG_INFO(LOG, 2, "hardware_serial");
1668 }
1669 
1671  void * /*data*/,
1672  struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
1673  const uint32_t /*hardware_id_hi*/,
1674  const uint32_t /*hardware_id_lo*/)
1675 {
1676  CLOG_INFO(LOG, 2, "hardware_id_wacom");
1677 }
1678 
1679 static void tablet_tool_handle_capability(void * /*data*/,
1680  struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
1681  const uint32_t capability)
1682 {
1683  CLOG_INFO(LOG,
1684  2,
1685  "capability (tilt=%d, distance=%d, rotation=%d, slider=%d, wheel=%d)",
1686  (capability & ZWP_TABLET_TOOL_V2_CAPABILITY_TILT) != 0,
1687  (capability & ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE) != 0,
1688  (capability & ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION) != 0,
1689  (capability & ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER) != 0,
1690  (capability & ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL) != 0);
1691 }
1692 
1693 static void tablet_tool_handle_done(void * /*data*/,
1694  struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/)
1695 {
1696  CLOG_INFO(LOG, 2, "done");
1697 }
1698 static void tablet_tool_handle_removed(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2)
1699 {
1700  CLOG_INFO(LOG, 2, "removed");
1701 
1702  tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
1703  input_t *input = tool_input->input;
1704 
1705  if (tool_input->cursor_surface) {
1706  wl_surface_destroy(tool_input->cursor_surface);
1707  }
1708  input->tablet_tools.erase(zwp_tablet_tool_v2);
1709 
1710  delete tool_input;
1711 }
1713  struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
1714  const uint32_t serial,
1715  struct zwp_tablet_v2 * /*tablet*/,
1716  struct wl_surface *surface)
1717 {
1718  if (!ghost_wl_surface_own(surface)) {
1719  CLOG_INFO(LOG, 2, "proximity_in (skipped)");
1720  return;
1721  }
1722  CLOG_INFO(LOG, 2, "proximity_in");
1723 
1724  tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
1725  tool_input->proximity = true;
1726 
1727  input_t *input = tool_input->input;
1728  input->cursor_source_serial = serial;
1729  input->tablet.wl_surface = surface;
1730  input->tablet.serial = serial;
1731 
1732  input->data_source_serial = serial;
1733 
1734  /* Update #GHOST_TabletData. */
1735  GHOST_TabletData &td = tool_input->data;
1736  /* Reset, to avoid using stale tilt/pressure. */
1737  td.Xtilt = 0.0f;
1738  td.Ytilt = 0.0f;
1739  /* In case pressure isn't supported. */
1740  td.Pressure = 1.0f;
1741 
1742  GHOST_WindowWayland *win = ghost_wl_surface_user_data(input->tablet.wl_surface);
1743 
1744  win->activate();
1745 
1746  win->setCursorShape(win->getCursorShape());
1747 }
1749  struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/)
1750 {
1751  CLOG_INFO(LOG, 2, "proximity_out");
1752  tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
1753  /* Defer clearing the surface until the frame is handled.
1754  * Without this, the frame can not access the surface. */
1755  tool_input->proximity = false;
1756 }
1757 
1758 static void tablet_tool_handle_down(void *data,
1759  struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
1760  const uint32_t serial)
1761 {
1762  CLOG_INFO(LOG, 2, "down");
1763 
1764  tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
1765  input_t *input = tool_input->input;
1766  const GHOST_TButton ebutton = GHOST_kButtonMaskLeft;
1768 
1769  input->data_source_serial = serial;
1770  input->tablet.buttons.set(ebutton, true);
1771 
1772  if (wl_surface *focus_surface = input->tablet.wl_surface) {
1773  GHOST_WindowWayland *win = ghost_wl_surface_user_data(focus_surface);
1774  input->system->pushEvent(new GHOST_EventButton(
1775  input->system->getMilliSeconds(), etype, win, ebutton, tool_input->data));
1776  }
1777 }
1778 
1779 static void tablet_tool_handle_up(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/)
1780 {
1781  CLOG_INFO(LOG, 2, "up");
1782 
1783  tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
1784  input_t *input = tool_input->input;
1785  const GHOST_TButton ebutton = GHOST_kButtonMaskLeft;
1786  const GHOST_TEventType etype = GHOST_kEventButtonUp;
1787 
1788  input->tablet.buttons.set(ebutton, false);
1789 
1790  if (wl_surface *focus_surface = input->tablet.wl_surface) {
1791  GHOST_WindowWayland *win = ghost_wl_surface_user_data(focus_surface);
1792  input->system->pushEvent(new GHOST_EventButton(
1793  input->system->getMilliSeconds(), etype, win, ebutton, tool_input->data));
1794  }
1795 }
1796 
1798  struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
1799  const wl_fixed_t x,
1800  const wl_fixed_t y)
1801 {
1802  CLOG_INFO(LOG, 2, "motion");
1803 
1804  tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
1805  input_t *input = tool_input->input;
1806 
1807  input->tablet.xy[0] = x;
1808  input->tablet.xy[1] = y;
1809 
1810  /* NOTE: #tablet_tool_handle_frame generates the event (with updated pressure, tilt... etc). */
1811 }
1812 
1814  struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
1815  const uint32_t pressure)
1816 {
1817  const float pressure_unit = (float)pressure / 65535;
1818  CLOG_INFO(LOG, 2, "pressure (%.4f)", pressure_unit);
1819 
1820  tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
1821  GHOST_TabletData &td = tool_input->data;
1822  td.Pressure = pressure_unit;
1823 }
1824 static void tablet_tool_handle_distance(void * /*data*/,
1825  struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
1826  const uint32_t distance)
1827 {
1828  CLOG_INFO(LOG, 2, "distance (distance=%u)", distance);
1829 }
1830 
1831 static void tablet_tool_handle_tilt(void *data,
1832  struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
1833  const wl_fixed_t tilt_x,
1834  const wl_fixed_t tilt_y)
1835 {
1836  /* Map degrees to `-1.0..1.0`. */
1837  const float tilt_unit[2] = {
1838  (float)(wl_fixed_to_double(tilt_x) / 90.0),
1839  (float)(wl_fixed_to_double(tilt_y) / 90.0),
1840  };
1841  CLOG_INFO(LOG, 2, "tilt (x=%.4f, y=%.4f)", UNPACK2(tilt_unit));
1842  tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
1843  GHOST_TabletData &td = tool_input->data;
1844  td.Xtilt = tilt_unit[0];
1845  td.Ytilt = tilt_unit[1];
1846  CLAMP(td.Xtilt, -1.0f, 1.0f);
1847  CLAMP(td.Ytilt, -1.0f, 1.0f);
1848 }
1849 
1850 static void tablet_tool_handle_rotation(void * /*data*/,
1851  struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
1852  const wl_fixed_t degrees)
1853 {
1854  CLOG_INFO(LOG, 2, "rotation (degrees=%.4f)", wl_fixed_to_double(degrees));
1855 }
1856 
1857 static void tablet_tool_handle_slider(void * /*data*/,
1858  struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
1859  const int32_t position)
1860 {
1861  CLOG_INFO(LOG, 2, "slider (position=%d)", position);
1862 }
1863 static void tablet_tool_handle_wheel(void *data,
1864  struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
1865  const wl_fixed_t /*degrees*/,
1866  const int32_t clicks)
1867 {
1868  if (clicks == 0) {
1869  return;
1870  }
1871  CLOG_INFO(LOG, 2, "wheel (clicks=%d)", clicks);
1872 
1873  tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
1874  input_t *input = tool_input->input;
1875  if (wl_surface *focus_surface = input->tablet.wl_surface) {
1876  GHOST_WindowWayland *win = ghost_wl_surface_user_data(focus_surface);
1877  input->system->pushEvent(new GHOST_EventWheel(input->system->getMilliSeconds(), win, clicks));
1878  }
1879 }
1881  struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
1882  const uint32_t serial,
1883  const uint32_t button,
1884  const uint32_t state)
1885 {
1886  CLOG_INFO(LOG, 2, "button (button=%u, state=%u)", button, state);
1887 
1888  tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
1889  input_t *input = tool_input->input;
1890 
1892  switch (state) {
1893  case WL_POINTER_BUTTON_STATE_RELEASED:
1894  etype = GHOST_kEventButtonUp;
1895  break;
1896  case WL_POINTER_BUTTON_STATE_PRESSED:
1897  etype = GHOST_kEventButtonDown;
1898  break;
1899  }
1900 
1902  switch (button) {
1903  case BTN_STYLUS:
1904  ebutton = GHOST_kButtonMaskRight;
1905  break;
1906  case BTN_STYLUS2:
1907  ebutton = GHOST_kButtonMaskMiddle;
1908  break;
1909  case BTN_STYLUS3:
1910  ebutton = GHOST_kButtonMaskButton4;
1911  break;
1912  }
1913 
1914  input->data_source_serial = serial;
1915  input->tablet.buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED);
1916 
1917  if (wl_surface *focus_surface = input->tablet.wl_surface) {
1918  GHOST_WindowWayland *win = ghost_wl_surface_user_data(focus_surface);
1919  input->system->pushEvent(new GHOST_EventButton(
1920  input->system->getMilliSeconds(), etype, win, ebutton, tool_input->data));
1921  }
1922 }
1923 static void tablet_tool_handle_frame(void *data,
1924  struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
1925  const uint32_t /*time*/)
1926 {
1927  CLOG_INFO(LOG, 2, "frame");
1928 
1929  tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
1930  input_t *input = tool_input->input;
1931 
1932  /* No need to check the surfaces origin, it's already known to be owned by GHOST. */
1933  if (wl_surface *focus_surface = input->tablet.wl_surface) {
1934  GHOST_WindowWayland *win = ghost_wl_surface_user_data(focus_surface);
1935  const wl_fixed_t scale = win->scale();
1936  input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
1938  win,
1939  wl_fixed_to_int(scale * input->tablet.xy[0]),
1940  wl_fixed_to_int(scale * input->tablet.xy[1]),
1941  tool_input->data));
1942  if (tool_input->proximity == false) {
1943  win->setCursorShape(win->getCursorShape());
1944  }
1945  }
1946 
1947  if (tool_input->proximity == false) {
1948  input->tablet.wl_surface = nullptr;
1949  }
1950 }
1951 
1952 static const struct zwp_tablet_tool_v2_listener tablet_tool_listner = {
1972 };
1973 
1974 #undef LOG
1975 
1978 /* -------------------------------------------------------------------- */
1982 static CLG_LogRef LOG_WL_TABLET_SEAT = {"ghost.wl.handle.tablet_seat"};
1983 #define LOG (&LOG_WL_TABLET_SEAT)
1984 
1985 static void tablet_seat_handle_tablet_added(void * /*data*/,
1986  struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/,
1987  struct zwp_tablet_v2 *id)
1988 {
1989  CLOG_INFO(LOG, 2, "tablet_added (id=%p)", id);
1990 }
1991 
1993  struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/,
1994  struct zwp_tablet_tool_v2 *id)
1995 {
1996  CLOG_INFO(LOG, 2, "tool_added (id=%p)", id);
1997 
1998  input_t *input = static_cast<input_t *>(data);
1999  tablet_tool_input_t *tool_input = new tablet_tool_input_t();
2000  tool_input->input = input;
2001 
2002  /* Every tool has it's own cursor surface. */
2003  tool_input->cursor_surface = wl_compositor_create_surface(input->system->compositor());
2005 
2006  wl_surface_add_listener(tool_input->cursor_surface, &cursor_surface_listener, (void *)input);
2007 
2008  zwp_tablet_tool_v2_add_listener(id, &tablet_tool_listner, tool_input);
2009 
2010  input->tablet_tools.insert(id);
2011 }
2012 
2013 static void tablet_seat_handle_pad_added(void * /*data*/,
2014  struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/,
2015  struct zwp_tablet_pad_v2 *id)
2016 {
2017  CLOG_INFO(LOG, 2, "pad_added (id=%p)", id);
2018 }
2019 
2020 static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
2024 };
2025 
2026 #undef LOG
2027 
2030 /* -------------------------------------------------------------------- */
2034 static CLG_LogRef LOG_WL_KEYBOARD = {"ghost.wl.handle.keyboard"};
2035 #define LOG (&LOG_WL_KEYBOARD)
2036 
2037 static void keyboard_handle_keymap(void *data,
2038  struct wl_keyboard * /*wl_keyboard*/,
2039  const uint32_t format,
2040  const int32_t fd,
2041  const uint32_t size)
2042 {
2043  input_t *input = static_cast<input_t *>(data);
2044 
2045  if ((!data) || (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)) {
2046  CLOG_INFO(LOG, 2, "keymap (no data or wrong version)");
2047  close(fd);
2048  return;
2049  }
2050 
2051  char *map_str = static_cast<char *>(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0));
2052  if (map_str == MAP_FAILED) {
2053  close(fd);
2054  throw std::runtime_error("keymap mmap failed: " + std::string(std::strerror(errno)));
2055  }
2056 
2057  struct xkb_keymap *keymap = xkb_keymap_new_from_string(
2058  input->xkb_context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
2059  munmap(map_str, size);
2060  close(fd);
2061 
2062  if (!keymap) {
2063  CLOG_INFO(LOG, 2, "keymap (not found)");
2064  return;
2065  }
2066 
2067  CLOG_INFO(LOG, 2, "keymap");
2068 
2069  /* In practice we can assume `xkb_state_new` always succeeds. */
2070  xkb_state_unref(input->xkb_state);
2071  input->xkb_state = xkb_state_new(keymap);
2072 
2073  xkb_state_unref(input->xkb_state_empty);
2074  input->xkb_state_empty = xkb_state_new(keymap);
2075 
2076  xkb_state_unref(input->xkb_state_empty_with_numlock);
2077  input->xkb_state_empty_with_numlock = nullptr;
2078 
2079  {
2080  const xkb_mod_index_t mod2 = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_NUM);
2081  const xkb_mod_index_t num = xkb_keymap_mod_get_index(keymap, "NumLock");
2082  if (num != XKB_MOD_INVALID && mod2 != XKB_MOD_INVALID) {
2083  input->xkb_state_empty_with_numlock = xkb_state_new(keymap);
2084  xkb_state_update_mask(
2085  input->xkb_state_empty_with_numlock, (1 << mod2), 0, (1 << num), 0, 0, 0);
2086  }
2087  }
2088 
2089  xkb_keymap_unref(keymap);
2090 }
2091 
2098 static void keyboard_handle_enter(void *data,
2099  struct wl_keyboard * /*wl_keyboard*/,
2100  const uint32_t serial,
2101  struct wl_surface *surface,
2102  struct wl_array * /*keys*/)
2103 {
2104  if (!ghost_wl_surface_own(surface)) {
2105  CLOG_INFO(LOG, 2, "enter (skipped)");
2106  return;
2107  }
2108  CLOG_INFO(LOG, 2, "enter");
2109 
2110  input_t *input = static_cast<input_t *>(data);
2111  input->keyboard.serial = serial;
2112  input->keyboard.wl_surface = surface;
2113 }
2114 
2121 static void keyboard_handle_leave(void *data,
2122  struct wl_keyboard * /*wl_keyboard*/,
2123  const uint32_t /*serial*/,
2124  struct wl_surface *surface)
2125 {
2126  if (!(surface && ghost_wl_surface_own(surface))) {
2127  CLOG_INFO(LOG, 2, "leave (skipped)");
2128  return;
2129  }
2130  CLOG_INFO(LOG, 2, "leave");
2131 
2132  input_t *input = static_cast<input_t *>(data);
2133  input->keyboard.wl_surface = nullptr;
2134 
2135  /* Losing focus must stop repeating text. */
2136  if (input->key_repeat.timer) {
2138  }
2139 }
2140 
2146  struct xkb_state *xkb_state_empty,
2147  struct xkb_state *xkb_state_empty_with_numlock,
2148  const xkb_keycode_t key)
2149 {
2150  /* Use an empty keyboard state to access key symbol without modifiers. */
2151  xkb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state_empty, key);
2152 
2153  /* NOTE(@campbellbarton): Only perform the number-locked lookup as a fallback
2154  * when a number-pad key has been pressed. This is important as some key-maps use number lock
2155  * for switching other layers (in particular `de(neo_qwertz)` turns on layer-4), see: T96170.
2156  * Alternative solutions could be to inspect the layout however this could get involved
2157  * and turning on the number-lock is only needed for a limited set of keys. */
2158 
2159  /* Accounts for key-pad keys typically swapped for numbers when number-lock is enabled:
2160  * `Home Left Up Right Down Prior Page_Up Next Page_Dow End Begin Insert Delete`. */
2161  if (xkb_state_empty_with_numlock && (sym >= XKB_KEY_KP_Home && sym <= XKB_KEY_KP_Delete)) {
2162  const xkb_keysym_t sym_test = xkb_state_key_get_one_sym(xkb_state_empty_with_numlock, key);
2163  if (sym_test != XKB_KEY_NoSymbol) {
2164  sym = sym_test;
2165  }
2166  }
2167 
2168  return sym;
2169 }
2170 
2172 {
2173  GHOST_ASSERT(input->key_repeat.timer != nullptr, "Caller much check for timer");
2174  delete static_cast<key_repeat_payload_t *>(input->key_repeat.timer->getUserData());
2175  input->system->removeTimer(input->key_repeat.timer);
2176  input->key_repeat.timer = nullptr;
2177 }
2178 
2184 static void keyboard_handle_key_repeat_reset(input_t *input, const bool use_delay)
2185 {
2186  GHOST_ASSERT(input->key_repeat.timer != nullptr, "Caller much check for timer");
2187  GHOST_SystemWayland *system = input->system;
2188  GHOST_ITimerTask *timer = input->key_repeat.timer;
2189  GHOST_TimerProcPtr key_repeat_fn = timer->getTimerProc();
2190  GHOST_TUserDataPtr payload = input->key_repeat.timer->getUserData();
2191  input->system->removeTimer(input->key_repeat.timer);
2192  const uint64_t time_step = 1000 / input->key_repeat.rate;
2193  const uint64_t time_start = use_delay ? input->key_repeat.delay : time_step;
2194  input->key_repeat.timer = system->installTimer(time_start, time_step, key_repeat_fn, payload);
2195 }
2196 
2197 static void keyboard_handle_key(void *data,
2198  struct wl_keyboard * /*wl_keyboard*/,
2199  const uint32_t serial,
2200  const uint32_t /*time*/,
2201  const uint32_t key,
2202  const uint32_t state)
2203 {
2204  input_t *input = static_cast<input_t *>(data);
2205  const xkb_keycode_t key_code = key + EVDEV_OFFSET;
2206 
2207  const xkb_keysym_t sym = xkb_state_key_get_one_sym_without_modifiers(
2208  input->xkb_state_empty, input->xkb_state_empty_with_numlock, key_code);
2209  if (sym == XKB_KEY_NoSymbol) {
2210  CLOG_INFO(LOG, 2, "key (no symbol, skipped)");
2211  return;
2212  }
2213  CLOG_INFO(LOG, 2, "key");
2214 
2216  switch (state) {
2217  case WL_KEYBOARD_KEY_STATE_RELEASED:
2218  etype = GHOST_kEventKeyUp;
2219  break;
2220  case WL_KEYBOARD_KEY_STATE_PRESSED:
2221  etype = GHOST_kEventKeyDown;
2222  break;
2223  }
2224 
2225  struct key_repeat_payload_t *key_repeat_payload = nullptr;
2226 
2227  /* Delete previous timer. */
2228  if (input->key_repeat.timer) {
2229  enum { NOP = 1, RESET, CANCEL } timer_action = NOP;
2230  key_repeat_payload = static_cast<key_repeat_payload_t *>(
2231  input->key_repeat.timer->getUserData());
2232 
2233  if (input->key_repeat.rate == 0) {
2234  /* Repeat was disabled (unlikely but possible). */
2235  timer_action = CANCEL;
2236  }
2237  else if (key_code == key_repeat_payload->key_code) {
2238  /* Releasing the key that was held always cancels. */
2239  timer_action = CANCEL;
2240  }
2241  else if (xkb_keymap_key_repeats(xkb_state_get_keymap(input->xkb_state), key_code)) {
2242  if (etype == GHOST_kEventKeyDown) {
2243  /* Any other key-down always cancels (and may start it's own repeat timer). */
2244  timer_action = CANCEL;
2245  }
2246  else {
2247  /* Key-up from keys that were not repeating cause the repeat timer to pause.
2248  *
2249  * NOTE(@campbellbarton): This behavior isn't universal, some text input systems will
2250  * stop the repeat entirely. Choose to pause repeat instead as this is what GTK/WIN32 do,
2251  * and it fits better for keyboard input that isn't related to text entry. */
2252  timer_action = RESET;
2253  }
2254  }
2255 
2256  switch (timer_action) {
2257  case NOP: {
2258  /* Don't add a new timer, leave the existing timer owning this `key_repeat_payload`. */
2259  key_repeat_payload = nullptr;
2260  break;
2261  }
2262  case RESET: {
2263  /* The payload will be added again. */
2264  input->system->removeTimer(input->key_repeat.timer);
2265  input->key_repeat.timer = nullptr;
2266  break;
2267  }
2268  case CANCEL: {
2269  delete key_repeat_payload;
2270  key_repeat_payload = nullptr;
2271 
2272  input->system->removeTimer(input->key_repeat.timer);
2273  input->key_repeat.timer = nullptr;
2274  break;
2275  }
2276  }
2277  }
2278 
2280  char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'};
2281  if (etype == GHOST_kEventKeyDown) {
2282  xkb_state_key_get_utf8(input->xkb_state, key_code, utf8_buf, sizeof(utf8_buf));
2283  }
2284 
2285  input->data_source_serial = serial;
2286 
2287  if (wl_surface *focus_surface = input->keyboard.wl_surface) {
2288  GHOST_IWindow *win = ghost_wl_surface_user_data(focus_surface);
2289  input->system->pushEvent(
2290  new GHOST_EventKey(input->system->getMilliSeconds(), etype, win, gkey, false, utf8_buf));
2291  }
2292 
2293  /* An existing payload means the key repeat timer is reset and will be added again. */
2294  if (key_repeat_payload == nullptr) {
2295  /* Start timer for repeating key, if applicable. */
2296  if ((input->key_repeat.rate > 0) && (etype == GHOST_kEventKeyDown) &&
2297  xkb_keymap_key_repeats(xkb_state_get_keymap(input->xkb_state), key_code)) {
2298  key_repeat_payload = new key_repeat_payload_t({
2299  .input = input,
2300  .key_code = key_code,
2301  .key_data = {.gkey = gkey},
2302  });
2303  }
2304  }
2305 
2306  if (key_repeat_payload) {
2307  auto key_repeat_fn = [](GHOST_ITimerTask *task, uint64_t /*time*/) {
2308  struct key_repeat_payload_t *payload = static_cast<key_repeat_payload_t *>(
2309  task->getUserData());
2310 
2311  input_t *input = payload->input;
2312  if (wl_surface *focus_surface = input->keyboard.wl_surface) {
2313  GHOST_IWindow *win = ghost_wl_surface_user_data(focus_surface);
2314  GHOST_SystemWayland *system = input->system;
2315  /* Calculate this value every time in case modifier keys are pressed. */
2316  char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'};
2317  xkb_state_key_get_utf8(input->xkb_state, payload->key_code, utf8_buf, sizeof(utf8_buf));
2318  system->pushEvent(new GHOST_EventKey(system->getMilliSeconds(),
2320  win,
2321  payload->key_data.gkey,
2322  true,
2323  utf8_buf));
2324  }
2325  };
2326  input->key_repeat.timer = input->system->installTimer(
2327  input->key_repeat.delay, 1000 / input->key_repeat.rate, key_repeat_fn, key_repeat_payload);
2328  }
2329 }
2330 
2332  struct wl_keyboard * /*wl_keyboard*/,
2333  const uint32_t /*serial*/,
2334  const uint32_t mods_depressed,
2335  const uint32_t mods_latched,
2336  const uint32_t mods_locked,
2337  const uint32_t group)
2338 {
2339  CLOG_INFO(LOG, 2, "modifiers");
2340 
2341  input_t *input = static_cast<input_t *>(data);
2342  xkb_state_update_mask(input->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
2343 
2344  /* A modifier changed so reset the timer,
2345  * see comment in #keyboard_handle_key regarding this behavior. */
2346  if (input->key_repeat.timer) {
2348  }
2349 }
2350 
2352  struct wl_keyboard * /*wl_keyboard*/,
2353  const int32_t rate,
2354  const int32_t delay)
2355 {
2356  CLOG_INFO(LOG, 2, "info (rate=%d, delay=%d)", rate, delay);
2357 
2358  input_t *input = static_cast<input_t *>(data);
2359  input->key_repeat.rate = rate;
2360  input->key_repeat.delay = delay;
2361 
2362  /* Unlikely possible this setting changes while repeating. */
2363  if (input->key_repeat.timer) {
2365  }
2366 }
2367 
2368 static const struct wl_keyboard_listener keyboard_listener = {
2375 };
2376 
2377 #undef LOG
2378 
2381 /* -------------------------------------------------------------------- */
2385 static CLG_LogRef LOG_WL_SEAT = {"ghost.wl.handle.seat"};
2386 #define LOG (&LOG_WL_SEAT)
2387 
2388 static void seat_handle_capabilities(void *data,
2389  struct wl_seat *wl_seat,
2390  const uint32_t capabilities)
2391 {
2392  CLOG_INFO(LOG,
2393  2,
2394  "capabilities (pointer=%d, keyboard=%d, touch=%d)",
2395  (capabilities & WL_SEAT_CAPABILITY_POINTER) != 0,
2396  (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) != 0,
2397  (capabilities & WL_SEAT_CAPABILITY_TOUCH) != 0);
2398 
2399  input_t *input = static_cast<input_t *>(data);
2400  input->wl_pointer = nullptr;
2401  input->wl_keyboard = nullptr;
2402 
2403  if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
2404  input->wl_pointer = wl_seat_get_pointer(wl_seat);
2405  input->cursor.wl_surface = wl_compositor_create_surface(input->system->compositor());
2406  input->cursor.visible = true;
2407  input->cursor.wl_buffer = nullptr;
2408  if (!get_cursor_settings(input->cursor.theme_name, input->cursor.size)) {
2409  input->cursor.theme_name = std::string();
2410  input->cursor.size = default_cursor_size;
2411  }
2412  wl_pointer_add_listener(input->wl_pointer, &pointer_listener, data);
2413 
2414  wl_surface_add_listener(input->cursor.wl_surface, &cursor_surface_listener, data);
2415  ghost_wl_surface_tag_cursor_pointer(input->cursor.wl_surface);
2416  }
2417 
2418  if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
2419  input->wl_keyboard = wl_seat_get_keyboard(wl_seat);
2420  wl_keyboard_add_listener(input->wl_keyboard, &keyboard_listener, data);
2421  }
2422 }
2423 
2424 static void seat_handle_name(void *data, struct wl_seat * /*wl_seat*/, const char *name)
2425 {
2426  CLOG_INFO(LOG, 2, "name (name=\"%s\")", name);
2427  static_cast<input_t *>(data)->name = std::string(name);
2428 }
2429 
2430 static const struct wl_seat_listener seat_listener = {
2433 };
2434 
2435 #undef LOG
2436 
2439 /* -------------------------------------------------------------------- */
2443 static CLG_LogRef LOG_WL_XDG_OUTPUT = {"ghost.wl.handle.xdg_output"};
2444 #define LOG (&LOG_WL_XDG_OUTPUT)
2445 
2447  struct zxdg_output_v1 * /*xdg_output*/,
2448  const int32_t x,
2449  const int32_t y)
2450 {
2451  CLOG_INFO(LOG, 2, "logical_position [%d, %d]", x, y);
2452 
2453  output_t *output = static_cast<output_t *>(data);
2454  output->position_logical[0] = x;
2455  output->position_logical[1] = y;
2456  output->has_position_logical = true;
2457 }
2458 
2460  struct zxdg_output_v1 * /*xdg_output*/,
2461  const int32_t width,
2462  const int32_t height)
2463 {
2464  CLOG_INFO(LOG, 2, "logical_size [%d, %d]", width, height);
2465 
2466  output_t *output = static_cast<output_t *>(data);
2467  if (output->size_logical[0] != 0 && output->size_logical[1] != 0) {
2468  /* Original comment from SDL. */
2469  /* FIXME(@flibit): GNOME has a bug where the logical size does not account for
2470  * scale, resulting in bogus viewport sizes.
2471  *
2472  * Until this is fixed, validate that _some_ kind of scaling is being
2473  * done (we can't match exactly because fractional scaling can't be
2474  * detected otherwise), then override if necessary. */
2475  if ((output->size_logical[0] == width) && (output->scale_fractional == wl_fixed_from_int(1))) {
2476  GHOST_PRINT("xdg_output scale did not match, overriding with wl_output scale");
2477 
2478 #ifdef USE_GNOME_CONFINE_HACK
2479  /* Use a bug in GNOME to check GNOME is in use. If the bug is fixed this won't cause an issue
2480  * as T98793 has been fixed up-stream too, but not in a release at time of writing. */
2481  use_gnome_confine_hack = true;
2482 #endif
2483 
2484  return;
2485  }
2486  }
2487 
2488  output->size_logical[0] = width;
2489  output->size_logical[1] = height;
2490  output->has_size_logical = true;
2491 }
2492 
2493 static void xdg_output_handle_done(void *data, struct zxdg_output_v1 * /*xdg_output*/)
2494 {
2495  CLOG_INFO(LOG, 2, "done");
2496  /* NOTE: `xdg-output.done` events are deprecated and only apply below version 3 of the protocol.
2497  * `wl-output.done` event will be emitted in version 3 or higher. */
2498  output_t *output = static_cast<output_t *>(data);
2499  if (zxdg_output_v1_get_version(output->xdg_output) < 3) {
2500  output_handle_done(data, output->wl_output);
2501  }
2502 }
2503 
2504 static void xdg_output_handle_name(void * /*data*/,
2505  struct zxdg_output_v1 * /*xdg_output*/,
2506  const char *name)
2507 {
2508  CLOG_INFO(LOG, 2, "name (name=\"%s\")", name);
2509 }
2510 
2511 static void xdg_output_handle_description(void * /*data*/,
2512  struct zxdg_output_v1 * /*xdg_output*/,
2513  const char *description)
2514 {
2515  CLOG_INFO(LOG, 2, "description (description=\"%s\")", description);
2516 }
2517 
2518 static const struct zxdg_output_v1_listener xdg_output_listener = {
2524 };
2525 
2526 #undef LOG
2527 
2530 /* -------------------------------------------------------------------- */
2534 static CLG_LogRef LOG_WL_OUTPUT = {"ghost.wl.handle.output"};
2535 #define LOG (&LOG_WL_OUTPUT)
2536 
2537 static void output_handle_geometry(void *data,
2538  struct wl_output * /*wl_output*/,
2539  const int32_t /*x*/,
2540  const int32_t /*y*/,
2541  const int32_t physical_width,
2542  const int32_t physical_height,
2543  const int32_t /*subpixel*/,
2544  const char *make,
2545  const char *model,
2546  const int32_t transform)
2547 {
2548  CLOG_INFO(LOG,
2549  2,
2550  "geometry (make=\"%s\", model=\"%s\", transform=%d, size=[%d, %d])",
2551  make,
2552  model,
2553  transform,
2554  physical_width,
2555  physical_height);
2556 
2557  output_t *output = static_cast<output_t *>(data);
2558  output->transform = transform;
2559  output->make = std::string(make);
2560  output->model = std::string(model);
2561  output->size_mm[0] = physical_width;
2562  output->size_mm[1] = physical_height;
2563 }
2564 
2565 static void output_handle_mode(void *data,
2566  struct wl_output * /*wl_output*/,
2567  const uint32_t flags,
2568  const int32_t width,
2569  const int32_t height,
2570  const int32_t /*refresh*/)
2571 {
2572  if ((flags & WL_OUTPUT_MODE_CURRENT) == 0) {
2573  CLOG_INFO(LOG, 2, "mode (skipped)");
2574  return;
2575  }
2576  CLOG_INFO(LOG, 2, "mode (size=[%d, %d], flags=%u)", width, height, flags);
2577 
2578  output_t *output = static_cast<output_t *>(data);
2579  output->size_native[0] = width;
2580  output->size_native[1] = height;
2581 
2582  /* Don't rotate this yet, `wl-output` coordinates are transformed in
2583  * handle_done and `xdg-output` coordinates are pre-transformed. */
2584  if (!output->has_size_logical) {
2585  output->size_logical[0] = width;
2586  output->size_logical[1] = height;
2587  }
2588 }
2589 
2598 static void output_handle_done(void *data, struct wl_output * /*wl_output*/)
2599 {
2600  CLOG_INFO(LOG, 2, "done");
2601 
2602  output_t *output = static_cast<output_t *>(data);
2603  int32_t size_native[2];
2604  if (output->transform & WL_OUTPUT_TRANSFORM_90) {
2605  size_native[0] = output->size_native[1];
2606  size_native[1] = output->size_native[0];
2607  }
2608  else {
2609  size_native[0] = output->size_native[0];
2610  size_native[1] = output->size_native[1];
2611  }
2612 
2613  /* If `xdg-output` is present, calculate the true scale of the desktop */
2614  if (output->has_size_logical) {
2615 
2616  /* NOTE: it's not necessary to divide these values by their greatest-common-denominator
2617  * as even a 64k screen resolution doesn't approach overflowing an `int32_t`. */
2618 
2619  GHOST_ASSERT(size_native[0] && output->size_logical[0],
2620  "Screen size values were not set when they were expected to be.");
2621 
2622  output->scale_fractional = wl_fixed_from_int(size_native[0]) / output->size_logical[0];
2623  output->has_scale_fractional = true;
2624  }
2625 }
2626 
2627 static void output_handle_scale(void *data, struct wl_output * /*wl_output*/, const int32_t factor)
2628 {
2629  CLOG_INFO(LOG, 2, "scale");
2630  static_cast<output_t *>(data)->scale = factor;
2631 
2632  if (window_manager) {
2633  for (GHOST_IWindow *iwin : window_manager->getWindows()) {
2634  GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(iwin);
2636  /* TODO(@campbellbarton): support refreshing the UI when the DPI changes.
2637  * There are glitches when resizing the monitor which would be nice to solve. */
2638  }
2639  }
2640 }
2641 
2642 static const struct wl_output_listener output_listener = {
2647 };
2648 
2649 #undef LOG
2650 
2653 /* -------------------------------------------------------------------- */
2657 #ifndef WITH_GHOST_WAYLAND_LIBDECOR
2658 
2659 static CLG_LogRef LOG_WL_XDG_WM_BASE = {"ghost.wl.handle.output"};
2660 # define LOG (&LOG_WL_XDG_WM_BASE)
2661 
2662 static void shell_handle_ping(void * /*data*/,
2663  struct xdg_wm_base *xdg_wm_base,
2664  const uint32_t serial)
2665 {
2666  CLOG_INFO(LOG, 2, "ping");
2667  xdg_wm_base_pong(xdg_wm_base, serial);
2668 }
2669 
2670 static const struct xdg_wm_base_listener shell_listener = {
2672 };
2673 
2674 # undef LOG
2675 
2676 #endif /* !WITH_GHOST_WAYLAND_LIBDECOR. */
2677 
2680 /* -------------------------------------------------------------------- */
2684 #ifdef WITH_GHOST_WAYLAND_LIBDECOR
2685 
2686 static CLG_LogRef LOG_WL_LIBDECOR = {"ghost.wl.handle.libdecor"};
2687 # define LOG (&LOG_WL_LIBDECOR)
2688 
2689 static void decor_handle_error(struct libdecor * /*context*/,
2690  enum libdecor_error error,
2691  const char *message)
2692 {
2693  CLOG_INFO(LOG, 2, "error (id=%d, message=%s)", error, message);
2694 
2695  (void)(error);
2696  (void)(message);
2697  GHOST_PRINT("decoration error (" << error << "): " << message << std::endl);
2698  exit(EXIT_FAILURE);
2699 }
2700 
2701 static struct libdecor_interface libdecor_interface = {
2702  decor_handle_error,
2703 };
2704 
2705 # undef LOG
2706 
2707 #endif /* WITH_GHOST_WAYLAND_LIBDECOR. */
2708 
2711 /* -------------------------------------------------------------------- */
2715 static CLG_LogRef LOG_WL_REGISTRY = {"ghost.wl.handle.registry"};
2716 #define LOG (&LOG_WL_REGISTRY)
2717 
2718 static void global_handle_add(void *data,
2719  struct wl_registry *wl_registry,
2720  const uint32_t name,
2721  const char *interface,
2722  const uint32_t version)
2723 {
2724  /* Log last since it can be noted if the interface was handled or not. */
2725  bool found = true;
2726 
2727  struct display_t *display = static_cast<struct display_t *>(data);
2728  if (!strcmp(interface, wl_compositor_interface.name)) {
2729  display->compositor = static_cast<wl_compositor *>(
2730  wl_registry_bind(wl_registry, name, &wl_compositor_interface, 3));
2731  }
2732 #ifdef WITH_GHOST_WAYLAND_LIBDECOR
2733  /* Pass. */
2734 #else
2735  else if (!strcmp(interface, xdg_wm_base_interface.name)) {
2736  display->xdg_shell = static_cast<xdg_wm_base *>(
2737  wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 1));
2738  xdg_wm_base_add_listener(display->xdg_shell, &shell_listener, nullptr);
2739  }
2740  else if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name)) {
2741  display->xdg_decoration_manager = static_cast<zxdg_decoration_manager_v1 *>(
2742  wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1));
2743  }
2744 #endif /* !WITH_GHOST_WAYLAND_LIBDECOR. */
2745  else if (!strcmp(interface, zxdg_output_manager_v1_interface.name)) {
2746  display->xdg_output_manager = static_cast<zxdg_output_manager_v1 *>(
2747  wl_registry_bind(wl_registry, name, &zxdg_output_manager_v1_interface, 2));
2748  for (output_t *output : display->outputs) {
2749  output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager,
2750  output->wl_output);
2751  zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output);
2752  }
2753  }
2754  else if (!strcmp(interface, wl_output_interface.name)) {
2755  output_t *output = new output_t;
2756  output->wl_output = static_cast<wl_output *>(
2757  wl_registry_bind(wl_registry, name, &wl_output_interface, 2));
2758  ghost_wl_output_tag(output->wl_output);
2759  wl_output_set_user_data(output->wl_output, output);
2760 
2761  display->outputs.push_back(output);
2762  wl_output_add_listener(output->wl_output, &output_listener, output);
2763 
2764  if (display->xdg_output_manager) {
2765  output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager,
2766  output->wl_output);
2767  zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output);
2768  }
2769  }
2770  else if (!strcmp(interface, wl_seat_interface.name)) {
2771  input_t *input = new input_t;
2772  input->system = display->system;
2773  input->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
2774  input->data_source = new data_source_t;
2775  input->wl_seat = static_cast<wl_seat *>(
2776  wl_registry_bind(wl_registry, name, &wl_seat_interface, 5));
2777  display->inputs.push_back(input);
2778  wl_seat_add_listener(input->wl_seat, &seat_listener, input);
2779  }
2780  else if (!strcmp(interface, wl_shm_interface.name)) {
2781  display->shm = static_cast<wl_shm *>(
2782  wl_registry_bind(wl_registry, name, &wl_shm_interface, 1));
2783  }
2784  else if (!strcmp(interface, wl_data_device_manager_interface.name)) {
2785  display->data_device_manager = static_cast<wl_data_device_manager *>(
2786  wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, 3));
2787  }
2788  else if (!strcmp(interface, zwp_tablet_manager_v2_interface.name)) {
2789  display->tablet_manager = static_cast<zwp_tablet_manager_v2 *>(
2790  wl_registry_bind(wl_registry, name, &zwp_tablet_manager_v2_interface, 1));
2791  }
2792  else if (!strcmp(interface, zwp_relative_pointer_manager_v1_interface.name)) {
2793  display->relative_pointer_manager = static_cast<zwp_relative_pointer_manager_v1 *>(
2794  wl_registry_bind(wl_registry, name, &zwp_relative_pointer_manager_v1_interface, 1));
2795  }
2796  else if (!strcmp(interface, zwp_pointer_constraints_v1_interface.name)) {
2797  display->pointer_constraints = static_cast<zwp_pointer_constraints_v1 *>(
2798  wl_registry_bind(wl_registry, name, &zwp_pointer_constraints_v1_interface, 1));
2799  }
2800  else {
2801  found = false;
2802  }
2803 
2804  CLOG_INFO(LOG,
2805  2,
2806  "add %s(interface=%s, version=%u, name=%u)",
2807  found ? "" : "(skipped), ",
2808  interface,
2809  version,
2810  name);
2811 }
2812 
2822 static void global_handle_remove(void * /*data*/,
2823  struct wl_registry * /*wl_registry*/,
2824  const uint32_t name)
2825 {
2826  CLOG_INFO(LOG, 2, "remove (name=%u)", name);
2827 }
2828 
2829 static const struct wl_registry_listener registry_listener = {
2832 };
2833 
2834 #undef LOG
2835 
2838 /* -------------------------------------------------------------------- */
2845 {
2847 
2848  d->system = this;
2849  /* Connect to the Wayland server. */
2850  d->display = wl_display_connect(nullptr);
2851  if (!d->display) {
2852  display_destroy(d);
2853  throw std::runtime_error("Wayland: unable to connect to display!");
2854  }
2855 
2856  /* Register interfaces. */
2857  struct wl_registry *registry = wl_display_get_registry(d->display);
2858  wl_registry_add_listener(registry, &registry_listener, d);
2859  /* Call callback for registry listener. */
2861  /* Call callbacks for registered listeners. */
2863  wl_registry_destroy(registry);
2864 
2865 #ifdef WITH_GHOST_WAYLAND_LIBDECOR
2866  d->decor_context = libdecor_new(d->display, &libdecor_interface);
2867  if (!d->decor_context) {
2868  display_destroy(d);
2869  throw std::runtime_error("Wayland: unable to create window decorations!");
2870  }
2871 #else
2872  if (!d->xdg_shell) {
2873  display_destroy(d);
2874  throw std::runtime_error("Wayland: unable to access xdg_shell!");
2875  }
2876 #endif
2877 
2878  /* Register data device per seat for IPC between Wayland clients. */
2879  if (d->data_device_manager) {
2880  for (input_t *input : d->inputs) {
2881  input->data_device = wl_data_device_manager_get_data_device(d->data_device_manager,
2882  input->wl_seat);
2883  wl_data_device_add_listener(input->data_device, &data_device_listener, input);
2884  }
2885  }
2886 
2887  if (d->tablet_manager) {
2888  for (input_t *input : d->inputs) {
2889  input->tablet_seat = zwp_tablet_manager_v2_get_tablet_seat(d->tablet_manager,
2890  input->wl_seat);
2891  zwp_tablet_seat_v2_add_listener(input->tablet_seat, &tablet_seat_listener, input);
2892  }
2893  }
2894 }
2895 
2897 {
2898  display_destroy(d);
2899 }
2900 
2901 bool GHOST_SystemWayland::processEvents(bool waitForEvent)
2902 {
2903  const bool fired = getTimerManager()->fireTimers(getMilliSeconds());
2904 
2905  if (waitForEvent) {
2907  }
2908  else {
2910  }
2911 
2912  return fired || (getEventManager()->getNumEvents() > 0);
2913 }
2914 
2916 {
2917  return 0;
2918 }
2919 
2921 {
2922  if (UNLIKELY(d->inputs.empty())) {
2923  return GHOST_kFailure;
2924  }
2925 
2926  static const xkb_state_component mods_all = xkb_state_component(
2927  XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_MODS_LOCKED |
2928  XKB_STATE_MODS_EFFECTIVE);
2929 
2930  bool val;
2931 
2932  /* NOTE: XKB doesn't seem to differentiate between left/right modifiers. */
2933 
2934  val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_SHIFT, mods_all) == 1;
2935  keys.set(GHOST_kModifierKeyLeftShift, val);
2936  keys.set(GHOST_kModifierKeyRightShift, val);
2937 
2938  val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_ALT, mods_all) == 1;
2939  keys.set(GHOST_kModifierKeyLeftAlt, val);
2940  keys.set(GHOST_kModifierKeyRightAlt, val);
2941 
2942  val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_CTRL, mods_all) == 1;
2945 
2946  val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_LOGO, mods_all) == 1;
2947  keys.set(GHOST_kModifierKeyOS, val);
2948 
2949  val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_NUM, mods_all) == 1;
2950  keys.set(GHOST_kModifierKeyNum, val);
2951 
2952  return GHOST_kSuccess;
2953 }
2954 
2956 {
2957  if (UNLIKELY(d->inputs.empty())) {
2958  return GHOST_kFailure;
2959  }
2960  input_t *input = d->inputs[0];
2962  if (!input_state) {
2963  return GHOST_kFailure;
2964  }
2965 
2966  buttons = input_state->buttons;
2967  return GHOST_kSuccess;
2968 }
2969 
2970 char *GHOST_SystemWayland::getClipboard(bool /*selection*/) const
2971 {
2972  char *clipboard = static_cast<char *>(malloc((selection.size() + 1)));
2973  memcpy(clipboard, selection.data(), selection.size() + 1);
2974  return clipboard;
2975 }
2976 
2977 void GHOST_SystemWayland::putClipboard(const char *buffer, bool /*selection*/) const
2978 {
2979  if (UNLIKELY(!d->data_device_manager || d->inputs.empty())) {
2980  return;
2981  }
2982 
2983  input_t *input = d->inputs[0];
2984 
2985  std::lock_guard lock{input->data_source_mutex};
2986 
2987  data_source_t *data_source = input->data_source;
2988 
2989  /* Copy buffer. */
2990  free(data_source->buffer_out);
2991  const size_t buffer_size = strlen(buffer) + 1;
2992  data_source->buffer_out = static_cast<char *>(malloc(buffer_size));
2993  std::memcpy(data_source->buffer_out, buffer, buffer_size);
2994 
2995  data_source->data_source = wl_data_device_manager_create_data_source(d->data_device_manager);
2996 
2997  wl_data_source_add_listener(data_source->data_source, &data_source_listener, input);
2998 
2999  for (const std::string &type : mime_send) {
3000  wl_data_source_offer(data_source->data_source, type.c_str());
3001  }
3002 
3003  if (input->data_device) {
3004  wl_data_device_set_selection(
3005  input->data_device, data_source->data_source, input->data_source_serial);
3006  }
3007 }
3008 
3010 {
3011  return d ? uint8_t(d->outputs.size()) : 0;
3012 }
3013 
3015  const input_state_pointer_t *input_state,
3016  const GHOST_WindowWayland *win,
3017  int32_t &x,
3018  int32_t &y)
3019 {
3020  const wl_fixed_t scale = win->scale();
3021  x = wl_fixed_to_int(scale * input_state->xy[0]);
3022  y = wl_fixed_to_int(scale * input_state->xy[1]);
3023  return GHOST_kSuccess;
3024 }
3025 
3027  GHOST_WindowWayland *win,
3028  const int32_t x,
3029  const int32_t y)
3030 {
3031  /* NOTE: WAYLAND doesn't support warping the cursor.
3032  * However when grab is enabled, we already simulate a cursor location
3033  * so that can be set to a new location. */
3034  if (!input->relative_pointer) {
3035  return GHOST_kFailure;
3036  }
3037  const wl_fixed_t scale = win->scale();
3038  const wl_fixed_t xy_next[2] = {
3039  wl_fixed_from_int(x) / scale,
3040  wl_fixed_from_int(y) / scale,
3041  };
3042 
3043  /* As the cursor was "warped" generate an event at the new location. */
3045 
3046  return GHOST_kSuccess;
3047 }
3048 
3050  int32_t &x,
3051  int32_t &y) const
3052 {
3053  if (UNLIKELY(d->inputs.empty())) {
3054  return GHOST_kFailure;
3055  }
3056  input_t *input = d->inputs[0];
3058  if (!input_state || !input_state->wl_surface) {
3059  return GHOST_kFailure;
3060  }
3061  const GHOST_WindowWayland *win = static_cast<const GHOST_WindowWayland *>(window);
3062  return getCursorPositionClientRelative_impl(input_state, win, x, y);
3063 }
3064 
3066  const int32_t x,
3067  const int32_t y)
3068 {
3069  if (UNLIKELY(d->inputs.empty())) {
3070  return GHOST_kFailure;
3071  }
3072  input_t *input = d->inputs[0];
3073  GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(window);
3075 }
3076 
3078 {
3079  if (UNLIKELY(d->inputs.empty())) {
3080  return GHOST_kFailure;
3081  }
3082  input_t *input = d->inputs[0];
3084  if (!input_state) {
3085  return GHOST_kFailure;
3086  }
3087 
3088  if (wl_surface *focus_surface = input_state->wl_surface) {
3089  GHOST_WindowWayland *win = ghost_wl_surface_user_data(focus_surface);
3090  return getCursorPositionClientRelative_impl(input_state, win, x, y);
3091  }
3092  return GHOST_kFailure;
3093 }
3094 
3096 {
3097  if (UNLIKELY(d->inputs.empty())) {
3098  return GHOST_kFailure;
3099  }
3100  input_t *input = d->inputs[0];
3101 
3102  /* Intentionally different from `getCursorPosition` which supports both tablet & pointer.
3103  * In the case of setting the cursor location, tablets don't support this. */
3104  if (wl_surface *focus_surface = input->pointer.wl_surface) {
3105  GHOST_WindowWayland *win = ghost_wl_surface_user_data(focus_surface);
3107  }
3108  return GHOST_kFailure;
3109 }
3110 
3112 {
3113  if (getNumDisplays() == 0) {
3114  return;
3115  }
3116  /* We assume first output as main. */
3117  width = uint32_t(d->outputs[0]->size_native[0]);
3118  height = uint32_t(d->outputs[0]->size_native[1]);
3119 }
3120 
3122 {
3123  int32_t xy_min[2] = {INT32_MAX, INT32_MAX};
3124  int32_t xy_max[2] = {INT32_MIN, INT32_MIN};
3125 
3126  for (const output_t *output : d->outputs) {
3127  int32_t xy[2] = {0, 0};
3128  if (output->has_position_logical) {
3129  xy[0] = output->position_logical[0];
3130  xy[1] = output->position_logical[1];
3131  }
3132  xy_min[0] = std::min(xy_min[0], xy[0]);
3133  xy_min[1] = std::min(xy_min[1], xy[1]);
3134  xy_max[0] = std::max(xy_max[0], xy[0] + output->size_native[0]);
3135  xy_max[1] = std::max(xy_max[1], xy[1] + output->size_native[1]);
3136  }
3137 
3138  width = xy_max[0] - xy_min[0];
3139  height = xy_max[1] - xy_min[1];
3140 }
3141 
3143  struct wl_display *wl_display,
3144  wl_egl_window *egl_window)
3145 {
3147 
3148  for (int minor = 6; minor >= 0; --minor) {
3149  context = new GHOST_ContextEGL(system,
3150  false,
3151  EGLNativeWindowType(egl_window),
3152  EGLNativeDisplayType(wl_display),
3153  EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
3154  4,
3155  minor,
3158  EGL_OPENGL_API);
3159 
3160  if (context->initializeDrawingContext()) {
3161  return context;
3162  }
3163  delete context;
3164  }
3165 
3166  context = new GHOST_ContextEGL(system,
3167  false,
3168  EGLNativeWindowType(egl_window),
3169  EGLNativeDisplayType(wl_display),
3170  EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
3171  3,
3172  3,
3175  EGL_OPENGL_API);
3176 
3177  if (context->initializeDrawingContext()) {
3178  return context;
3179  }
3180  delete context;
3181  return nullptr;
3182 }
3183 
3185 {
3186  /* Create new off-screen window. */
3187  wl_surface *wl_surface = wl_compositor_create_surface(compositor());
3188  wl_egl_window *egl_window = wl_surface ? wl_egl_window_create(wl_surface, 1, 1) : nullptr;
3189 
3190  GHOST_Context *context = createOffscreenContext_impl(this, d->display, egl_window);
3191 
3192  if (!context) {
3193  GHOST_PRINT("Cannot create off-screen EGL context" << std::endl);
3194  if (wl_surface) {
3195  wl_surface_destroy(wl_surface);
3196  }
3197  if (egl_window) {
3198  wl_egl_window_destroy(egl_window);
3199  }
3200  return nullptr;
3201  }
3202 
3203  wl_surface_set_user_data(wl_surface, egl_window);
3204  context->setUserData(wl_surface);
3205 
3206  return context;
3207 }
3208 
3210 {
3211  struct wl_surface *wl_surface = (struct wl_surface *)((GHOST_Context *)context)->getUserData();
3212  wl_egl_window *egl_window = (wl_egl_window *)wl_surface_get_user_data(wl_surface);
3213  wl_egl_window_destroy(egl_window);
3214  wl_surface_destroy(wl_surface);
3215 
3216  delete context;
3217 
3218  return GHOST_kSuccess;
3219 }
3220 
3222  const int32_t left,
3223  const int32_t top,
3224  const uint32_t width,
3225  const uint32_t height,
3226  const GHOST_TWindowState state,
3228  const GHOST_GLSettings glSettings,
3229  const bool exclusive,
3230  const bool is_dialog,
3231  const GHOST_IWindow *parentWindow)
3232 {
3233  /* Globally store pointer to window manager. */
3234  if (!window_manager) {
3236  }
3237 
3239  this,
3240  title,
3241  left,
3242  top,
3243  width,
3244  height,
3245  state,
3246  parentWindow,
3247  type,
3248  is_dialog,
3249  ((glSettings.flags & GHOST_glStereoVisual) != 0),
3250  exclusive);
3251 
3252  if (window) {
3253  if (window->getValid()) {
3254  m_windowManager->addWindow(window);
3257  }
3258  else {
3259  delete window;
3260  window = nullptr;
3261  }
3262  }
3263 
3264  return window;
3265 }
3266 
3273 static void cursor_buffer_show(const input_t *input)
3274 {
3275  const cursor_t *c = &input->cursor;
3276 
3277  if (input->wl_pointer) {
3278  const int scale = c->is_custom ? c->custom_scale : input->pointer.theme_scale;
3279  const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale;
3280  const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale;
3281  if (input->wl_pointer) {
3282  wl_pointer_set_cursor(
3283  input->wl_pointer, input->pointer.serial, c->wl_surface, hotspot_x, hotspot_y);
3284  }
3285  }
3286 
3287  if (!input->tablet_tools.empty()) {
3288  const int scale = c->is_custom ? c->custom_scale : input->tablet.theme_scale;
3289  const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale;
3290  const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale;
3291  for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) {
3292  tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(
3293  zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2));
3294  zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2,
3295  input->tablet.serial,
3296  tool_input->cursor_surface,
3297  hotspot_x,
3298  hotspot_y);
3299  }
3300  }
3301 }
3302 
3309 static void cursor_buffer_hide(const input_t *input)
3310 {
3311  wl_pointer_set_cursor(input->wl_pointer, input->pointer.serial, nullptr, 0, 0);
3312  for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) {
3313  zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, input->tablet.serial, nullptr, 0, 0);
3314  }
3315 }
3316 
3320 static int cursor_buffer_compatible_scale_from_image(const struct wl_cursor_image *wl_image,
3321  int scale)
3322 {
3323  const int32_t image_size_x = int32_t(wl_image->width);
3324  const int32_t image_size_y = int32_t(wl_image->height);
3325  while (scale > 1) {
3326  if ((image_size_x % scale) == 0 && (image_size_y % scale) == 0) {
3327  break;
3328  }
3329  scale -= 1;
3330  }
3331  return scale;
3332 }
3333 
3335  wl_buffer *buffer,
3336  struct wl_surface *wl_surface,
3337  const int scale)
3338 {
3339  const wl_cursor_image *wl_image = &input->cursor.wl_image;
3340  const int32_t image_size_x = int32_t(wl_image->width);
3341  const int32_t image_size_y = int32_t(wl_image->height);
3342  GHOST_ASSERT((image_size_x % scale) == 0 && (image_size_y % scale) == 0,
3343  "The size must be a multiple of the scale!");
3344 
3345  wl_surface_set_buffer_scale(wl_surface, scale);
3346  wl_surface_attach(wl_surface, buffer, 0, 0);
3347  wl_surface_damage(wl_surface, 0, 0, image_size_x, image_size_y);
3348  wl_surface_commit(wl_surface);
3349 }
3350 
3351 static void cursor_buffer_set(const input_t *input, wl_buffer *buffer)
3352 {
3353  const cursor_t *c = &input->cursor;
3354  const wl_cursor_image *wl_image = &input->cursor.wl_image;
3355  const bool visible = (c->visible && c->is_hardware);
3356 
3357  /* This is a requirement of WAYLAND, when this isn't the case,
3358  * it causes Blender's window to close intermittently. */
3359  if (input->wl_pointer) {
3361  wl_image, c->is_custom ? c->custom_scale : input->pointer.theme_scale);
3362  const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale;
3363  const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale;
3364  cursor_buffer_set_surface_impl(input, buffer, c->wl_surface, scale);
3365  wl_pointer_set_cursor(input->wl_pointer,
3366  input->pointer.serial,
3367  visible ? c->wl_surface : nullptr,
3368  hotspot_x,
3369  hotspot_y);
3370  }
3371 
3372  /* Set the cursor for all tablet tools as well. */
3373  if (!input->tablet_tools.empty()) {
3375  wl_image, c->is_custom ? c->custom_scale : input->tablet.theme_scale);
3376  const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale;
3377  const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale;
3378  for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) {
3379  tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(
3380  zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2));
3382  zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2,
3383  input->tablet.serial,
3384  visible ? tool_input->cursor_surface : nullptr,
3385  hotspot_x,
3386  hotspot_y);
3387  }
3388  }
3389 }
3390 
3395 };
3396 
3398  const bool visible,
3399  const bool is_hardware,
3400  const enum eCursorSetMode set_mode)
3401 {
3402  cursor_t *cursor = &input->cursor;
3403  const bool was_visible = cursor->is_hardware && cursor->visible;
3404  const bool use_visible = is_hardware && visible;
3405 
3406  if (set_mode == CURSOR_VISIBLE_ALWAYS_SET) {
3407  /* Pass. */
3408  }
3409  else if ((set_mode == CURSOR_VISIBLE_ONLY_SHOW)) {
3410  if (!use_visible) {
3411  return;
3412  }
3413  }
3414  else if ((set_mode == CURSOR_VISIBLE_ONLY_HIDE)) {
3415  if (use_visible) {
3416  return;
3417  }
3418  }
3419 
3420  if (use_visible) {
3421  if (!was_visible) {
3423  }
3424  }
3425  else {
3426  if (was_visible) {
3428  }
3429  }
3430  cursor->visible = visible;
3431  cursor->is_hardware = is_hardware;
3432 }
3433 
3434 static bool cursor_is_software(const GHOST_TGrabCursorMode mode, const bool use_software_confine)
3435 {
3436  if (mode == GHOST_kGrabWrap) {
3437  return true;
3438  }
3439 #ifdef USE_GNOME_CONFINE_HACK
3440  if (mode == GHOST_kGrabNormal) {
3441  if (use_software_confine) {
3442  return true;
3443  }
3444  }
3445 #else
3446  (void)use_software_confine;
3447 #endif
3448  return false;
3449 }
3450 
3452 {
3453  if (UNLIKELY(d->inputs.empty())) {
3454  return GHOST_kFailure;
3455  }
3456  auto cursor_find = cursors.find(shape);
3457  const char *cursor_name = (cursor_find == cursors.end()) ?
3459  (*cursor_find).second;
3460 
3461  input_t *input = d->inputs[0];
3462  cursor_t *c = &input->cursor;
3463 
3464  if (!c->wl_theme) {
3465  /* The cursor surface hasn't entered an output yet. Initialize theme with scale 1. */
3466  c->wl_theme = wl_cursor_theme_load(
3467  c->theme_name.c_str(), c->size, d->inputs[0]->system->shm());
3468  }
3469 
3470  wl_cursor *cursor = wl_cursor_theme_get_cursor(c->wl_theme, cursor_name);
3471 
3472  if (!cursor) {
3473  GHOST_PRINT("cursor '" << cursor_name << "' does not exist" << std::endl);
3474  return GHOST_kFailure;
3475  }
3476 
3477  struct wl_cursor_image *image = cursor->images[0];
3478  struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
3479  if (!buffer) {
3480  return GHOST_kFailure;
3481  }
3482 
3483  c->visible = true;
3484  c->is_custom = false;
3485  c->wl_buffer = buffer;
3486  c->wl_image = *image;
3487 
3489 
3490  return GHOST_kSuccess;
3491 }
3492 
3494 {
3495  auto cursor_find = cursors.find(cursorShape);
3496  if (cursor_find == cursors.end()) {
3497  return GHOST_kFailure;
3498  }
3499  const char *value = (*cursor_find).second;
3500  if (*value == '\0') {
3501  return GHOST_kFailure;
3502  }
3503  return GHOST_kSuccess;
3504 }
3505 
3507  uint8_t *mask,
3508  const int sizex,
3509  const int sizey,
3510  const int hotX,
3511  const int hotY,
3512  const bool /*canInvertColor*/)
3513 {
3514  if (UNLIKELY(d->inputs.empty())) {
3515  return GHOST_kFailure;
3516  }
3517 
3518  cursor_t *cursor = &d->inputs[0]->cursor;
3519 
3520  if (cursor->custom_data) {
3521  munmap(cursor->custom_data, cursor->custom_data_size);
3522  cursor->custom_data = nullptr;
3523  cursor->custom_data_size = 0; /* Not needed, but the value is no longer meaningful. */
3524  }
3525 
3526  const int32_t size_xy[2] = {sizex, sizey};
3528  d->shm, size_xy, WL_SHM_FORMAT_ARGB8888, &cursor->custom_data, &cursor->custom_data_size);
3529  if (buffer == nullptr) {
3530  return GHOST_kFailure;
3531  }
3532 
3533  wl_buffer_add_listener(buffer, &cursor_buffer_listener, cursor);
3534 
3535  static constexpr uint32_t black = 0xFF000000;
3536  static constexpr uint32_t white = 0xFFFFFFFF;
3537  static constexpr uint32_t transparent = 0x00000000;
3538 
3539  uint8_t datab = 0, maskb = 0;
3540  uint32_t *pixel;
3541 
3542  for (int y = 0; y < sizey; ++y) {
3543  pixel = &static_cast<uint32_t *>(cursor->custom_data)[y * sizex];
3544  for (int x = 0; x < sizex; ++x) {
3545  if ((x % 8) == 0) {
3546  datab = *bitmap++;
3547  maskb = *mask++;
3548 
3549  /* Reverse bit order. */
3550  datab = uint8_t((datab * 0x0202020202ULL & 0x010884422010ULL) % 1023);
3551  maskb = uint8_t((maskb * 0x0202020202ULL & 0x010884422010ULL) % 1023);
3552  }
3553 
3554  if (maskb & 0x80) {
3555  *pixel++ = (datab & 0x80) ? white : black;
3556  }
3557  else {
3558  *pixel++ = (datab & 0x80) ? white : transparent;
3559  }
3560  datab <<= 1;
3561  maskb <<= 1;
3562  }
3563  }
3564 
3565  cursor->visible = true;
3566  cursor->is_custom = true;
3567  cursor->custom_scale = 1; /* TODO: support Hi-DPI custom cursors. */
3568  cursor->wl_buffer = buffer;
3569  cursor->wl_image.width = uint32_t(sizex);
3570  cursor->wl_image.height = uint32_t(sizey);
3571  cursor->wl_image.hotspot_x = uint32_t(hotX);
3572  cursor->wl_image.hotspot_y = uint32_t(hotY);
3573 
3575 
3576  return GHOST_kSuccess;
3577 }
3578 
3580 {
3581  cursor_t *cursor = &d->inputs[0]->cursor;
3582  if (cursor->custom_data == nullptr) {
3583  return GHOST_kFailure;
3584  }
3585  if (!cursor->is_custom) {
3586  return GHOST_kFailure;
3587  }
3588 
3589  bitmap->data_size[0] = cursor->wl_image.width;
3590  bitmap->data_size[1] = cursor->wl_image.height;
3591 
3592  bitmap->hot_spot[0] = cursor->wl_image.hotspot_x;
3593  bitmap->hot_spot[1] = cursor->wl_image.hotspot_y;
3594 
3595  bitmap->data = (uint8_t *)static_cast<void *>(cursor->custom_data);
3596 
3597  return GHOST_kSuccess;
3598 }
3599 
3601 {
3602  if (UNLIKELY(d->inputs.empty())) {
3603  return GHOST_kFailure;
3604  }
3605 
3606  input_t *input = d->inputs[0];
3607  cursor_visible_set(input, visible, input->cursor.is_hardware, CURSOR_VISIBLE_ALWAYS_SET);
3608  return GHOST_kSuccess;
3609 }
3610 
3612 {
3613  /* WAYLAND doesn't support setting the cursor position directly,
3614  * this is an intentional choice, forcing us to use a software cursor in this case. */
3615  return false;
3616 }
3617 
3619 {
3620  /* WAYLAND doesn't support accessing the window position. */
3621  return false;
3622 }
3623 
3625 {
3626  if (UNLIKELY(d->inputs.empty())) {
3627  return false;
3628  }
3629 
3630 #ifdef USE_GNOME_CONFINE_HACK
3631  input_t *input = d->inputs[0];
3632  const bool use_software_confine = input->use_pointer_software_confine;
3633 #else
3634  const bool use_software_confine = false;
3635 #endif
3636 
3637  return cursor_is_software(mode, use_software_confine);
3638 }
3639 
3640 #ifdef USE_GNOME_CONFINE_HACK
3642  wl_surface *surface)
3643 {
3644 # ifndef USE_GNOME_CONFINE_HACK_ALWAYS_ON
3645  if (use_gnome_confine_hack == false) {
3646  return false;
3647  }
3648 # endif
3649  if (mode != GHOST_kGrabNormal) {
3650  return false;
3651  }
3653  if (!win) {
3654  return false;
3655  }
3656 
3657 # ifndef USE_GNOME_CONFINE_HACK_ALWAYS_ON
3658  if (win->scale() <= 1) {
3659  return false;
3660  }
3661 # endif
3662  return true;
3663 }
3664 #endif
3665 
3667  const bool use_software_confine)
3668 {
3669  /* Initialize all members. */
3670  const struct input_grab_state_t grab_state = {
3671  /* Warping happens to require software cursor which also hides. */
3672  .use_lock = ELEM(mode, GHOST_kGrabWrap, GHOST_kGrabHide) || use_software_confine,
3673  .use_confine = (mode == GHOST_kGrabNormal) && (use_software_confine == false),
3674  };
3675  return grab_state;
3676 }
3677 
3680 /* -------------------------------------------------------------------- */
3684 static const char *ghost_wl_output_tag_id = "GHOST-output";
3685 static const char *ghost_wl_surface_tag_id = "GHOST-window";
3686 static const char *ghost_wl_surface_cursor_pointer_tag_id = "GHOST-cursor-pointer";
3687 static const char *ghost_wl_surface_cursor_tablet_tag_id = "GHOST-cursor-tablet";
3688 
3689 bool ghost_wl_output_own(const struct wl_output *output)
3690 {
3691  return wl_proxy_get_tag((struct wl_proxy *)output) == &ghost_wl_output_tag_id;
3692 }
3693 
3694 bool ghost_wl_surface_own(const struct wl_surface *surface)
3695 {
3696  return wl_proxy_get_tag((struct wl_proxy *)surface) == &ghost_wl_surface_tag_id;
3697 }
3698 
3699 bool ghost_wl_surface_own_cursor_pointer(const struct wl_surface *surface)
3700 {
3701  return wl_proxy_get_tag((struct wl_proxy *)surface) == &ghost_wl_surface_cursor_pointer_tag_id;
3702 }
3703 
3704 bool ghost_wl_surface_own_cursor_tablet(const struct wl_surface *surface)
3705 {
3706  return wl_proxy_get_tag((struct wl_proxy *)surface) == &ghost_wl_surface_cursor_tablet_tag_id;
3707 }
3708 
3709 void ghost_wl_output_tag(struct wl_output *output)
3710 {
3711  wl_proxy_set_tag((struct wl_proxy *)output, &ghost_wl_output_tag_id);
3712 }
3713 
3714 void ghost_wl_surface_tag(struct wl_surface *surface)
3715 {
3716  wl_proxy_set_tag((struct wl_proxy *)surface, &ghost_wl_surface_tag_id);
3717 }
3718 
3720 {
3722 }
3723 
3725 {
3727 }
3728 
3731 /* -------------------------------------------------------------------- */
3738 {
3739  return d->display;
3740 }
3741 
3743 {
3744  return d->compositor;
3745 }
3746 
3747 #ifdef WITH_GHOST_WAYLAND_LIBDECOR
3748 
3749 libdecor *GHOST_SystemWayland::decor_context()
3750 {
3751  return d->decor_context;
3752 }
3753 
3754 #else /* WITH_GHOST_WAYLAND_LIBDECOR */
3755 
3757 {
3758  return d->xdg_shell;
3759 }
3760 
3761 zxdg_decoration_manager_v1 *GHOST_SystemWayland::xdg_decoration_manager()
3762 {
3763  return d->xdg_decoration_manager;
3764 }
3765 
3766 #endif /* !WITH_GHOST_WAYLAND_LIBDECOR */
3767 
3768 const std::vector<output_t *> &GHOST_SystemWayland::outputs() const
3769 {
3770  return d->outputs;
3771 }
3772 
3774 {
3775  return d->shm;
3776 }
3777 
3780 /* -------------------------------------------------------------------- */
3785 {
3786  GHOST_ASSERT(wl_output, "output must not be NULL");
3787  GHOST_ASSERT(ghost_wl_output_own(wl_output), "output is not owned by GHOST");
3788  output_t *output = static_cast<output_t *>(wl_output_get_user_data(wl_output));
3789  return output;
3790 }
3791 
3793 {
3794  GHOST_ASSERT(surface, "surface must not be NULL");
3795  GHOST_ASSERT(ghost_wl_surface_own(surface), "surface is not owned by GHOST");
3796  GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(wl_surface_get_user_data(surface));
3797  return win;
3798 }
3799 
3802 /* -------------------------------------------------------------------- */
3808 void GHOST_SystemWayland::selection_set(const std::string &selection)
3809 {
3810  this->selection = selection;
3811 }
3812 
3814 {
3815 #define SURFACE_CLEAR_PTR(surface_test) \
3816  if (surface_test == surface) { \
3817  surface_test = nullptr; \
3818  } \
3819  ((void)0);
3820 
3821  /* Only clear window surfaces (not cursors, off-screen surfaces etc). */
3822  for (input_t *input : d->inputs) {
3823  SURFACE_CLEAR_PTR(input->pointer.wl_surface);
3824  SURFACE_CLEAR_PTR(input->tablet.wl_surface);
3825  SURFACE_CLEAR_PTR(input->keyboard.wl_surface);
3826  SURFACE_CLEAR_PTR(input->focus_dnd);
3827  }
3828 #undef SURFACE_CLEAR_PTR
3829 }
3830 
3832  const GHOST_TGrabCursorMode mode_current,
3833  int32_t init_grab_xy[2],
3834  const GHOST_Rect *wrap_bounds,
3835  const GHOST_TAxisFlag wrap_axis,
3836  wl_surface *surface,
3837  const int scale)
3838 {
3839  /* Ignore, if the required protocols are not supported. */
3841  return GHOST_kFailure;
3842  }
3843 
3844  if (UNLIKELY(d->inputs.empty())) {
3845  return GHOST_kFailure;
3846  }
3847  /* No change, success. */
3848  if (mode == mode_current) {
3849  return GHOST_kSuccess;
3850  }
3851 
3852  input_t *input = d->inputs[0];
3853 
3854 #ifdef USE_GNOME_CONFINE_HACK
3855  const bool was_software_confine = input->use_pointer_software_confine;
3856  const bool use_software_confine = setCursorGrab_use_software_confine(mode, surface);
3857 #else
3858  const bool was_software_confine = false;
3859  const bool use_software_confine = false;
3860 #endif
3861 
3862  const struct input_grab_state_t grab_state_prev = input_grab_state_from_mode(
3863  mode_current, was_software_confine);
3864  const struct input_grab_state_t grab_state_next = input_grab_state_from_mode(
3865  mode, use_software_confine);
3866 
3867  /* Check for wrap as #supportsCursorWarp isn't supported. */
3868  const bool use_visible = !(ELEM(mode, GHOST_kGrabHide, GHOST_kGrabWrap) || use_software_confine);
3869  const bool is_hardware_cursor = !cursor_is_software(mode, use_software_confine);
3870 
3871  /* Only hide so the cursor is not made visible before it's location is restored.
3872  * This function is called again at the end of this function which only shows. */
3873  cursor_visible_set(input, use_visible, is_hardware_cursor, CURSOR_VISIBLE_ONLY_HIDE);
3874 
3875  /* Switching from one grab mode to another,
3876  * in this case disable the current locks as it makes logic confusing,
3877  * postpone changing the cursor to avoid flickering. */
3878  if (!grab_state_next.use_lock) {
3879  if (input->relative_pointer) {
3880  zwp_relative_pointer_v1_destroy(input->relative_pointer);
3881  input->relative_pointer = nullptr;
3882  }
3883  if (input->locked_pointer) {
3884  /* Potentially add a motion event so the application has updated X/Y coordinates. */
3885  int32_t xy_motion[2] = {0, 0};
3886  bool xy_motion_create_event = false;
3887 
3888  /* Request location to restore to. */
3889  if (mode_current == GHOST_kGrabWrap) {
3890  /* Since this call is initiated by Blender, we can be sure the window wasn't closed
3891  * by logic outside this function - as the window was needed to make this call. */
3892  int32_t xy_next[2] = {UNPACK2(input->pointer.xy)};
3893 
3894  GHOST_Rect bounds_scale;
3895 
3896  bounds_scale.m_l = wl_fixed_from_int(wrap_bounds->m_l) / scale;
3897  bounds_scale.m_t = wl_fixed_from_int(wrap_bounds->m_t) / scale;
3898  bounds_scale.m_r = wl_fixed_from_int(wrap_bounds->m_r) / scale;
3899  bounds_scale.m_b = wl_fixed_from_int(wrap_bounds->m_b) / scale;
3900 
3901  bounds_scale.wrapPoint(UNPACK2(xy_next), 0, wrap_axis);
3902 
3903  /* Push an event so the new location is registered. */
3904  if ((xy_next[0] != input->pointer.xy[0]) || (xy_next[1] != input->pointer.xy[1])) {
3905  xy_motion[0] = xy_next[0];
3906  xy_motion[1] = xy_next[1];
3907  xy_motion_create_event = true;
3908  }
3909  input->pointer.xy[0] = xy_next[0];
3910  input->pointer.xy[1] = xy_next[1];
3911 
3912  zwp_locked_pointer_v1_set_cursor_position_hint(input->locked_pointer, UNPACK2(xy_next));
3913  wl_surface_commit(surface);
3914  }
3915  else if (mode_current == GHOST_kGrabHide) {
3916  if ((init_grab_xy[0] != input->grab_lock_xy[0]) ||
3917  (init_grab_xy[1] != input->grab_lock_xy[1])) {
3918  const wl_fixed_t xy_next[2] = {
3919  wl_fixed_from_int(init_grab_xy[0]) / scale,
3920  wl_fixed_from_int(init_grab_xy[1]) / scale,
3921  };
3922  zwp_locked_pointer_v1_set_cursor_position_hint(input->locked_pointer, UNPACK2(xy_next));
3923  wl_surface_commit(surface);
3924 
3925  /* NOTE(@campbellbarton): The new cursor position is a hint,
3926  * it's possible the hint is ignored. It doesn't seem like there is a good way to
3927  * know if the hint will be used or not, at least not immediately. */
3928  xy_motion[0] = xy_next[0];
3929  xy_motion[1] = xy_next[1];
3930  xy_motion_create_event = true;
3931  }
3932  }
3933 #ifdef USE_GNOME_CONFINE_HACK
3934  else if (mode_current == GHOST_kGrabNormal) {
3935  if (was_software_confine) {
3936  zwp_locked_pointer_v1_set_cursor_position_hint(input->locked_pointer,
3937  UNPACK2(input->pointer.xy));
3938  wl_surface_commit(surface);
3939  }
3940  }
3941 #endif
3942 
3943  if (xy_motion_create_event) {
3944  input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
3947  wl_fixed_to_int(scale * xy_motion[0]),
3948  wl_fixed_to_int(scale * xy_motion[1]),
3950  }
3951 
3952  zwp_locked_pointer_v1_destroy(input->locked_pointer);
3953  input->locked_pointer = nullptr;
3954  }
3955  }
3956 
3957  if (!grab_state_next.use_confine) {
3958  if (input->confined_pointer) {
3959  zwp_confined_pointer_v1_destroy(input->confined_pointer);
3960  input->confined_pointer = nullptr;
3961  }
3962  }
3963 
3964  if (mode != GHOST_kGrabDisable) {
3965  if (grab_state_next.use_lock) {
3966  if (!grab_state_prev.use_lock) {
3967  /* TODO(@campbellbarton): As WAYLAND does not support warping the pointer it may not be
3968  * possible to support #GHOST_kGrabWrap by pragmatically settings it's coordinates.
3969  * An alternative could be to draw the cursor in software (and hide the real cursor),
3970  * or just accept a locked cursor on WAYLAND. */
3971  input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(
3972  d->relative_pointer_manager, input->wl_pointer);
3973  zwp_relative_pointer_v1_add_listener(
3974  input->relative_pointer, &relative_pointer_listener, input);
3975  input->locked_pointer = zwp_pointer_constraints_v1_lock_pointer(
3977  surface,
3978  input->wl_pointer,
3979  nullptr,
3980  ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
3981  }
3982  if (mode == GHOST_kGrabHide) {
3983  /* Set the initial position to detect any changes when un-grabbing,
3984  * otherwise the unlocked cursor defaults to un-locking in-place. */
3985  init_grab_xy[0] = wl_fixed_to_int(scale * input->pointer.xy[0]);
3986  init_grab_xy[1] = wl_fixed_to_int(scale * input->pointer.xy[1]);
3987  input->grab_lock_xy[0] = init_grab_xy[0];
3988  input->grab_lock_xy[1] = init_grab_xy[1];
3989  }
3990  }
3991  else if (grab_state_next.use_confine) {
3992  if (!grab_state_prev.use_confine) {
3993  input->confined_pointer = zwp_pointer_constraints_v1_confine_pointer(
3995  surface,
3996  input->wl_pointer,
3997  nullptr,
3998  ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
3999  }
4000  }
4001  }
4002 
4003  /* Only show so the cursor is made visible as the last step. */
4004  cursor_visible_set(input, use_visible, is_hardware_cursor, CURSOR_VISIBLE_ONLY_SHOW);
4005 
4006 #ifdef USE_GNOME_CONFINE_HACK
4007  input->use_pointer_software_confine = use_software_confine;
4008 #endif
4009 
4010  return GHOST_kSuccess;
4011 }
4012 
4013 #ifdef WITH_GHOST_WAYLAND_DYNLOAD
4014 bool ghost_wl_dynload_libraries()
4015 {
4016  /* Only report when `libwayland-client` is not found when building without X11,
4017  * which will be used as a fallback. */
4018 # ifdef WITH_GHOST_X11
4019  bool verbose = false;
4020 # else
4021  bool verbose = true;
4022 # endif
4023 
4024  if (wayland_dynload_client_init(verbose) && /* `libwayland-client`. */
4025  wayland_dynload_cursor_init(verbose) && /* `libwayland-cursor`. */
4026  wayland_dynload_egl_init(verbose) && /* `libwayland-egl`. */
4027 # ifdef WITH_GHOST_WAYLAND_LIBDECOR
4028  wayland_dynload_libdecor_init(verbose) && /* `libdecor-0`. */
4029 # endif
4030  true) {
4031  return true;
4032  }
4033 # ifdef WITH_GHOST_WAYLAND_LIBDECOR
4035 # endif
4039 
4040  return false;
4041 }
4042 #endif /* WITH_GHOST_WAYLAND_DYNLOAD */
4043 
typedef float(TangentPoint)[2]
void BLI_kdtree_nd_() free(KDTree *tree)
Definition: kdtree_impl.h:102
KDTree *BLI_kdtree_nd_() new(unsigned int maxsize)
Definition: kdtree_impl.h:85
#define UNPACK2(a)
#define UNLIKELY(x)
#define ELEM(...)
SSIZE_T ssize_t
Definition: BLI_winstuff.h:71
#define CLOG_INFO(clg_ref, level,...)
Definition: CLG_log.h:187
ThreadMutex mutex
#define GHOST_OPENGL_EGL_CONTEXT_FLAGS
#define GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY
#define GHOST_ASSERT(x, info)
Definition: GHOST_Debug.h:54
#define GHOST_PRINT(x)
Definition: GHOST_Debug.h:35
static void tablet_tool_handle_down(void *data, struct zwp_tablet_tool_v2 *, const uint32_t serial)
#define BTN_STYLUS2
static void tablet_tool_handle_distance(void *, struct zwp_tablet_tool_v2 *, const uint32_t distance)
void ghost_wl_surface_tag_cursor_pointer(struct wl_surface *surface)
void ghost_wl_surface_tag_cursor_tablet(struct wl_surface *surface)
static const struct wl_data_source_listener data_source_listener
static CLG_LogRef LOG_WL_TABLET_TOOL
static void shell_handle_ping(void *, struct xdg_wm_base *xdg_wm_base, const uint32_t serial)
static void tablet_tool_handle_up(void *data, struct zwp_tablet_tool_v2 *)
static GHOST_WindowManager * window_manager
static void tablet_tool_handle_wheel(void *data, struct zwp_tablet_tool_v2 *, const wl_fixed_t, const int32_t clicks)
static GHOST_TSuccess getCursorPositionClientRelative_impl(const input_state_pointer_t *input_state, const GHOST_WindowWayland *win, int32_t &x, int32_t &y)
static const char * ghost_wl_surface_cursor_tablet_tag_id
#define LOG
#define KEY_GRAVE
void ghost_wl_surface_tag(struct wl_surface *surface)
#define BTN_STYLUS
static const struct wl_output_listener output_listener
static void xdg_output_handle_done(void *data, struct zxdg_output_v1 *)
static CLG_LogRef LOG_WL_TABLET_SEAT
static const zwp_relative_pointer_v1_listener relative_pointer_listener
static void keyboard_handle_key_repeat_cancel(struct input_t *input)
static void tablet_tool_handle_done(void *, struct zwp_tablet_tool_v2 *)
static const std::unordered_map< std::string, GHOST_TDragnDropTypes > mime_dnd
struct output_t * ghost_wl_output_user_data(struct wl_output *wl_output)
static const struct xdg_wm_base_listener shell_listener
static const struct zxdg_output_v1_listener xdg_output_listener
static int memfd_create_sealed(const char *name)
static GHOST_Context * createOffscreenContext_impl(GHOST_SystemWayland *system, struct wl_display *wl_display, wl_egl_window *egl_window)
static const struct wl_surface_listener cursor_surface_listener
static void xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *, const int32_t width, const int32_t height)
#define GXMAP(k, x, y)
static void data_source_handle_dnd_drop_performed(void *, struct wl_data_source *)
static const struct wl_data_device_listener data_device_listener
static const std::unordered_map< GHOST_TStandardCursor, const char * > cursors
static void dnd_events(const input_t *const input, const GHOST_TEventType event)
bool ghost_wl_surface_own_cursor_pointer(const struct wl_surface *surface)
static void xdg_output_handle_logical_position(void *data, struct zxdg_output_v1 *, const int32_t x, const int32_t y)
static input_state_pointer_t * input_state_pointer_from_cursor_surface(input_t *input, const wl_surface *wl_surface)
static void cursor_surface_handle_leave(void *data, struct wl_surface *wl_surface, struct wl_output *output)
static void pointer_handle_axis_discrete(void *data, struct wl_pointer *, uint32_t axis, int32_t discrete)
static void data_device_handle_leave(void *data, struct wl_data_device *)
static void pointer_handle_enter(void *data, struct wl_pointer *, const uint32_t serial, struct wl_surface *surface, const wl_fixed_t surface_x, const wl_fixed_t surface_y)
static CLG_LogRef LOG_WL_RELATIVE_POINTER
static void display_destroy(display_t *d)
static void data_device_handle_motion(void *data, struct wl_data_device *, const uint32_t, const wl_fixed_t x, const wl_fixed_t y)
static void cursor_buffer_show(const input_t *input)
static CLG_LogRef LOG_WL_XDG_OUTPUT
static void tablet_tool_handle_rotation(void *, struct zwp_tablet_tool_v2 *, const wl_fixed_t degrees)
static void cursor_buffer_set(const input_t *input, wl_buffer *buffer)
static void data_device_handle_enter(void *data, struct wl_data_device *, const uint32_t serial, struct wl_surface *surface, const wl_fixed_t x, const wl_fixed_t y, struct wl_data_offer *id)
static void relative_pointer_handle_relative_motion_impl(input_t *input, GHOST_WindowWayland *win, const wl_fixed_t xy[2])
static void cursor_buffer_set_surface_impl(const input_t *input, wl_buffer *buffer, struct wl_surface *wl_surface, const int scale)
static void data_device_handle_selection(void *data, struct wl_data_device *, struct wl_data_offer *id)
static void data_source_handle_cancelled(void *, struct wl_data_source *wl_data_source)
static const struct wl_buffer_listener cursor_buffer_listener
static const struct wl_seat_listener seat_listener
static GHOST_TKey xkb_map_gkey(const xkb_keysym_t sym)
static void data_device_handle_data_offer(void *, struct wl_data_device *, struct wl_data_offer *id)
static void tablet_seat_handle_tool_added(void *data, struct zwp_tablet_seat_v2 *, struct zwp_tablet_tool_v2 *id)
static void tablet_tool_handle_slider(void *, struct zwp_tablet_tool_v2 *, const int32_t position)
static void output_handle_geometry(void *data, struct wl_output *, const int32_t, const int32_t, const int32_t physical_width, const int32_t physical_height, const int32_t, const char *make, const char *model, const int32_t transform)
static void tablet_tool_handle_frame(void *data, struct zwp_tablet_tool_v2 *, const uint32_t)
#define BTN_EXTRA
static void pointer_handle_axis(void *, struct wl_pointer *, const uint32_t, const uint32_t axis, const wl_fixed_t value)
static void tablet_tool_handle_hardware_serial(void *, struct zwp_tablet_tool_v2 *, const uint32_t, const uint32_t)
static void data_device_handle_drop(void *data, struct wl_data_device *)
static void global_handle_remove(void *, struct wl_registry *, const uint32_t name)
static void keyboard_handle_modifiers(void *data, struct wl_keyboard *, const uint32_t, const uint32_t mods_depressed, const uint32_t mods_latched, const uint32_t mods_locked, const uint32_t group)
static CLG_LogRef LOG_WL_OUTPUT
static void global_handle_add(void *data, struct wl_registry *wl_registry, const uint32_t name, const char *interface, const uint32_t version)
static const struct wl_data_offer_listener data_offer_listener
static void pointer_handle_button(void *data, struct wl_pointer *, const uint32_t serial, const uint32_t, const uint32_t button, const uint32_t state)
static void keyboard_repeat_handle_info(void *data, struct wl_keyboard *, const int32_t rate, const int32_t delay)
static void data_offer_handle_offer(void *data, struct wl_data_offer *, const char *mime_type)
static void tablet_tool_handle_tilt(void *data, struct zwp_tablet_tool_v2 *, const wl_fixed_t tilt_x, const wl_fixed_t tilt_y)
static void tablet_tool_handle_proximity_out(void *data, struct zwp_tablet_tool_v2 *)
static std::string read_pipe(data_offer_t *data_offer, const std::string mime_receive, std::mutex *mutex)
#define BTN_LEFT
static bool setCursorGrab_use_software_confine(const GHOST_TGrabCursorMode mode, wl_surface *surface)
static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm, input_state_pointer_t *input_state, wl_surface *cursor_surface)
static bool cursor_is_software(const GHOST_TGrabCursorMode mode, const bool use_software_confine)
static void keyboard_handle_enter(void *data, struct wl_keyboard *, const uint32_t serial, struct wl_surface *surface, struct wl_array *)
static void tablet_tool_handle_pressure(void *data, struct zwp_tablet_tool_v2 *, const uint32_t pressure)
static void tablet_tool_handle_capability(void *, struct zwp_tablet_tool_v2 *, const uint32_t capability)
bool ghost_wl_surface_own(const struct wl_surface *surface)
static void keyboard_handle_key(void *data, struct wl_keyboard *, const uint32_t serial, const uint32_t, const uint32_t key, const uint32_t state)
static const std::vector< std::string > mime_preference_order
static const struct wl_registry_listener registry_listener
static constexpr const char * mime_text_utf8
static CLG_LogRef LOG_WL_KEYBOARD
static void output_handle_scale(void *data, struct wl_output *, const int32_t factor)
static GHOST_TKey xkb_map_gkey_or_scan_code(const xkb_keysym_t sym, const uint32_t key)
static void output_handle_mode(void *data, struct wl_output *, const uint32_t flags, const int32_t width, const int32_t height, const int32_t)
static void cursor_surface_handle_enter(void *data, struct wl_surface *wl_surface, struct wl_output *output)
static void ghost_wayland_log_handler(const char *msg, va_list arg)
static CLG_LogRef LOG_WL_REGISTRY
static void cursor_buffer_handle_release(void *data, struct wl_buffer *wl_buffer)
@ CURSOR_VISIBLE_ONLY_HIDE
@ CURSOR_VISIBLE_ALWAYS_SET
@ CURSOR_VISIBLE_ONLY_SHOW
static const struct wl_pointer_listener pointer_listener
bool ghost_wl_surface_own_cursor_tablet(const struct wl_surface *surface)
static CLG_LogRef LOG_WL_POINTER
static const struct zwp_tablet_seat_v2_listener tablet_seat_listener
static std::mutex system_selection_mutex
static void data_offer_handle_source_actions(void *data, struct wl_data_offer *, const uint32_t source_actions)
static void cursor_buffer_hide(const input_t *input)
static void tablet_tool_handle_proximity_in(void *data, struct zwp_tablet_tool_v2 *, const uint32_t serial, struct zwp_tablet_v2 *, struct wl_surface *surface)
static void tablet_tool_handle_removed(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2)
static xkb_keysym_t xkb_state_key_get_one_sym_without_modifiers(struct xkb_state *xkb_state_empty, struct xkb_state *xkb_state_empty_with_numlock, const xkb_keycode_t key)
#define BTN_STYLUS3
static void data_source_handle_target(void *, struct wl_data_source *, const char *)
void ghost_wl_output_tag(struct wl_output *output)
static int cursor_buffer_compatible_scale_from_image(const struct wl_cursor_image *wl_image, int scale)
static void data_offer_handle_action(void *data, struct wl_data_offer *, const uint32_t dnd_action)
static GHOST_TSuccess setCursorPositionClientRelative_impl(input_t *input, GHOST_WindowWayland *win, const int32_t x, const int32_t y)
static void keyboard_handle_keymap(void *data, struct wl_keyboard *, const uint32_t format, const int32_t fd, const uint32_t size)
static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, const uint32_t capabilities)
static void pointer_handle_axis_stop(void *, struct wl_pointer *, uint32_t, uint32_t axis)
#define EVDEV_OFFSET
static const struct zwp_tablet_tool_v2_listener tablet_tool_listner
static wl_buffer * ghost_wl_buffer_create_for_image(struct wl_shm *shm, const int32_t size_xy[2], enum wl_shm_format format, void **r_buffer_data, size_t *r_buffer_data_size)
static void cursor_visible_set(input_t *input, const bool visible, const bool is_hardware, const enum eCursorSetMode set_mode)
static CLG_LogRef LOG_WL_DATA_OFFER
static void relative_pointer_handle_relative_motion(void *data, struct zwp_relative_pointer_v1 *, const uint32_t, const uint32_t, const wl_fixed_t dx, const wl_fixed_t dy, const wl_fixed_t, const wl_fixed_t)
static CLG_LogRef LOG_WL_SEAT
#define SURFACE_CLEAR_PTR(surface_test)
static CLG_LogRef LOG_WL_CURSOR_SURFACE
static const char * ghost_wl_surface_cursor_pointer_tag_id
static void data_source_handle_send(void *data, struct wl_data_source *, const char *, const int32_t fd)
static void tablet_tool_handle_motion(void *data, struct zwp_tablet_tool_v2 *, const wl_fixed_t x, const wl_fixed_t y)
static GHOST_TTabletMode tablet_tool_map_type(enum zwp_tablet_tool_v2_type wl_tablet_tool_type)
static CLG_LogRef LOG_WL_XDG_WM_BASE
#define BTN_SIDE
static void keyboard_handle_leave(void *data, struct wl_keyboard *, const uint32_t, struct wl_surface *surface)
static const char * ghost_wl_surface_tag_id
static void pointer_handle_axis_source(void *, struct wl_pointer *, uint32_t axis_source)
static void seat_handle_name(void *data, struct wl_seat *, const char *name)
#define BTN_MIDDLE
static void output_handle_done(void *data, struct wl_output *wl_output)
static void xdg_output_handle_description(void *, struct zxdg_output_v1 *, const char *description)
static const struct wl_keyboard_listener keyboard_listener
#define BTN_BACK
static CLG_LogRef LOG_WL_DATA_SOURCE
static CLG_LogRef LOG_WL_CURSOR_BUFFER
GHOST_WindowWayland * ghost_wl_surface_user_data(struct wl_surface *surface)
static void tablet_tool_handle_button(void *data, struct zwp_tablet_tool_v2 *, const uint32_t serial, const uint32_t button, const uint32_t state)
static CLG_LogRef LOG_WL_DATA_DEVICE
static constexpr const char * mime_text_uri
static void xdg_output_handle_name(void *, struct zxdg_output_v1 *, const char *name)
static void pointer_handle_leave(void *data, struct wl_pointer *, const uint32_t, struct wl_surface *surface)
static void keyboard_handle_key_repeat_reset(input_t *input, const bool use_delay)
static void tablet_seat_handle_pad_added(void *, struct zwp_tablet_seat_v2 *, struct zwp_tablet_pad_v2 *id)
static const int default_cursor_size
static input_grab_state_t input_grab_state_from_mode(const GHOST_TGrabCursorMode mode, const bool use_software_confine)
static void tablet_tool_handle_hardware_id_wacom(void *, struct zwp_tablet_tool_v2 *, const uint32_t, const uint32_t)
static void data_source_handle_action(void *, struct wl_data_source *, const uint32_t dnd_action)
static input_state_pointer_t * input_state_pointer_active(input_t *input)
#define BTN_RIGHT
static const std::vector< std::string > mime_send
static void tablet_tool_handle_type(void *data, struct zwp_tablet_tool_v2 *, const uint32_t tool_type)
#define BTN_FORWARD
static bool use_gnome_confine_hack
static void data_source_handle_dnd_finished(void *, struct wl_data_source *)
static void pointer_handle_motion(void *data, struct wl_pointer *, const uint32_t, const wl_fixed_t surface_x, const wl_fixed_t surface_y)
static const char * ghost_wl_output_tag_id
bool ghost_wl_output_own(const struct wl_output *output)
static void pointer_handle_frame(void *, struct wl_pointer *)
static constexpr const char * mime_text_plain
static void tablet_seat_handle_tablet_added(void *, struct zwp_tablet_seat_v2 *, struct zwp_tablet_v2 *id)
static size_t ghost_wl_shm_format_as_size(enum wl_shm_format format)
GHOST_TWindowState
Definition: GHOST_Types.h:129
void * GHOST_TUserDataPtr
Definition: GHOST_Types.h:72
GHOST_TStandardCursor
Definition: GHOST_Types.h:214
@ GHOST_kStandardCursorBottomLeftCorner
Definition: GHOST_Types.h:252
@ GHOST_kStandardCursorZoomIn
Definition: GHOST_Types.h:236
@ GHOST_kStandardCursorVerticalSplit
Definition: GHOST_Types.h:231
@ GHOST_kStandardCursorHelp
Definition: GHOST_Types.h:221
@ GHOST_kStandardCursorCopy
Definition: GHOST_Types.h:253
@ GHOST_kStandardCursorWait
Definition: GHOST_Types.h:222
@ GHOST_kStandardCursorHorizontalSplit
Definition: GHOST_Types.h:232
@ GHOST_kStandardCursorTopSide
Definition: GHOST_Types.h:245
@ GHOST_kStandardCursorStop
Definition: GHOST_Types.h:242
@ GHOST_kStandardCursorCrosshair
Definition: GHOST_Types.h:224
@ GHOST_kStandardCursorNSEWScroll
Definition: GHOST_Types.h:239
@ GHOST_kStandardCursorLeftRight
Definition: GHOST_Types.h:244
@ GHOST_kStandardCursorPencil
Definition: GHOST_Types.h:228
@ GHOST_kStandardCursorNSScroll
Definition: GHOST_Types.h:240
@ GHOST_kStandardCursorCrosshairA
Definition: GHOST_Types.h:225
@ GHOST_kStandardCursorUpDown
Definition: GHOST_Types.h:243
@ GHOST_kStandardCursorUpArrow
Definition: GHOST_Types.h:229
@ GHOST_kStandardCursorBottomSide
Definition: GHOST_Types.h:246
@ GHOST_kStandardCursorInfo
Definition: GHOST_Types.h:219
@ GHOST_kStandardCursorTopLeftCorner
Definition: GHOST_Types.h:249
@ GHOST_kStandardCursorEyedropper
Definition: GHOST_Types.h:235
@ GHOST_kStandardCursorKnife
Definition: GHOST_Types.h:234
@ GHOST_kStandardCursorMove
Definition: GHOST_Types.h:238
@ GHOST_kStandardCursorCrosshairB
Definition: GHOST_Types.h:226
@ GHOST_kStandardCursorBottomRightCorner
Definition: GHOST_Types.h:251
@ GHOST_kStandardCursorDownArrow
Definition: GHOST_Types.h:230
@ GHOST_kStandardCursorEraser
Definition: GHOST_Types.h:233
@ GHOST_kStandardCursorDefault
Definition: GHOST_Types.h:216
@ GHOST_kStandardCursorEWScroll
Definition: GHOST_Types.h:241
@ GHOST_kStandardCursorRightSide
Definition: GHOST_Types.h:248
@ GHOST_kStandardCursorRightArrow
Definition: GHOST_Types.h:217
@ GHOST_kStandardCursorTopRightCorner
Definition: GHOST_Types.h:250
@ GHOST_kStandardCursorDestroy
Definition: GHOST_Types.h:220
@ GHOST_kStandardCursorCrosshairC
Definition: GHOST_Types.h:227
@ GHOST_kStandardCursorZoomOut
Definition: GHOST_Types.h:237
@ GHOST_kStandardCursorLeftSide
Definition: GHOST_Types.h:247
@ GHOST_kStandardCursorText
Definition: GHOST_Types.h:223
@ GHOST_kStandardCursorLeftArrow
Definition: GHOST_Types.h:218
GHOST_TEventType
Definition: GHOST_Types.h:169
@ GHOST_kEventWindowSize
Definition: GHOST_Types.h:193
@ GHOST_kEventDraggingDropDone
Definition: GHOST_Types.h:200
@ GHOST_kEventDraggingExited
Definition: GHOST_Types.h:199
@ GHOST_kEventCursorMove
Definition: GHOST_Types.h:172
@ GHOST_kEventDraggingUpdated
Definition: GHOST_Types.h:198
@ GHOST_kEventDraggingEntered
Definition: GHOST_Types.h:197
@ GHOST_kEventButtonUp
Definition: GHOST_Types.h:174
@ GHOST_kEventButtonDown
Definition: GHOST_Types.h:173
@ GHOST_kEventKeyDown
Definition: GHOST_Types.h:183
@ GHOST_kEventUnknown
Definition: GHOST_Types.h:170
@ GHOST_kEventKeyUp
Definition: GHOST_Types.h:184
static const GHOST_TabletData GHOST_TABLET_DATA_NONE
Definition: GHOST_Types.h:104
GHOST_TTabletMode
Definition: GHOST_Types.h:83
@ GHOST_kTabletModeEraser
Definition: GHOST_Types.h:86
@ GHOST_kTabletModeStylus
Definition: GHOST_Types.h:85
@ GHOST_glStereoVisual
Definition: GHOST_Types.h:62
GHOST_TAxisFlag
Definition: GHOST_Types.h:420
void(* GHOST_TimerProcPtr)(struct GHOST_TimerTaskHandle__ *task, uint64_t time)
Definition: GHOST_Types.h:614
GHOST_TKey
Definition: GHOST_Types.h:259
@ GHOST_kKeyInsert
Definition: GHOST_Types.h:346
@ GHOST_kKeySemicolon
Definition: GHOST_Types.h:288
@ GHOST_kKeyMediaPlay
Definition: GHOST_Types.h:398
@ GHOST_kKeyQuote
Definition: GHOST_Types.h:269
@ GHOST_kKeyAccentGrave
Definition: GHOST_Types.h:322
@ GHOST_kKeyLeftAlt
Definition: GHOST_Types.h:328
@ GHOST_kKeyRightShift
Definition: GHOST_Types.h:325
@ GHOST_kKeyNumLock
Definition: GHOST_Types.h:335
@ GHOST_kKeyEnter
Definition: GHOST_Types.h:265
@ GHOST_kKeyNumpadSlash
Definition: GHOST_Types.h:369
@ GHOST_kKeyRightArrow
Definition: GHOST_Types.h:339
@ GHOST_kKeyPause
Definition: GHOST_Types.h:344
@ GHOST_kKeyCapsLock
Definition: GHOST_Types.h:334
@ GHOST_kKeyApp
Definition: GHOST_Types.h:332
@ GHOST_kKeyMinus
Definition: GHOST_Types.h:271
@ GHOST_kKeyMediaStop
Definition: GHOST_Types.h:399
@ GHOST_kKeyBackSpace
Definition: GHOST_Types.h:261
@ GHOST_kKeyDownPage
Definition: GHOST_Types.h:351
@ GHOST_kKeyDownArrow
Definition: GHOST_Types.h:341
@ GHOST_kKeyClear
Definition: GHOST_Types.h:264
@ GHOST_kKeyNumpadPeriod
Definition: GHOST_Types.h:364
@ GHOST_kKeyF1
Definition: GHOST_Types.h:372
@ GHOST_kKeyNumpadAsterisk
Definition: GHOST_Types.h:368
@ GHOST_kKeyPrintScreen
Definition: GHOST_Types.h:343
@ GHOST_kKeyLeftControl
Definition: GHOST_Types.h:326
@ GHOST_kKeyLeftBracket
Definition: GHOST_Types.h:319
@ GHOST_kKeyTab
Definition: GHOST_Types.h:262
@ GHOST_kKeyComma
Definition: GHOST_Types.h:270
@ GHOST_kKeyRightBracket
Definition: GHOST_Types.h:320
@ GHOST_kKeyBackslash
Definition: GHOST_Types.h:321
@ GHOST_kKeyOS
Definition: GHOST_Types.h:330
@ GHOST_kKeyLinefeed
Definition: GHOST_Types.h:263
@ GHOST_kKeyRightAlt
Definition: GHOST_Types.h:329
@ GHOST_kKeyPeriod
Definition: GHOST_Types.h:273
@ GHOST_kKeyNumpadPlus
Definition: GHOST_Types.h:366
@ GHOST_kKeyUpPage
Definition: GHOST_Types.h:350
@ GHOST_kKeyLeftArrow
Definition: GHOST_Types.h:338
@ GHOST_kKeyEqual
Definition: GHOST_Types.h:289
@ GHOST_kKeyHome
Definition: GHOST_Types.h:348
@ GHOST_kKeyEnd
Definition: GHOST_Types.h:349
@ GHOST_kKeyUpArrow
Definition: GHOST_Types.h:340
@ GHOST_kKeyDelete
Definition: GHOST_Types.h:347
@ GHOST_kKeyNumpad0
Definition: GHOST_Types.h:354
@ GHOST_kKeyMediaFirst
Definition: GHOST_Types.h:400
@ GHOST_kKeyRightControl
Definition: GHOST_Types.h:327
@ GHOST_kKeyEsc
Definition: GHOST_Types.h:267
@ GHOST_kKeyPlus
Definition: GHOST_Types.h:272
@ GHOST_kKeyUnknown
Definition: GHOST_Types.h:260
@ GHOST_kKeyScrollLock
Definition: GHOST_Types.h:336
@ GHOST_kKeySlash
Definition: GHOST_Types.h:274
@ GHOST_kKeyNumpadEnter
Definition: GHOST_Types.h:365
@ GHOST_kKeyNumpadMinus
Definition: GHOST_Types.h:367
@ GHOST_kKeyLeftShift
Definition: GHOST_Types.h:324
@ GHOST_kKeyMediaLast
Definition: GHOST_Types.h:401
@ GHOST_kKeySpace
Definition: GHOST_Types.h:268
GHOST_TDrawingContextType
Definition: GHOST_Types.h:148
@ GHOST_kModifierKeyNum
Definition: GHOST_Types.h:126
@ GHOST_kModifierKeyRightControl
Definition: GHOST_Types.h:124
@ GHOST_kModifierKeyLeftControl
Definition: GHOST_Types.h:123
@ GHOST_kModifierKeyRightAlt
Definition: GHOST_Types.h:122
@ GHOST_kModifierKeyOS
Definition: GHOST_Types.h:125
@ GHOST_kModifierKeyRightShift
Definition: GHOST_Types.h:120
@ GHOST_kModifierKeyLeftAlt
Definition: GHOST_Types.h:121
@ GHOST_kModifierKeyLeftShift
Definition: GHOST_Types.h:119
GHOST_TSuccess
Definition: GHOST_Types.h:74
@ GHOST_kFailure
Definition: GHOST_Types.h:74
@ GHOST_kSuccess
Definition: GHOST_Types.h:74
void(* GHOST_TBacktraceFn)(void *file_handle)
Definition: GHOST_Types.h:45
GHOST_TGrabCursorMode
Definition: GHOST_Types.h:404
@ GHOST_kGrabWrap
Definition: GHOST_Types.h:410
@ GHOST_kGrabDisable
Definition: GHOST_Types.h:406
@ GHOST_kGrabHide
Definition: GHOST_Types.h:415
@ GHOST_kGrabNormal
Definition: GHOST_Types.h:408
@ GHOST_kDragnDropTypeFilenames
Definition: GHOST_Types.h:476
@ GHOST_kDragnDropTypeString
Definition: GHOST_Types.h:477
GHOST_TButton
Definition: GHOST_Types.h:156
@ GHOST_kButtonMaskRight
Definition: GHOST_Types.h:160
@ GHOST_kButtonMaskButton4
Definition: GHOST_Types.h:161
@ GHOST_kButtonMaskLeft
Definition: GHOST_Types.h:158
@ GHOST_kButtonMaskButton7
Definition: GHOST_Types.h:165
@ GHOST_kButtonMaskButton6
Definition: GHOST_Types.h:164
@ GHOST_kButtonMaskButton5
Definition: GHOST_Types.h:162
@ GHOST_kButtonMaskMiddle
Definition: GHOST_Types.h:159
GHOST_TConsoleWindowState
Definition: GHOST_Types.h:139
static bool get_cursor_settings(std::string &theme, int &size)
static struct wl_surface_listener wl_surface_listener
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei width
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble top
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position CLAMP
volatile int lock
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition: btDbvt.cpp:299
static int verbose
Definition: cineonlib.c:29
static GHOST_TBacktraceFn getBacktraceFn()
virtual GHOST_TimerProcPtr getTimerProc() const =0
int32_t m_l
Definition: GHOST_Rect.h:156
int32_t m_r
Definition: GHOST_Rect.h:160
int32_t m_b
Definition: GHOST_Rect.h:162
virtual void wrapPoint(int32_t &x, int32_t &y, int32_t ofs, GHOST_TAxisFlag axis)
Definition: GHOST_Rect.h:229
int32_t m_t
Definition: GHOST_Rect.h:158
zxdg_decoration_manager_v1 * xdg_decoration_manager()
GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const override
char * getClipboard(bool selection) const override
GHOST_TSuccess setCursorPosition(int32_t x, int32_t y) override
GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const override
GHOST_TSuccess disposeContext(GHOST_IContext *context) override
void window_surface_unref(const wl_surface *surface)
GHOST_IContext * createOffscreenContext(GHOST_GLSettings glSettings) override
wl_compositor * compositor()
void selection_set(const std::string &selection)
GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const override
uint8_t getNumDisplays() const override
void putClipboard(const char *buffer, bool selection) const override
GHOST_IWindow * createWindow(const char *title, int32_t left, int32_t top, uint32_t width, uint32_t height, GHOST_TWindowState state, GHOST_TDrawingContextType type, GHOST_GLSettings glSettings, const bool exclusive, const bool is_dialog, const GHOST_IWindow *parentWindow) override
GHOST_TSuccess setCursorVisibility(bool visible)
bool window_cursor_grab_set(const GHOST_TGrabCursorMode mode, const GHOST_TGrabCursorMode mode_current, int32_t init_grab_xy[2], const GHOST_Rect *wrap_bounds, GHOST_TAxisFlag wrap_axis, wl_surface *surface, int scale)
const std::vector< output_t * > & outputs() const
GHOST_TSuccess getCursorBitmap(GHOST_CursorBitmapRef *bitmap)
GHOST_TSuccess setCursorShape(GHOST_TStandardCursor shape)
bool processEvents(bool waitForEvent) override
GHOST_TSuccess getCursorPositionClientRelative(const GHOST_IWindow *window, int32_t &x, int32_t &y) const override
GHOST_TSuccess setCursorPositionClientRelative(GHOST_IWindow *window, int32_t x, int32_t y) override
int setConsoleWindowState(GHOST_TConsoleWindowState action) override
GHOST_TSuccess hasCursorShape(GHOST_TStandardCursor cursorShape)
GHOST_TSuccess setCustomCursorShape(uint8_t *bitmap, uint8_t *mask, int sizex, int sizey, int hotX, int hotY, bool canInvertColor)
void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const override
bool getCursorGrabUseSoftwareDisplay(const GHOST_TGrabCursorMode mode)
void getAllDisplayDimensions(uint32_t &width, uint32_t &height) const override
virtual uint64_t getMilliSeconds() const
GHOST_EventManager * getEventManager() const
Definition: GHOST_System.h:427
GHOST_WindowManager * getWindowManager() const
Definition: GHOST_System.h:432
GHOST_TimerManager * getTimerManager() const
Definition: GHOST_System.h:422
GHOST_WindowManager * m_windowManager
Definition: GHOST_System.h:395
GHOST_ITimerTask * installTimer(uint64_t delay, uint64_t interval, GHOST_TimerProcPtr timerProc, GHOST_TUserDataPtr userData=NULL)
GHOST_TSuccess pushEvent(GHOST_IEvent *event)
bool fireTimers(uint64_t time)
GHOST_TSuccess addWindow(GHOST_IWindow *window)
const std::vector< GHOST_IWindow * > & getWindows() const
GHOST_TSuccess setActiveWindow(GHOST_IWindow *window)
GHOST_TSuccess deactivate()
void getClientBounds(GHOST_Rect &bounds) const override
virtual bool getValid() const
Definition: GHOST_Window.h:75
GHOST_TSuccess setCursorShape(GHOST_TStandardCursor cursorShape)
GHOST_TStandardCursor getCursorShape() const
Definition: GHOST_Window.h:446
double time
SyclQueue void void size_t num_bytes void
int len
Definition: draw_manager.c:108
depth_tx normal_tx diffuse_light_tx specular_light_tx volume_light_tx environment_tx ambient_occlusion_tx aov_value_tx in_weight_img image(1, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "out_weight_img") .image(3
struct @211::@212 surface
uint pos
ccl_global float * buffer
ccl_global KernelShaderEvalInput ccl_global float * output
ccl_global KernelShaderEvalInput * input
const int state
format
Definition: logImageCore.h:38
static char ** types
Definition: makesdna.c:67
ccl_device_inline float4 mask(const int4 &mask, const float4 &a)
Definition: math_float4.h:513
static int left
static void error(const char *str)
Definition: meshlaplacian.c:51
static int make(const char *input_file_name, const char *output_file_name)
Definition: msgfmt.c:286
static unsigned c
Definition: RandGen.cpp:83
struct blender::compositor::@179::@181 task
T distance(const T &a, const T &b)
SymEdge< T > * sym(const SymEdge< T > *se)
Definition: delaunay_2d.cc:99
#define min(a, b)
Definition: sort.c:35
#define INT32_MAX
Definition: stdint.h:137
unsigned int uint32_t
Definition: stdint.h:80
#define INT32_MIN
Definition: stdint.h:136
signed int int32_t
Definition: stdint.h:77
unsigned char uint8_t
Definition: stdint.h:78
unsigned __int64 uint64_t
Definition: stdint.h:90
const uint8_t * data
Definition: GHOST_Types.h:52
void set(GHOST_TModifierKey mask, bool down)
uint8_t ** strings
Definition: GHOST_Types.h:508
GHOST_TTabletMode Active
Definition: GHOST_Types.h:98
std::string theme_name
struct wl_cursor_image wl_image
struct wl_surface * wl_surface
struct wl_buffer * wl_buffer
struct wl_cursor_theme * wl_theme
struct data_offer_t::@1291 dnd
std::atomic< bool > in_use
std::unordered_set< std::string > types
struct wl_data_offer * id
struct wl_data_source * data_source
struct zwp_tablet_manager_v2 * tablet_manager
std::vector< output_t * > outputs
struct wl_shm * shm
struct zxdg_output_manager_v1 * xdg_output_manager
struct zxdg_decoration_manager_v1 * xdg_decoration_manager
struct zwp_relative_pointer_manager_v1 * relative_pointer_manager
GHOST_SystemWayland * system
struct wl_compositor * compositor
std::vector< input_t * > inputs
struct xdg_wm_base * xdg_shell
struct wl_display * display
struct zwp_pointer_constraints_v1 * pointer_constraints
struct wl_data_device_manager * data_device_manager
struct wl_surface * wl_surface
struct wl_surface * wl_surface
std::unordered_set< const output_t * > outputs
struct wl_pointer * wl_pointer
struct wl_seat * wl_seat
std::mutex data_offer_dnd_mutex
input_state_keyboard_t keyboard
wl_fixed_t grab_lock_xy[2]
GHOST_SystemWayland * system
std::unordered_set< zwp_tablet_tool_v2 * > tablet_tools
uint32_t cursor_source_serial
struct xkb_state * xkb_state_empty
struct xkb_state * xkb_state
struct xkb_context * xkb_context
struct wl_keyboard * wl_keyboard
struct wl_surface * focus_dnd
std::mutex data_offer_copy_paste_mutex
std::string name
struct zwp_relative_pointer_v1 * relative_pointer
struct wl_data_device * data_device
GHOST_ITimerTask * timer
input_state_pointer_t tablet
struct zwp_tablet_seat_v2 * tablet_seat
struct zwp_confined_pointer_v1 * confined_pointer
struct data_offer_t * data_offer_copy_paste
struct zwp_locked_pointer_v1 * locked_pointer
struct data_offer_t * data_offer_dnd
struct cursor_t cursor
std::mutex data_source_mutex
struct xkb_state * xkb_state_empty_with_numlock
struct input_t::@1293 key_repeat
input_state_pointer_t pointer
bool use_pointer_software_confine
uint32_t data_source_serial
struct data_source_t * data_source
struct key_repeat_payload_t::@1292 key_data
struct wl_output * wl_output
struct wl_surface * cursor_surface
static const char hex[17]
Definition: thumbs.c:156
float max
bool wayland_dynload_egl_init(bool verbose)
void wayland_dynload_client_exit(void)
void wayland_dynload_cursor_exit(void)
bool wayland_dynload_cursor_init(bool verbose)
bool wayland_dynload_client_init(bool verbose)
void wayland_dynload_egl_exit(void)
#define wl_proxy_get_tag(...)
#define wl_display_connect(...)
#define wl_log_set_handler_client(...)
#define wl_proxy_set_tag(...)
#define wl_display_dispatch(...)
#define wl_display_disconnect(...)
#define wl_display_roundtrip(...)
#define wl_cursor_theme_get_cursor(...)
#define wl_cursor_theme_load(...)
#define wl_cursor_image_get_buffer(...)
#define wl_cursor_theme_destroy(...)
#define wl_egl_window_create(...)
#define wl_egl_window_destroy(...)
bool wayland_dynload_libdecor_init(const bool verbose)
void wayland_dynload_libdecor_exit(void)
#define libdecor_unref(...)
#define libdecor_new(...)
int xy[2]
Definition: wm_draw.c:135