Blender  V3.3
GHOST_SystemX11.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2001-2002 NaN Holding BV. All rights reserved.
3  * 2009 Nokia Corporation and/or its subsidiary(-ies).
4  * Part of this code has been taken from Qt, under LGPL license. */
5 
10 #include <X11/XKBlib.h> /* Allow detectable auto-repeat. */
11 #include <X11/Xatom.h>
12 #include <X11/Xutil.h>
13 #include <X11/keysym.h>
14 
16 #include "GHOST_EventButton.h"
17 #include "GHOST_EventCursor.h"
18 #include "GHOST_EventDragnDrop.h"
19 #include "GHOST_EventKey.h"
20 #include "GHOST_EventWheel.h"
21 #include "GHOST_SystemX11.h"
22 #include "GHOST_TimerManager.h"
23 #include "GHOST_WindowManager.h"
24 #include "GHOST_WindowX11.h"
25 #ifdef WITH_INPUT_NDOF
26 # include "GHOST_NDOFManagerUnix.h"
27 #endif
28 #include "GHOST_utildefines.h"
29 
30 #ifdef WITH_XDND
31 # include "GHOST_DropTargetX11.h"
32 #endif
33 
34 #include "GHOST_Debug.h"
35 
36 #if defined(WITH_GL_EGL)
37 # include "GHOST_ContextEGL.h"
38 # include <EGL/eglext.h>
39 #else
40 # include "GHOST_ContextGLX.h"
41 #endif
42 
43 #ifdef WITH_XF86KEYSYM
44 # include <X11/XF86keysym.h>
45 #endif
46 
47 #ifdef WITH_X11_XFIXES
48 # include <X11/extensions/Xfixes.h>
49 /* Workaround for XWayland grab glitch: T53004. */
50 # define WITH_XWAYLAND_HACK
51 #endif
52 
53 /* for XIWarpPointer */
54 #ifdef WITH_X11_XINPUT
55 # include <X11/extensions/XInput2.h>
56 #endif
57 
58 /* For timing */
59 #include <sys/time.h>
60 #include <unistd.h>
61 
62 #include <cstdio> /* for fprintf only */
63 #include <cstdlib> /* for exit */
64 #include <iostream>
65 #include <vector>
66 
67 /* For debugging, so we can break-point X11 errors. */
68 // #define USE_X11_ERROR_HANDLERS
69 
70 #ifdef WITH_X11_XINPUT
71 # define USE_XINPUT_HOTPLUG
72 #endif
73 
74 /* see T34039 Fix Alt key glitch on Unity desktop */
75 #define USE_UNITY_WORKAROUND
76 
77 /* Fix 'shortcut' part of keyboard reading code only ever using first defined key-map
78  * instead of active one. See T47228 and D1746 */
79 #define USE_NON_LATIN_KB_WORKAROUND
80 
81 static uchar bit_is_on(const uchar *ptr, int bit)
82 {
83  return ptr[bit >> 3] & (1 << (bit & 7));
84 }
85 
86 static GHOST_TKey ghost_key_from_keysym(const KeySym key);
87 static GHOST_TKey ghost_key_from_keycode(const XkbDescPtr xkb_descr, const KeyCode keycode);
88 static GHOST_TKey ghost_key_from_keysym_or_keycode(const KeySym key,
89  const XkbDescPtr xkb_descr,
90  const KeyCode keycode);
91 
92 /* these are for copy and select copy */
93 static char *txt_cut_buffer = nullptr;
94 static char *txt_select_buffer = nullptr;
95 
96 #ifdef WITH_XWAYLAND_HACK
97 static bool use_xwayland_hack = false;
98 #endif
99 
100 using namespace std;
101 
102 GHOST_SystemX11::GHOST_SystemX11() : GHOST_System(), m_xkb_descr(nullptr), m_start_time(0)
103 {
104  XInitThreads();
105  m_display = XOpenDisplay(nullptr);
106 
107  if (!m_display) {
108  std::cerr << "Unable to open a display" << std::endl;
109  abort(); /* was return before, but this would just mean it will crash later */
110  }
111 
112 #ifdef USE_X11_ERROR_HANDLERS
113  (void)XSetErrorHandler(GHOST_X11_ApplicationErrorHandler);
114  (void)XSetIOErrorHandler(GHOST_X11_ApplicationIOErrorHandler);
115 #endif
116 
117 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
118  /* NOTE: Don't open connection to XIM server here, because the locale has to be
119  * set before opening the connection but `setlocale()` has not been called yet.
120  * the connection will be opened after entering the event loop. */
121  m_xim = nullptr;
122 #endif
123 
124 #define GHOST_INTERN_ATOM_IF_EXISTS(atom) \
125  { \
126  m_atom.atom = XInternAtom(m_display, #atom, True); \
127  } \
128  (void)0
129 #define GHOST_INTERN_ATOM(atom) \
130  { \
131  m_atom.atom = XInternAtom(m_display, #atom, False); \
132  } \
133  (void)0
134 
143 
155 #ifdef WITH_X11_XINPUT
156  m_atom.TABLET = XInternAtom(m_display, XI_TABLET, False);
157 #endif
158 
159 #undef GHOST_INTERN_ATOM_IF_EXISTS
160 #undef GHOST_INTERN_ATOM
161 
162  m_last_warp_x = 0;
163  m_last_warp_y = 0;
164  m_last_release_keycode = 0;
165  m_last_release_time = 0;
166 
167  /* compute the initial time */
168  timeval tv;
169  if (gettimeofday(&tv, nullptr) == -1) {
170  GHOST_ASSERT(false, "Could not instantiate timer!");
171  }
172 
173  /* Taking care not to overflow the `tv.tv_sec * 1000`. */
174  m_start_time = uint64_t(tv.tv_sec) * 1000 + tv.tv_usec / 1000;
175 
176  /* Use detectable auto-repeat, mac and windows also do this. */
177  int use_xkb;
178  int xkb_opcode, xkb_event, xkb_error;
179  int xkb_major = XkbMajorVersion, xkb_minor = XkbMinorVersion;
180 
181  use_xkb = XkbQueryExtension(
182  m_display, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor);
183  if (use_xkb) {
184  XkbSetDetectableAutoRepeat(m_display, true, nullptr);
185 
186  m_xkb_descr = XkbGetMap(m_display, 0, XkbUseCoreKbd);
187  if (m_xkb_descr) {
188  XkbGetNames(m_display, XkbKeyNamesMask, m_xkb_descr);
189  XkbGetControls(m_display, XkbPerKeyRepeatMask | XkbRepeatKeysMask, m_xkb_descr);
190  }
191  }
192 
193 #ifdef WITH_XWAYLAND_HACK
194  use_xwayland_hack = getenv("WAYLAND_DISPLAY") != nullptr;
195 #endif
196 
197 #ifdef WITH_X11_XINPUT
198  /* detect if we have xinput (for reuse) */
199  {
200  memset(&m_xinput_version, 0, sizeof(m_xinput_version));
201  XExtensionVersion *version = XGetExtensionVersion(m_display, INAME);
202  if (version && (version != (XExtensionVersion *)NoSuchExtension)) {
203  if (version->present) {
204  m_xinput_version = *version;
205  }
206  XFree(version);
207  }
208  }
209 
210 # ifdef USE_XINPUT_HOTPLUG
211  if (m_xinput_version.present) {
212  XEventClass class_presence;
213  int xi_presence;
214  DevicePresence(m_display, xi_presence, class_presence);
215  XSelectExtensionEvent(
216  m_display, RootWindow(m_display, DefaultScreen(m_display)), &class_presence, 1);
217  (void)xi_presence;
218  }
219 # endif /* USE_XINPUT_HOTPLUG */
220 
221  refreshXInputDevices();
222 #endif /* WITH_X11_XINPUT */
223 }
224 
226 {
227 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
228  if (m_xim) {
229  XCloseIM(m_xim);
230  }
231 #endif
232 
233 #ifdef WITH_X11_XINPUT
234  /* Close tablet devices. */
235  clearXInputDevices();
236 #endif /* WITH_X11_XINPUT */
237 
238 #ifdef WITH_GL_EGL
239  ::eglTerminate(::eglGetDisplay(m_display));
240 #endif
241 
242  if (m_xkb_descr) {
243  XkbFreeKeyboard(m_xkb_descr, XkbAllComponentsMask, true);
244  }
245 
246  XCloseDisplay(m_display);
247 }
248 
250 {
252 
253  if (success) {
254 #ifdef WITH_INPUT_NDOF
255  m_ndofManager = new GHOST_NDOFManagerUnix(*this);
256 #endif
258 
259  if (m_displayManager) {
260  return GHOST_kSuccess;
261  }
262  }
263 
264  return GHOST_kFailure;
265 }
266 
268 {
269  timeval tv;
270  if (gettimeofday(&tv, nullptr) == -1) {
271  GHOST_ASSERT(false, "Could not compute time!");
272  }
273 
274  /* Taking care not to overflow the tv.tv_sec * 1000 */
275  return uint64_t(tv.tv_sec) * 1000 + tv.tv_usec / 1000 - m_start_time;
276 }
277 
279 {
280  return uint8_t(1);
281 }
282 
288 {
289  if (m_display) {
290  /* NOTE(campbell): for this to work as documented,
291  * we would need to use Xinerama check r54370 for code that did this,
292  * we've since removed since its not worth the extra dependency. */
294  }
295 }
296 
302 {
303  if (m_display) {
304  width = DisplayWidth(m_display, DefaultScreen(m_display));
305  height = DisplayHeight(m_display, DefaultScreen(m_display));
306  }
307 }
308 
327  int32_t left,
328  int32_t top,
329  uint32_t width,
333  GHOST_GLSettings glSettings,
334  const bool exclusive,
335  const bool is_dialog,
336  const GHOST_IWindow *parentWindow)
337 {
338  GHOST_WindowX11 *window = nullptr;
339 
340  if (!m_display) {
341  return nullptr;
342  }
343 
344  window = new GHOST_WindowX11(this,
345  m_display,
346  title,
347  left,
348  top,
349  width,
350  height,
351  state,
352  (GHOST_WindowX11 *)parentWindow,
353  type,
354  is_dialog,
355  ((glSettings.flags & GHOST_glStereoVisual) != 0),
356  exclusive,
357  ((glSettings.flags & GHOST_glAlphaBackground) != 0),
358  (glSettings.flags & GHOST_glDebugContext) != 0);
359 
360  if (window) {
361  /* Both are now handle in GHOST_WindowX11.cpp
362  * Focus and Delete atoms. */
363 
364  if (window->getValid()) {
365  /* Store the pointer to the window */
366  m_windowManager->addWindow(window);
369  }
370  else {
371  delete window;
372  window = nullptr;
373  }
374  }
375  return window;
376 }
377 
384 {
385  /* During development:
386  * try 4.x compatibility profile
387  * try 3.3 compatibility profile
388  * fall back to 3.0 if needed
389  *
390  * Final Blender 2.8:
391  * try 4.x core profile
392  * try 3.3 core profile
393  * no fall-backs. */
394 
395  const bool debug_context = (glSettings.flags & GHOST_glDebugContext) != 0;
396 
397 #if defined(WITH_GL_PROFILE_CORE)
398  {
399  const char *version_major = (char *)glewGetString(GLEW_VERSION_MAJOR);
400  if (version_major != nullptr && version_major[0] == '1') {
401  fprintf(stderr, "Error: GLEW version 2.0 and above is required.\n");
402  abort();
403  }
404  }
405 #endif
406 
407  const int profile_mask =
408 #ifdef WITH_GL_EGL
409 # if defined(WITH_GL_PROFILE_CORE)
410  EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT;
411 # elif defined(WITH_GL_PROFILE_COMPAT)
412  EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT;
413 # else
414 # error // must specify either core or compat at build time
415 # endif
416 #else
417 # if defined(WITH_GL_PROFILE_CORE)
418  GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
419 # elif defined(WITH_GL_PROFILE_COMPAT)
420  GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
421 # else
422 # error // must specify either core or compat at build time
423 # endif
424 #endif
425 
427 
428  for (int minor = 5; minor >= 0; --minor) {
429 #if defined(WITH_GL_EGL)
430  context = new GHOST_ContextEGL(this,
431  false,
432  EGLNativeWindowType(nullptr),
433  EGLNativeDisplayType(m_display),
434  profile_mask,
435  4,
436  minor,
438  (debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
440  EGL_OPENGL_API);
441 #else
442  context = new GHOST_ContextGLX(false,
443  (Window) nullptr,
444  m_display,
445  (GLXFBConfig) nullptr,
446  profile_mask,
447  4,
448  minor,
450  (debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
452 #endif
453 
454  if (context->initializeDrawingContext()) {
455  return context;
456  }
457  delete context;
458  }
459 
460 #if defined(WITH_GL_EGL)
461  context = new GHOST_ContextEGL(this,
462  false,
463  EGLNativeWindowType(nullptr),
464  EGLNativeDisplayType(m_display),
465  profile_mask,
466  3,
467  3,
469  (debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
471  EGL_OPENGL_API);
472 #else
473  context = new GHOST_ContextGLX(false,
474  (Window) nullptr,
475  m_display,
476  (GLXFBConfig) nullptr,
477  profile_mask,
478  3,
479  3,
481  (debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
483 #endif
484 
485  if (context->initializeDrawingContext()) {
486  return context;
487  }
488  delete context;
489 
490  return nullptr;
491 }
492 
499 {
500  delete context;
501 
502  return GHOST_kSuccess;
503 }
504 
505 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
506 static void destroyIMCallback(XIM /*xim*/, XPointer ptr, XPointer /*data*/)
507 {
508  GHOST_PRINT("XIM server died\n");
509 
510  if (ptr) {
511  *(XIM *)ptr = nullptr;
512  }
513 }
514 
515 bool GHOST_SystemX11::openX11_IM()
516 {
517  if (!m_display)
518  return false;
519 
520  /* set locale modifiers such as `@im=ibus` specified by XMODIFIERS. */
521  XSetLocaleModifiers("");
522 
523  m_xim = XOpenIM(m_display, nullptr, (char *)GHOST_X11_RES_NAME, (char *)GHOST_X11_RES_CLASS);
524  if (!m_xim) {
525  return false;
526  }
527 
528  XIMCallback destroy;
529  destroy.callback = (XIMProc)destroyIMCallback;
530  destroy.client_data = (XPointer)&m_xim;
531  XSetIMValues(m_xim, XNDestroyCallback, &destroy, nullptr);
532  return true;
533 }
534 #endif
535 
536 GHOST_WindowX11 *GHOST_SystemX11::findGhostWindow(Window xwind) const
537 {
538 
539  if (xwind == 0) {
540  return nullptr;
541  }
542 
543  /* It is not entirely safe to do this as the backptr may point
544  * to a window that has recently been removed.
545  * We should always check the window manager's list of windows
546  * and only process events on these windows. */
547 
548  const vector<GHOST_IWindow *> &win_vec = m_windowManager->getWindows();
549 
550  vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
551  vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
552 
553  for (; win_it != win_end; ++win_it) {
554  GHOST_WindowX11 *window = static_cast<GHOST_WindowX11 *>(*win_it);
555  if (window->getXWindow() == xwind) {
556  return window;
557  }
558  }
559  return nullptr;
560 }
561 
562 static void SleepTillEvent(Display *display, int64_t maxSleep)
563 {
564  int fd = ConnectionNumber(display);
565  fd_set fds;
566 
567  FD_ZERO(&fds);
568  FD_SET(fd, &fds);
569 
570  if (maxSleep == -1) {
571  select(fd + 1, &fds, nullptr, nullptr, nullptr);
572  }
573  else {
574  timeval tv;
575 
576  tv.tv_sec = maxSleep / 1000;
577  tv.tv_usec = (maxSleep - tv.tv_sec * 1000) * 1000;
578 
579  select(fd + 1, &fds, nullptr, nullptr, &tv);
580  }
581 }
582 
583 /* This function borrowed from Qt's X11 support qclipboard_x11.cpp */
585  Time timestamp;
586 };
587 
588 static Bool init_timestamp_scanner(Display *, XEvent *event, XPointer arg)
589 {
590  init_timestamp_data *data = reinterpret_cast<init_timestamp_data *>(arg);
591  switch (event->type) {
592  case ButtonPress:
593  case ButtonRelease:
594  data->timestamp = event->xbutton.time;
595  break;
596  case MotionNotify:
597  data->timestamp = event->xmotion.time;
598  break;
599  case KeyPress:
600  case KeyRelease:
601  data->timestamp = event->xkey.time;
602  break;
603  case PropertyNotify:
604  data->timestamp = event->xproperty.time;
605  break;
606  case EnterNotify:
607  case LeaveNotify:
608  data->timestamp = event->xcrossing.time;
609  break;
610  case SelectionClear:
611  data->timestamp = event->xselectionclear.time;
612  break;
613  default:
614  break;
615  }
616 
617  return false;
618 }
619 
620 Time GHOST_SystemX11::lastEventTime(Time default_time)
621 {
623  data.timestamp = default_time;
624  XEvent ev;
625  XCheckIfEvent(m_display, &ev, &init_timestamp_scanner, (XPointer)&data);
626 
627  return data.timestamp;
628 }
629 
630 bool GHOST_SystemX11::processEvents(bool waitForEvent)
631 {
632  /* Get all the current events -- translate them into
633  * ghost events and call base class pushEvent() method. */
634 
635  bool anyProcessed = false;
636 
637  do {
638  GHOST_TimerManager *timerMgr = getTimerManager();
639 
640  if (waitForEvent && m_dirty_windows.empty() && !XPending(m_display)) {
641  uint64_t next = timerMgr->nextFireTime();
642 
643  if (next == GHOST_kFireTimeNever) {
644  SleepTillEvent(m_display, -1);
645  }
646  else {
647  const int64_t maxSleep = next - getMilliSeconds();
648 
649  if (maxSleep >= 0) {
650  SleepTillEvent(m_display, next - getMilliSeconds());
651  }
652  }
653  }
654 
655  if (timerMgr->fireTimers(getMilliSeconds())) {
656  anyProcessed = true;
657  }
658 
659  while (XPending(m_display)) {
660  XEvent xevent;
661  XNextEvent(m_display, &xevent);
662 
663 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
664  /* open connection to XIM server and create input context (XIC)
665  * when receiving the first FocusIn or KeyPress event after startup,
666  * or recover XIM and XIC when the XIM server has been restarted */
667  if (ELEM(xevent.type, FocusIn, KeyPress)) {
668  if (!m_xim && openX11_IM()) {
669  GHOST_PRINT("Connected to XIM server\n");
670  }
671 
672  if (m_xim) {
673  GHOST_WindowX11 *window = findGhostWindow(xevent.xany.window);
674  if (window && !window->getX11_XIC() && window->createX11_XIC()) {
675  GHOST_PRINT("XIM input context created\n");
676  if (xevent.type == KeyPress)
677  /* we can assume the window has input focus
678  * here, because key events are received only
679  * when the window is focused. */
680  XSetICFocus(window->getX11_XIC());
681  }
682  }
683  }
684 
685  /* dispatch event to XIM server */
686  if ((XFilterEvent(&xevent, (Window) nullptr) == True)) {
687  /* do nothing now, the event is consumed by XIM. */
688  continue;
689  }
690 #endif
691  /* When using auto-repeat, some key-press events can actually come *after* the
692  * last key-release. The next code takes care of that. */
693  if (xevent.type == KeyRelease) {
694  m_last_release_keycode = xevent.xkey.keycode;
695  m_last_release_time = xevent.xkey.time;
696  }
697  else if (xevent.type == KeyPress) {
698  if ((xevent.xkey.keycode == m_last_release_keycode) &&
699  ((xevent.xkey.time <= m_last_release_time))) {
700  continue;
701  }
702  }
703 
704  processEvent(&xevent);
705  anyProcessed = true;
706 
707 #ifdef USE_UNITY_WORKAROUND
708  /* NOTE: processEvent() can't include this code because
709  * KeymapNotify event have no valid window information. */
710 
711  /* the X server generates KeymapNotify event immediately after
712  * every EnterNotify and FocusIn event. we handle this event
713  * to correct modifier states. */
714  if (xevent.type == FocusIn) {
715  /* use previous event's window, because KeymapNotify event
716  * has no window information. */
717  GHOST_WindowX11 *window = findGhostWindow(xevent.xany.window);
718  if (window && XPending(m_display) >= 2) {
719  XNextEvent(m_display, &xevent);
720 
721  if (xevent.type == KeymapNotify) {
722  XEvent xev_next;
723 
724  /* check if KeyPress or KeyRelease event was generated
725  * in order to confirm the window is active. */
726  XPeekEvent(m_display, &xev_next);
727 
728  if (ELEM(xev_next.type, KeyPress, KeyRelease)) {
729  /* XK_Hyper_L/R currently unused. */
730  const static KeySym modifiers[] = {
731  XK_Shift_L,
732  XK_Shift_R,
733  XK_Control_L,
734  XK_Control_R,
735  XK_Alt_L,
736  XK_Alt_R,
737  XK_Super_L,
738  XK_Super_R,
739  };
740 
741  for (int i = 0; i < (int)ARRAY_SIZE(modifiers); i++) {
742  KeyCode kc = XKeysymToKeycode(m_display, modifiers[i]);
743  if (kc != 0 && ((xevent.xkeymap.key_vector[kc >> 3] >> (kc & 7)) & 1) != 0) {
746  window,
747  ghost_key_from_keysym(modifiers[i]),
748  false,
749  nullptr));
750  }
751  }
752  }
753  }
754  }
755  }
756 #endif /* USE_UNITY_WORKAROUND */
757  }
758 
759  if (generateWindowExposeEvents()) {
760  anyProcessed = true;
761  }
762 
763 #ifdef WITH_INPUT_NDOF
764  if (static_cast<GHOST_NDOFManagerUnix *>(m_ndofManager)->processEvents()) {
765  anyProcessed = true;
766  }
767 #endif
768 
769  } while (waitForEvent && !anyProcessed);
770 
771  return anyProcessed;
772 }
773 
774 #ifdef WITH_X11_XINPUT
775 static bool checkTabletProximity(Display *display, XDevice *device)
776 {
777  /* we could have true/false/not-found return value, but for now false is OK */
778 
779  /* see: state.c from xinput, to get more data out of the device */
780  XDeviceState *state;
781 
782  if (device == nullptr) {
783  return false;
784  }
785 
786  /* needed since unplugging will abort() without this */
787  GHOST_X11_ERROR_HANDLERS_OVERRIDE(handler_store);
788 
789  state = XQueryDeviceState(display, device);
790 
791  GHOST_X11_ERROR_HANDLERS_RESTORE(handler_store);
792 
793  if (state) {
794  XInputClass *cls = state->data;
795  // printf("%d class%s :\n", state->num_classes, (state->num_classes > 1) ? "es" : "");
796  for (int loop = 0; loop < state->num_classes; loop++) {
797  switch (cls->c_class) {
798  case ValuatorClass:
799  XValuatorState *val_state = (XValuatorState *)cls;
800  // printf("ValuatorClass Mode=%s Proximity=%s\n",
801  // val_state->mode & 1 ? "Absolute" : "Relative",
802  // val_state->mode & 2 ? "Out" : "In");
803 
804  if ((val_state->mode & 2) == 0) {
805  XFreeDeviceState(state);
806  return true;
807  }
808  break;
809  }
810  cls = (XInputClass *)((char *)cls + cls->length);
811  }
812  XFreeDeviceState(state);
813  }
814  return false;
815 }
816 #endif /* WITH_X11_XINPUT */
817 
818 void GHOST_SystemX11::processEvent(XEvent *xe)
819 {
820  GHOST_WindowX11 *window = findGhostWindow(xe->xany.window);
821  GHOST_Event *g_event = nullptr;
822 
823  /* Detect auto-repeat. */
824  bool is_repeat = false;
825  if (ELEM(xe->type, KeyPress, KeyRelease)) {
826  XKeyEvent *xke = &(xe->xkey);
827 
828  /* Set to true if this key will repeat. */
829  bool is_repeat_keycode = false;
830 
831  if (m_xkb_descr != nullptr) {
832  /* Use XKB support. */
833  is_repeat_keycode = (
834  /* Should always be true, check just in case. */
835  (xke->keycode < (XkbPerKeyBitArraySize << 3)) &&
836  bit_is_on(m_xkb_descr->ctrls->per_key_repeat, xke->keycode));
837  }
838  else {
839  /* No XKB support (filter by modifier). */
840  switch (XLookupKeysym(xke, 0)) {
841  case XK_Shift_L:
842  case XK_Shift_R:
843  case XK_Control_L:
844  case XK_Control_R:
845  case XK_Alt_L:
846  case XK_Alt_R:
847  case XK_Super_L:
848  case XK_Super_R:
849  case XK_Hyper_L:
850  case XK_Hyper_R:
851  case XK_Caps_Lock:
852  case XK_Scroll_Lock:
853  case XK_Num_Lock: {
854  break;
855  }
856  default: {
857  is_repeat_keycode = true;
858  }
859  }
860  }
861 
862  if (is_repeat_keycode) {
863  if (xe->type == KeyPress) {
864  if (m_keycode_last_repeat_key == xke->keycode) {
865  is_repeat = true;
866  }
867  m_keycode_last_repeat_key = xke->keycode;
868  }
869  else {
870  if (m_keycode_last_repeat_key == xke->keycode) {
871  m_keycode_last_repeat_key = (uint)-1;
872  }
873  }
874  }
875  }
876  else if (xe->type == EnterNotify) {
877  /* We can't tell how the key state changed, clear it to avoid stuck keys. */
878  m_keycode_last_repeat_key = (uint)-1;
879  }
880 
881 #ifdef USE_XINPUT_HOTPLUG
882  /* Hot-Plug support */
883  if (m_xinput_version.present) {
884  XEventClass class_presence;
885  int xi_presence;
886 
887  DevicePresence(m_display, xi_presence, class_presence);
888  (void)class_presence;
889 
890  if (xe->type == xi_presence) {
891  XDevicePresenceNotifyEvent *notify_event = (XDevicePresenceNotifyEvent *)xe;
892  if (ELEM(notify_event->devchange,
893  DeviceEnabled,
894  DeviceDisabled,
895  DeviceAdded,
896  DeviceRemoved)) {
897  refreshXInputDevices();
898 
899  /* update all window events */
900  {
901  const vector<GHOST_IWindow *> &win_vec = m_windowManager->getWindows();
902  vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
903  vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
904 
905  for (; win_it != win_end; ++win_it) {
906  GHOST_WindowX11 *window_xinput = static_cast<GHOST_WindowX11 *>(*win_it);
907  window_xinput->refreshXInputDevices();
908  }
909  }
910  }
911  }
912  }
913 #endif /* USE_XINPUT_HOTPLUG */
914 
915  if (!window) {
916  return;
917  }
918 
919 #ifdef WITH_X11_XINPUT
920  /* Proximity-Out Events are not reliable, if the tablet is active - check on each event
921  * this adds a little overhead but only while the tablet is in use.
922  * in the future we could have a ghost call window->CheckTabletProximity()
923  * but for now enough parts of the code are checking 'Active'
924  * - campbell */
925  if (window->GetTabletData().Active != GHOST_kTabletModeNone) {
926  bool any_proximity = false;
927 
928  for (GHOST_TabletX11 &xtablet : m_xtablets) {
929  if (checkTabletProximity(xe->xany.display, xtablet.Device)) {
930  any_proximity = true;
931  }
932  }
933 
934  if (!any_proximity) {
935  // printf("proximity disable\n");
937  }
938  }
939 #endif /* WITH_X11_XINPUT */
940  switch (xe->type) {
941  case Expose: {
942  XExposeEvent &xee = xe->xexpose;
943 
944  if (xee.count == 0) {
945  /* Only generate a single expose event
946  * per read of the event queue. */
947 
948  g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window);
949  }
950  break;
951  }
952 
953  case MotionNotify: {
954  XMotionEvent &xme = xe->xmotion;
955 
956  bool is_tablet = window->GetTabletData().Active != GHOST_kTabletModeNone;
957 
958  if (is_tablet == false && window->getCursorGrabModeIsWarp()) {
959  int32_t x_new = xme.x_root;
960  int32_t y_new = xme.y_root;
961  int32_t x_accum, y_accum;
963 
964  /* fallback to window bounds */
965  if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) {
966  window->getClientBounds(bounds);
967  }
968 
969  /* Could also clamp to screen bounds wrap with a window outside the view will
970  * fail at the moment. Use offset of 8 in case the window is at screen bounds. */
971  bounds.wrapPoint(x_new, y_new, 8, window->getCursorGrabAxis());
972 
973  window->getCursorGrabAccum(x_accum, y_accum);
974 
975  if (x_new != xme.x_root || y_new != xme.y_root) {
976  /* Use time of last event to avoid wrapping several times on the 'same' actual wrap.
977  * Note that we need to deal with X and Y separately as those might wrap at the same time
978  * but still in two different events (corner case, see T74918).
979  * We also have to add a few extra milliseconds of 'padding', as sometimes we get two
980  * close events that will generate extra wrap on the same axis within those few
981  * milliseconds. */
982  if (x_new != xme.x_root && xme.time > m_last_warp_x) {
983  x_accum += (xme.x_root - x_new);
984  m_last_warp_x = lastEventTime(xme.time) + 25;
985  }
986  if (y_new != xme.y_root && xme.time > m_last_warp_y) {
987  y_accum += (xme.y_root - y_new);
988  m_last_warp_y = lastEventTime(xme.time) + 25;
989  }
990  window->setCursorGrabAccum(x_accum, y_accum);
991  /* When wrapping we don't need to add an event because the
992  * #setCursorPosition call will cause a new event after. */
993  setCursorPosition(x_new, y_new); /* wrap */
994  }
995  else {
996  g_event = new GHOST_EventCursor(getMilliSeconds(),
998  window,
999  xme.x_root + x_accum,
1000  xme.y_root + y_accum,
1001  window->GetTabletData());
1002  }
1003  }
1004  else {
1005  g_event = new GHOST_EventCursor(getMilliSeconds(),
1007  window,
1008  xme.x_root,
1009  xme.y_root,
1010  window->GetTabletData());
1011  }
1012  break;
1013  }
1014 
1015  case KeyPress:
1016  case KeyRelease: {
1017  XKeyEvent *xke = &(xe->xkey);
1018  KeySym key_sym;
1019  char *utf8_buf = nullptr;
1020  char ascii;
1021 
1022 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1023  /* utf8_array[] is initial buffer used for Xutf8LookupString().
1024  * if the length of the utf8 string exceeds this array, allocate
1025  * another memory area and call Xutf8LookupString() again.
1026  * the last 5 bytes are used to avoid segfault that might happen
1027  * at the end of this buffer when the constructor of GHOST_EventKey
1028  * reads 6 bytes regardless of the effective data length. */
1029  char utf8_array[16 * 6 + 5]; /* 16 utf8 characters */
1030  int len = 1; /* at least one null character will be stored */
1031 #else
1032  char utf8_array[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'};
1033 #endif
1034  GHOST_TEventType type = (xke->type == KeyPress) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp;
1035 
1036  GHOST_TKey gkey;
1037 
1038 #ifdef USE_NON_LATIN_KB_WORKAROUND
1039  /* XXX: Code below is kinda awfully convoluted... Issues are:
1040  * - In keyboards like Latin ones, numbers need a 'Shift' to be accessed but key_sym
1041  * is unmodified (or anyone swapping the keys with `xmodmap`).
1042  * - #XLookupKeysym seems to always use first defined key-map (see T47228), which generates
1043  * key-codes unusable by ghost_key_from_keysym for non-Latin-compatible key-maps.
1044  *
1045  * To address this, we:
1046  * - Try to get a 'number' key_sym using #XLookupKeysym (with virtual shift modifier),
1047  * in a very restrictive set of cases.
1048  * - Fallback to #XLookupString to get a key_sym from active user-defined key-map.
1049  *
1050  * Note that:
1051  * - This effectively 'lock' main number keys to always output number events
1052  * (except when using alt-gr).
1053  * - This enforces users to use an ASCII-compatible key-map with Blender -
1054  * but at least it gives predictable and consistent results.
1055  *
1056  * Also, note that nothing in XLib sources [1] makes it obvious why those two functions give
1057  * different key_sym results.
1058  *
1059  * [1] http://cgit.freedesktop.org/xorg/lib/libX11/tree/src/KeyBind.c
1060  */
1061  KeySym key_sym_str;
1062  /* Mode_switch 'modifier' is `AltGr` - when this one or Shift are enabled,
1063  * we do not want to apply that 'forced number' hack. */
1064  const unsigned int mode_switch_mask = XkbKeysymToModifiers(xke->display, XK_Mode_switch);
1065  const unsigned int number_hack_forbidden_kmods_mask = mode_switch_mask | ShiftMask;
1066  if ((xke->keycode >= 10 && xke->keycode < 20) &&
1067  ((xke->state & number_hack_forbidden_kmods_mask) == 0)) {
1068  key_sym = XLookupKeysym(xke, ShiftMask);
1069  if (!((key_sym >= XK_0) && (key_sym <= XK_9))) {
1070  key_sym = XLookupKeysym(xke, 0);
1071  }
1072  }
1073  else {
1074  key_sym = XLookupKeysym(xke, 0);
1075  }
1076 
1077  if (!XLookupString(xke, &ascii, 1, &key_sym_str, nullptr)) {
1078  ascii = '\0';
1079  }
1080 
1081  /* Only allow a limited set of keys from XLookupKeysym,
1082  * all others we take from XLookupString, unless it gives unknown key... */
1083  gkey = ghost_key_from_keysym_or_keycode(key_sym, m_xkb_descr, xke->keycode);
1084  switch (gkey) {
1085  case GHOST_kKeyRightAlt:
1086  case GHOST_kKeyLeftAlt:
1087  case GHOST_kKeyRightShift:
1088  case GHOST_kKeyLeftShift:
1090  case GHOST_kKeyLeftControl:
1091  case GHOST_kKeyOS:
1092  case GHOST_kKey0:
1093  case GHOST_kKey1:
1094  case GHOST_kKey2:
1095  case GHOST_kKey3:
1096  case GHOST_kKey4:
1097  case GHOST_kKey5:
1098  case GHOST_kKey6:
1099  case GHOST_kKey7:
1100  case GHOST_kKey8:
1101  case GHOST_kKey9:
1102  case GHOST_kKeyNumpad0:
1103  case GHOST_kKeyNumpad1:
1104  case GHOST_kKeyNumpad2:
1105  case GHOST_kKeyNumpad3:
1106  case GHOST_kKeyNumpad4:
1107  case GHOST_kKeyNumpad5:
1108  case GHOST_kKeyNumpad6:
1109  case GHOST_kKeyNumpad7:
1110  case GHOST_kKeyNumpad8:
1111  case GHOST_kKeyNumpad9:
1113  case GHOST_kKeyNumpadEnter:
1114  case GHOST_kKeyNumpadPlus:
1115  case GHOST_kKeyNumpadMinus:
1117  case GHOST_kKeyNumpadSlash:
1118  break;
1119  default: {
1120  GHOST_TKey gkey_str = ghost_key_from_keysym(key_sym_str);
1121  if (gkey_str != GHOST_kKeyUnknown) {
1122  gkey = gkey_str;
1123  }
1124  }
1125  }
1126 #else
1127  /* In keyboards like Latin ones,
1128  * numbers needs a 'Shift' to be accessed but key_sym
1129  * is unmodified (or anyone swapping the keys with `xmodmap`).
1130  *
1131  * Here we look at the 'Shifted' version of the key.
1132  * If it is a number, then we take it instead of the normal key.
1133  *
1134  * The modified key is sent in the 'ascii's variable anyway.
1135  */
1136  if ((xke->keycode >= 10 && xke->keycode < 20) &&
1137  ((key_sym = XLookupKeysym(xke, ShiftMask)) >= XK_0) && (key_sym <= XK_9)) {
1138  /* Pass (keep shifted `key_sym`). */
1139  }
1140  else {
1141  /* regular case */
1142  key_sym = XLookupKeysym(xke, 0);
1143  }
1144 
1145  gkey = ghost_key_from_keysym_or_keycode(key_sym, m_xkb_descr, xke->keycode);
1146 
1147  if (!XLookupString(xke, &ascii, 1, nullptr, nullptr)) {
1148  ascii = '\0';
1149  }
1150 #endif
1151 
1152 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1153  /* Only used for key-press. */
1154  XIC xic = nullptr;
1155 #endif
1156 
1157  if (xke->type == KeyPress) {
1158  utf8_buf = utf8_array;
1159 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1160  /* Setting unicode on key-up events gives #XLookupNone status. */
1161  xic = window->getX11_XIC();
1162  if (xic) {
1163  Status status;
1164 
1165  /* Use utf8 because its not locale repentant, from XORG docs. */
1166  if (!(len = Xutf8LookupString(
1167  xic, xke, utf8_buf, sizeof(utf8_array) - 5, &key_sym, &status))) {
1168  utf8_buf[0] = '\0';
1169  }
1170 
1171  if (status == XBufferOverflow) {
1172  utf8_buf = (char *)malloc(len + 5);
1173  len = Xutf8LookupString(xic, xke, utf8_buf, len, &key_sym, &status);
1174  }
1175 
1176  if (ELEM(status, XLookupChars, XLookupBoth)) {
1177  if ((unsigned char)utf8_buf[0] >= 32) { /* not an ascii control character */
1178  /* do nothing for now, this is valid utf8 */
1179  }
1180  else {
1181  utf8_buf[0] = '\0';
1182  }
1183  }
1184  else if (status == XLookupKeySym) {
1185  /* this key doesn't have a text representation, it is a command
1186  * key of some sort */
1187  }
1188  else {
1189  printf("Bad keycode lookup. Keysym 0x%x Status: %s\n",
1190  (unsigned int)key_sym,
1191  (status == XLookupNone ? "XLookupNone" :
1192  status == XLookupKeySym ? "XLookupKeySym" :
1193  "Unknown status"));
1194 
1195  printf("'%.*s' %p %p\n", len, utf8_buf, xic, m_xim);
1196  }
1197  }
1198  else {
1199  utf8_buf[0] = '\0';
1200  }
1201 #endif
1202  if (!utf8_buf[0] && ascii) {
1203  utf8_buf[0] = ascii;
1204  utf8_buf[1] = '\0';
1205  }
1206  }
1207 
1208  g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, is_repeat, utf8_buf);
1209 
1210 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1211  /* when using IM for some languages such as Japanese,
1212  * one event inserts multiple utf8 characters */
1213  if (xke->type == KeyPress && xic) {
1214  unsigned char c;
1215  int i = 0;
1216  while (true) {
1217  /* Search character boundary. */
1218  if ((uchar)utf8_buf[i++] > 0x7f) {
1219  for (; i < len; ++i) {
1220  c = utf8_buf[i];
1221  if (c < 0x80 || c > 0xbf) {
1222  break;
1223  }
1224  }
1225  }
1226 
1227  if (i >= len) {
1228  break;
1229  }
1230  /* Enqueue previous character. */
1231  pushEvent(g_event);
1232 
1233  g_event = new GHOST_EventKey(
1234  getMilliSeconds(), type, window, gkey, is_repeat, &utf8_buf[i]);
1235  }
1236  }
1237 
1238  if (utf8_buf != utf8_array) {
1239  free(utf8_buf);
1240  }
1241 #endif
1242 
1243  break;
1244  }
1245 
1246  case ButtonPress:
1247  case ButtonRelease: {
1248  XButtonEvent &xbe = xe->xbutton;
1250  GHOST_TEventType type = (xbe.type == ButtonPress) ? GHOST_kEventButtonDown :
1252 
1253  /* process wheel mouse events and break, only pass on press events */
1254  if (xbe.button == Button4) {
1255  if (xbe.type == ButtonPress) {
1256  g_event = new GHOST_EventWheel(getMilliSeconds(), window, 1);
1257  }
1258  break;
1259  }
1260  if (xbe.button == Button5) {
1261  if (xbe.type == ButtonPress) {
1262  g_event = new GHOST_EventWheel(getMilliSeconds(), window, -1);
1263  }
1264  break;
1265  }
1266 
1267  /* process rest of normal mouse buttons */
1268  if (xbe.button == Button1) {
1269  gbmask = GHOST_kButtonMaskLeft;
1270  }
1271  else if (xbe.button == Button2) {
1272  gbmask = GHOST_kButtonMaskMiddle;
1273  }
1274  else if (xbe.button == Button3) {
1275  gbmask = GHOST_kButtonMaskRight;
1276  /* It seems events 6 and 7 are for horizontal scrolling.
1277  * you can re-order button mapping like this... (swaps 6,7 with 8,9)
1278  * `xmodmap -e "pointer = 1 2 3 4 5 8 9 6 7"` */
1279  }
1280  else if (xbe.button == 6) {
1281  gbmask = GHOST_kButtonMaskButton6;
1282  }
1283  else if (xbe.button == 7) {
1284  gbmask = GHOST_kButtonMaskButton7;
1285  }
1286  else if (xbe.button == 8) {
1287  gbmask = GHOST_kButtonMaskButton4;
1288  }
1289  else if (xbe.button == 9) {
1290  gbmask = GHOST_kButtonMaskButton5;
1291  }
1292  else {
1293  break;
1294  }
1295 
1296  g_event = new GHOST_EventButton(
1297  getMilliSeconds(), type, window, gbmask, window->GetTabletData());
1298  break;
1299  }
1300 
1301  /* change of size, border, layer etc. */
1302  case ConfigureNotify: {
1303  // XConfigureEvent & xce = xe->xconfigure;
1304  g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window);
1305  break;
1306  }
1307 
1308  case FocusIn:
1309  case FocusOut: {
1310  XFocusChangeEvent &xfe = xe->xfocus;
1311 
1312  /* TODO: make sure this is the correct place for activate/deactivate */
1313  // printf("X: focus %s for window %d\n",
1314  // xfe.type == FocusIn ? "in" : "out", (int) xfe.window);
1315 
1316  /* May have to look at the type of event and filter some out. */
1317 
1318  GHOST_TEventType gtype = (xfe.type == FocusIn) ? GHOST_kEventWindowActivate :
1320 
1321 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1322  XIC xic = window->getX11_XIC();
1323  if (xic) {
1324  if (xe->type == FocusIn)
1325  XSetICFocus(xic);
1326  else
1327  XUnsetICFocus(xic);
1328  }
1329 #endif
1330 
1331  g_event = new GHOST_Event(getMilliSeconds(), gtype, window);
1332  break;
1333  }
1334  case ClientMessage: {
1335  XClientMessageEvent &xcme = xe->xclient;
1336 
1337  if (((Atom)xcme.data.l[0]) == m_atom.WM_DELETE_WINDOW) {
1338  g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window);
1339  }
1340  else if (((Atom)xcme.data.l[0]) == m_atom.WM_TAKE_FOCUS) {
1341  XWindowAttributes attr;
1342  Window fwin;
1343  int revert_to;
1344 
1345  /* as ICCCM say, we need reply this event
1346  * with a #SetInputFocus, the data[1] have
1347  * the valid timestamp (send by the wm).
1348  *
1349  * Some WM send this event before the
1350  * window is really mapped (for example
1351  * change from virtual desktop), so we need
1352  * to be sure that our windows is mapped
1353  * or this call fail and close blender.
1354  */
1355  if (XGetWindowAttributes(m_display, xcme.window, &attr) == True) {
1356  if (XGetInputFocus(m_display, &fwin, &revert_to) == True) {
1357  if (attr.map_state == IsViewable) {
1358  if (fwin != xcme.window) {
1359  XSetInputFocus(m_display, xcme.window, RevertToParent, xcme.data.l[1]);
1360  }
1361  }
1362  }
1363  }
1364  }
1365  else {
1366 #ifdef WITH_XDND
1367  /* try to handle drag event
1368  * (if there's no such events, #GHOST_HandleClientMessage will return zero) */
1369  if (window->getDropTarget()->GHOST_HandleClientMessage(xe) == false) {
1370  /* Unknown client message, ignore */
1371  }
1372 #else
1373  /* Unknown client message, ignore */
1374 #endif
1375  }
1376 
1377  break;
1378  }
1379 
1380  case DestroyNotify:
1381  ::exit(-1);
1382  /* We're not interested in the following things.(yet...) */
1383  case NoExpose:
1384  case GraphicsExpose:
1385  break;
1386 
1387  case EnterNotify:
1388  case LeaveNotify: {
1389  /* #XCrossingEvents pointer leave enter window.
1390  * also do cursor move here, #MotionNotify only
1391  * happens when motion starts & ends inside window.
1392  * we only do moves when the crossing mode is 'normal'
1393  * (really crossing between windows) since some window-managers
1394  * also send grab/un-grab crossings for mouse-wheel events.
1395  */
1396  XCrossingEvent &xce = xe->xcrossing;
1397  if (xce.mode == NotifyNormal) {
1398  g_event = new GHOST_EventCursor(getMilliSeconds(),
1400  window,
1401  xce.x_root,
1402  xce.y_root,
1403  window->GetTabletData());
1404  }
1405 
1406  // printf("X: %s window %d\n",
1407  // xce.type == EnterNotify ? "entering" : "leaving", (int) xce.window);
1408 
1409  if (xce.type == EnterNotify) {
1411  }
1412  else {
1414  }
1415 
1416  break;
1417  }
1418  case MapNotify:
1419  /*
1420  * From ICCCM:
1421  * [ Clients can select for #StructureNotify on their
1422  * top-level windows to track transition between
1423  * Normal and Iconic states. Receipt of a #MapNotify
1424  * event will indicate a transition to the Normal
1425  * state, and receipt of an #UnmapNotify event will
1426  * indicate a transition to the Iconic state. ]
1427  */
1428  if (window->m_post_init == True) {
1429  /*
1430  * Now we are sure that the window is
1431  * mapped, so only need change the state.
1432  */
1433  window->setState(window->m_post_state);
1434  window->m_post_init = False;
1435  }
1436  break;
1437  case UnmapNotify:
1438  break;
1439  case MappingNotify:
1440  case ReparentNotify:
1441  break;
1442  case SelectionRequest: {
1443  XEvent nxe;
1444  Atom target, utf8_string, string, compound_text, c_string;
1445  XSelectionRequestEvent *xse = &xe->xselectionrequest;
1446 
1447  target = XInternAtom(m_display, "TARGETS", False);
1448  utf8_string = XInternAtom(m_display, "UTF8_STRING", False);
1449  string = XInternAtom(m_display, "STRING", False);
1450  compound_text = XInternAtom(m_display, "COMPOUND_TEXT", False);
1451  c_string = XInternAtom(m_display, "C_STRING", False);
1452 
1453  /* support obsolete clients */
1454  if (xse->property == None) {
1455  xse->property = xse->target;
1456  }
1457 
1458  nxe.xselection.type = SelectionNotify;
1459  nxe.xselection.requestor = xse->requestor;
1460  nxe.xselection.property = xse->property;
1461  nxe.xselection.display = xse->display;
1462  nxe.xselection.selection = xse->selection;
1463  nxe.xselection.target = xse->target;
1464  nxe.xselection.time = xse->time;
1465 
1466  /* Check to see if the requester is asking for String */
1467  if (ELEM(xse->target, utf8_string, string, compound_text, c_string)) {
1468  if (xse->selection == XInternAtom(m_display, "PRIMARY", False)) {
1469  XChangeProperty(m_display,
1470  xse->requestor,
1471  xse->property,
1472  xse->target,
1473  8,
1474  PropModeReplace,
1475  (unsigned char *)txt_select_buffer,
1476  strlen(txt_select_buffer));
1477  }
1478  else if (xse->selection == XInternAtom(m_display, "CLIPBOARD", False)) {
1479  XChangeProperty(m_display,
1480  xse->requestor,
1481  xse->property,
1482  xse->target,
1483  8,
1484  PropModeReplace,
1485  (unsigned char *)txt_cut_buffer,
1486  strlen(txt_cut_buffer));
1487  }
1488  }
1489  else if (xse->target == target) {
1490  Atom alist[5];
1491  alist[0] = target;
1492  alist[1] = utf8_string;
1493  alist[2] = string;
1494  alist[3] = compound_text;
1495  alist[4] = c_string;
1496  XChangeProperty(m_display,
1497  xse->requestor,
1498  xse->property,
1499  xse->target,
1500  32,
1501  PropModeReplace,
1502  (unsigned char *)alist,
1503  5);
1504  XFlush(m_display);
1505  }
1506  else {
1507  /* Change property to None because we do not support anything but STRING */
1508  nxe.xselection.property = None;
1509  }
1510 
1511  /* Send the event to the client 0 0 == False, #SelectionNotify */
1512  XSendEvent(m_display, xse->requestor, 0, 0, &nxe);
1513  XFlush(m_display);
1514  break;
1515  }
1516 
1517  default: {
1518 #ifdef WITH_X11_XINPUT
1519  for (GHOST_TabletX11 &xtablet : m_xtablets) {
1520  if (ELEM(xe->type, xtablet.MotionEvent, xtablet.PressEvent)) {
1521  XDeviceMotionEvent *data = (XDeviceMotionEvent *)xe;
1522  if (data->deviceid != xtablet.ID) {
1523  continue;
1524  }
1525 
1526  const unsigned char axis_first = data->first_axis;
1527  const unsigned char axes_end = axis_first + data->axes_count; /* after the last */
1528  int axis_value;
1529 
1530  /* stroke might begin without leading ProxyIn event,
1531  * this happens when window is opened when stylus is already hovering
1532  * around tablet surface */
1533  window->GetTabletData().Active = xtablet.mode;
1534 
1535  /* NOTE: This event might be generated with incomplete data-set
1536  * (don't exactly know why, looks like in some cases, if the value does not change,
1537  * it is not included in subsequent #XDeviceMotionEvent events).
1538  * So we have to check which values this event actually contains!
1539  */
1540 
1541 # define AXIS_VALUE_GET(axis, val) \
1542  ((axis_first <= axis && axes_end > axis) && \
1543  ((void)(val = data->axis_data[axis - axis_first]), true))
1544 
1545  if (AXIS_VALUE_GET(2, axis_value)) {
1546  window->GetTabletData().Pressure = axis_value / ((float)xtablet.PressureLevels);
1547  }
1548 
1549  /* NOTE(@broken): the (short) cast and the & 0xffff is bizarre and unexplained anywhere,
1550  * but I got garbage data without it. Found it in the `xidump.c` source.
1551  *
1552  * NOTE(@mont29): The '& 0xffff' just truncates the value to its two lowest bytes,
1553  * this probably means some drivers do not properly set the whole int value?
1554  * Since we convert to float afterward,
1555  * I don't think we need to cast to short here, but do not have a device to check this.
1556  */
1557  if (AXIS_VALUE_GET(3, axis_value)) {
1558  window->GetTabletData().Xtilt = (short)(axis_value & 0xffff) /
1559  ((float)xtablet.XtiltLevels);
1560  }
1561  if (AXIS_VALUE_GET(4, axis_value)) {
1562  window->GetTabletData().Ytilt = (short)(axis_value & 0xffff) /
1563  ((float)xtablet.YtiltLevels);
1564  }
1565 
1566 # undef AXIS_VALUE_GET
1567  }
1568  else if (xe->type == xtablet.ProxInEvent) {
1569  XProximityNotifyEvent *data = (XProximityNotifyEvent *)xe;
1570  if (data->deviceid != xtablet.ID) {
1571  continue;
1572  }
1573 
1574  window->GetTabletData().Active = xtablet.mode;
1575  }
1576  else if (xe->type == xtablet.ProxOutEvent) {
1578  }
1579  }
1580 #endif // WITH_X11_XINPUT
1581  break;
1582  }
1583  }
1584 
1585  if (g_event) {
1586  pushEvent(g_event);
1587  }
1588 }
1589 
1591 {
1592 
1593  /* Analyze the masks returned from #XQueryPointer. */
1594 
1595  memset((void *)m_keyboard_vector, 0, sizeof(m_keyboard_vector));
1596 
1597  XQueryKeymap(m_display, (char *)m_keyboard_vector);
1598 
1599  /* Now translate key symbols into key-codes and test with vector. */
1600 
1601  const static KeyCode shift_l = XKeysymToKeycode(m_display, XK_Shift_L);
1602  const static KeyCode shift_r = XKeysymToKeycode(m_display, XK_Shift_R);
1603  const static KeyCode control_l = XKeysymToKeycode(m_display, XK_Control_L);
1604  const static KeyCode control_r = XKeysymToKeycode(m_display, XK_Control_R);
1605  const static KeyCode alt_l = XKeysymToKeycode(m_display, XK_Alt_L);
1606  const static KeyCode alt_r = XKeysymToKeycode(m_display, XK_Alt_R);
1607  const static KeyCode super_l = XKeysymToKeycode(m_display, XK_Super_L);
1608  const static KeyCode super_r = XKeysymToKeycode(m_display, XK_Super_R);
1609 
1610  /* shift */
1612  ((m_keyboard_vector[shift_l >> 3] >> (shift_l & 7)) & 1) != 0);
1614  ((m_keyboard_vector[shift_r >> 3] >> (shift_r & 7)) & 1) != 0);
1615  /* control */
1617  ((m_keyboard_vector[control_l >> 3] >> (control_l & 7)) & 1) != 0);
1619  ((m_keyboard_vector[control_r >> 3] >> (control_r & 7)) & 1) != 0);
1620  /* alt */
1621  keys.set(GHOST_kModifierKeyLeftAlt, ((m_keyboard_vector[alt_l >> 3] >> (alt_l & 7)) & 1) != 0);
1622  keys.set(GHOST_kModifierKeyRightAlt, ((m_keyboard_vector[alt_r >> 3] >> (alt_r & 7)) & 1) != 0);
1623  /* super (windows) - only one GHOST-kModifierKeyOS, so mapping to either */
1625  (((m_keyboard_vector[super_l >> 3] >> (super_l & 7)) & 1) ||
1626  ((m_keyboard_vector[super_r >> 3] >> (super_r & 7)) & 1)) != 0);
1627 
1628  return GHOST_kSuccess;
1629 }
1630 
1632 {
1633  Window root_return, child_return;
1634  int rx, ry, wx, wy;
1635  unsigned int mask_return;
1636 
1637  if (XQueryPointer(m_display,
1638  RootWindow(m_display, DefaultScreen(m_display)),
1639  &root_return,
1640  &child_return,
1641  &rx,
1642  &ry,
1643  &wx,
1644  &wy,
1645  &mask_return) == True) {
1646  buttons.set(GHOST_kButtonMaskLeft, (mask_return & Button1Mask) != 0);
1647  buttons.set(GHOST_kButtonMaskMiddle, (mask_return & Button2Mask) != 0);
1648  buttons.set(GHOST_kButtonMaskRight, (mask_return & Button3Mask) != 0);
1649  buttons.set(GHOST_kButtonMaskButton4, (mask_return & Button4Mask) != 0);
1650  buttons.set(GHOST_kButtonMaskButton5, (mask_return & Button5Mask) != 0);
1651  }
1652  else {
1653  return GHOST_kFailure;
1654  }
1655 
1656  return GHOST_kSuccess;
1657 }
1658 
1659 static GHOST_TSuccess getCursorPosition_impl(Display *display,
1660  int32_t &x,
1661  int32_t &y,
1662  Window *child_return)
1663 {
1664  int rx, ry, wx, wy;
1665  unsigned int mask_return;
1666  Window root_return;
1667 
1668  if (XQueryPointer(display,
1669  RootWindow(display, DefaultScreen(display)),
1670  &root_return,
1671  child_return,
1672  &rx,
1673  &ry,
1674  &wx,
1675  &wy,
1676  &mask_return) == False) {
1677  return GHOST_kFailure;
1678  }
1679 
1680  x = rx;
1681  y = ry;
1682 
1683  return GHOST_kSuccess;
1684 }
1685 
1687 {
1688  Window child_return;
1689  return getCursorPosition_impl(m_display, x, y, &child_return);
1690 }
1691 
1693 {
1694 
1695  /* This is a brute force move in screen coordinates
1696  * #XWarpPointer does relative moves so first determine the
1697  * current pointer position. */
1698 
1699  int cx, cy;
1700 
1701 #ifdef WITH_XWAYLAND_HACK
1702  Window child_return = None;
1703  if (getCursorPosition_impl(m_display, cx, cy, &child_return) == GHOST_kFailure) {
1704  return GHOST_kFailure;
1705  }
1706 #else
1707  if (getCursorPosition(cx, cy) == GHOST_kFailure) {
1708  return GHOST_kFailure;
1709  }
1710 #endif
1711 
1712  int relx = x - cx;
1713  int rely = y - cy;
1714 
1715 #ifdef WITH_XWAYLAND_HACK
1716  if (use_xwayland_hack) {
1717  if (child_return != None) {
1718  XFixesHideCursor(m_display, child_return);
1719  }
1720  }
1721 #endif
1722 
1723 #if defined(WITH_X11_XINPUT) && defined(USE_X11_XINPUT_WARP)
1724  if ((m_xinput_version.present) && (m_xinput_version.major_version >= 2)) {
1725  /* Needed to account for XInput "Coordinate Transformation Matrix", see T48901 */
1726  int device_id;
1727  if (XIGetClientPointer(m_display, None, &device_id) != False) {
1728  XIWarpPointer(m_display, device_id, None, None, 0, 0, 0, 0, relx, rely);
1729  }
1730  }
1731  else
1732 #endif
1733  {
1734  XWarpPointer(m_display, None, None, 0, 0, 0, 0, relx, rely);
1735  }
1736 
1737 #ifdef WITH_XWAYLAND_HACK
1738  if (use_xwayland_hack) {
1739  if (child_return != None) {
1740  XFixesShowCursor(m_display, child_return);
1741  }
1742  }
1743 #endif
1744 
1745  XSync(m_display, 0); /* Sync to process all requests */
1746 
1747  return GHOST_kSuccess;
1748 }
1749 
1751 {
1752  GHOST_ASSERT((bad_wind != nullptr), "addDirtyWindow() nullptr ptr trapped (window)");
1753 
1754  m_dirty_windows.push_back(bad_wind);
1755 }
1756 
1757 bool GHOST_SystemX11::generateWindowExposeEvents()
1758 {
1759  vector<GHOST_WindowX11 *>::const_iterator w_start = m_dirty_windows.begin();
1760  vector<GHOST_WindowX11 *>::const_iterator w_end = m_dirty_windows.end();
1761  bool anyProcessed = false;
1762 
1763  for (; w_start != w_end; ++w_start) {
1764  GHOST_Event *g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, *w_start);
1765 
1766  (*w_start)->validate();
1767 
1768  if (g_event) {
1769  pushEvent(g_event);
1770  anyProcessed = true;
1771  }
1772  }
1773 
1774  m_dirty_windows.clear();
1775  return anyProcessed;
1776 }
1777 
1779  XkbDescPtr xkb_descr,
1780  const KeyCode keycode)
1781 {
1783  if (type == GHOST_kKeyUnknown) {
1784  if (xkb_descr) {
1785  type = ghost_key_from_keycode(xkb_descr, keycode);
1786  }
1787  }
1788  return type;
1789 }
1790 
1791 #define GXMAP(k, x, y) \
1792  case x: \
1793  k = y; \
1794  break
1795 
1796 static GHOST_TKey ghost_key_from_keysym(const KeySym key)
1797 {
1798  GHOST_TKey type;
1799 
1800  if ((key >= XK_A) && (key <= XK_Z)) {
1801  type = GHOST_TKey(key - XK_A + int(GHOST_kKeyA));
1802  }
1803  else if ((key >= XK_a) && (key <= XK_z)) {
1804  type = GHOST_TKey(key - XK_a + int(GHOST_kKeyA));
1805  }
1806  else if ((key >= XK_0) && (key <= XK_9)) {
1807  type = GHOST_TKey(key - XK_0 + int(GHOST_kKey0));
1808  }
1809  else if ((key >= XK_F1) && (key <= XK_F24)) {
1810  type = GHOST_TKey(key - XK_F1 + int(GHOST_kKeyF1));
1811  }
1812  else {
1813  switch (key) {
1814  GXMAP(type, XK_BackSpace, GHOST_kKeyBackSpace);
1815  GXMAP(type, XK_Tab, GHOST_kKeyTab);
1816  GXMAP(type, XK_ISO_Left_Tab, GHOST_kKeyTab);
1817  GXMAP(type, XK_Return, GHOST_kKeyEnter);
1818  GXMAP(type, XK_Escape, GHOST_kKeyEsc);
1819  GXMAP(type, XK_space, GHOST_kKeySpace);
1820 
1821  GXMAP(type, XK_Linefeed, GHOST_kKeyLinefeed);
1822  GXMAP(type, XK_semicolon, GHOST_kKeySemicolon);
1823  GXMAP(type, XK_period, GHOST_kKeyPeriod);
1824  GXMAP(type, XK_comma, GHOST_kKeyComma);
1825  GXMAP(type, XK_quoteright, GHOST_kKeyQuote);
1826  GXMAP(type, XK_quoteleft, GHOST_kKeyAccentGrave);
1827  GXMAP(type, XK_minus, GHOST_kKeyMinus);
1828  GXMAP(type, XK_plus, GHOST_kKeyPlus);
1829  GXMAP(type, XK_slash, GHOST_kKeySlash);
1830  GXMAP(type, XK_backslash, GHOST_kKeyBackslash);
1831  GXMAP(type, XK_equal, GHOST_kKeyEqual);
1832  GXMAP(type, XK_bracketleft, GHOST_kKeyLeftBracket);
1833  GXMAP(type, XK_bracketright, GHOST_kKeyRightBracket);
1834  GXMAP(type, XK_Pause, GHOST_kKeyPause);
1835 
1836  GXMAP(type, XK_Shift_L, GHOST_kKeyLeftShift);
1837  GXMAP(type, XK_Shift_R, GHOST_kKeyRightShift);
1838  GXMAP(type, XK_Control_L, GHOST_kKeyLeftControl);
1839  GXMAP(type, XK_Control_R, GHOST_kKeyRightControl);
1840  GXMAP(type, XK_Alt_L, GHOST_kKeyLeftAlt);
1841  GXMAP(type, XK_Alt_R, GHOST_kKeyRightAlt);
1842  GXMAP(type, XK_Super_L, GHOST_kKeyOS);
1843  GXMAP(type, XK_Super_R, GHOST_kKeyOS);
1844 
1845  GXMAP(type, XK_Insert, GHOST_kKeyInsert);
1846  GXMAP(type, XK_Delete, GHOST_kKeyDelete);
1847  GXMAP(type, XK_Home, GHOST_kKeyHome);
1848  GXMAP(type, XK_End, GHOST_kKeyEnd);
1849  GXMAP(type, XK_Page_Up, GHOST_kKeyUpPage);
1850  GXMAP(type, XK_Page_Down, GHOST_kKeyDownPage);
1851 
1852  GXMAP(type, XK_Left, GHOST_kKeyLeftArrow);
1853  GXMAP(type, XK_Right, GHOST_kKeyRightArrow);
1854  GXMAP(type, XK_Up, GHOST_kKeyUpArrow);
1855  GXMAP(type, XK_Down, GHOST_kKeyDownArrow);
1856 
1857  GXMAP(type, XK_Caps_Lock, GHOST_kKeyCapsLock);
1858  GXMAP(type, XK_Scroll_Lock, GHOST_kKeyScrollLock);
1859  GXMAP(type, XK_Num_Lock, GHOST_kKeyNumLock);
1860  GXMAP(type, XK_Menu, GHOST_kKeyApp);
1861 
1862  /* keypad events */
1863 
1864  GXMAP(type, XK_KP_0, GHOST_kKeyNumpad0);
1865  GXMAP(type, XK_KP_1, GHOST_kKeyNumpad1);
1866  GXMAP(type, XK_KP_2, GHOST_kKeyNumpad2);
1867  GXMAP(type, XK_KP_3, GHOST_kKeyNumpad3);
1868  GXMAP(type, XK_KP_4, GHOST_kKeyNumpad4);
1869  GXMAP(type, XK_KP_5, GHOST_kKeyNumpad5);
1870  GXMAP(type, XK_KP_6, GHOST_kKeyNumpad6);
1871  GXMAP(type, XK_KP_7, GHOST_kKeyNumpad7);
1872  GXMAP(type, XK_KP_8, GHOST_kKeyNumpad8);
1873  GXMAP(type, XK_KP_9, GHOST_kKeyNumpad9);
1874  GXMAP(type, XK_KP_Decimal, GHOST_kKeyNumpadPeriod);
1875 
1876  GXMAP(type, XK_KP_Insert, GHOST_kKeyNumpad0);
1877  GXMAP(type, XK_KP_End, GHOST_kKeyNumpad1);
1878  GXMAP(type, XK_KP_Down, GHOST_kKeyNumpad2);
1879  GXMAP(type, XK_KP_Page_Down, GHOST_kKeyNumpad3);
1880  GXMAP(type, XK_KP_Left, GHOST_kKeyNumpad4);
1881  GXMAP(type, XK_KP_Begin, GHOST_kKeyNumpad5);
1882  GXMAP(type, XK_KP_Right, GHOST_kKeyNumpad6);
1883  GXMAP(type, XK_KP_Home, GHOST_kKeyNumpad7);
1884  GXMAP(type, XK_KP_Up, GHOST_kKeyNumpad8);
1885  GXMAP(type, XK_KP_Page_Up, GHOST_kKeyNumpad9);
1886  GXMAP(type, XK_KP_Delete, GHOST_kKeyNumpadPeriod);
1887 
1888  GXMAP(type, XK_KP_Enter, GHOST_kKeyNumpadEnter);
1889  GXMAP(type, XK_KP_Add, GHOST_kKeyNumpadPlus);
1890  GXMAP(type, XK_KP_Subtract, GHOST_kKeyNumpadMinus);
1891  GXMAP(type, XK_KP_Multiply, GHOST_kKeyNumpadAsterisk);
1892  GXMAP(type, XK_KP_Divide, GHOST_kKeyNumpadSlash);
1893 
1894  /* Media keys in some keyboards and laptops with XFree86/Xorg */
1895 #ifdef WITH_XF86KEYSYM
1896  GXMAP(type, XF86XK_AudioPlay, GHOST_kKeyMediaPlay);
1897  GXMAP(type, XF86XK_AudioStop, GHOST_kKeyMediaStop);
1898  GXMAP(type, XF86XK_AudioPrev, GHOST_kKeyMediaFirst);
1899  GXMAP(type, XF86XK_AudioRewind, GHOST_kKeyMediaFirst);
1900  GXMAP(type, XF86XK_AudioNext, GHOST_kKeyMediaLast);
1901 # ifdef XF86XK_AudioForward /* Debian lenny's XF86keysym.h has no XF86XK_AudioForward define */
1902  GXMAP(type, XF86XK_AudioForward, GHOST_kKeyMediaLast);
1903 # endif
1904 #endif
1905  default:
1906 #ifdef WITH_GHOST_DEBUG
1907  printf("%s: unknown key: %lu / 0x%lx\n", __func__, key, key);
1908 #endif
1910  break;
1911  }
1912  }
1913 
1914  return type;
1915 }
1916 
1917 #undef GXMAP
1918 
1919 #define MAKE_ID(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a))
1920 
1921 static GHOST_TKey ghost_key_from_keycode(const XkbDescPtr xkb_descr, const KeyCode keycode)
1922 {
1923  GHOST_ASSERT(XkbKeyNameLength == 4, "Name length is invalid!");
1924  if (keycode >= xkb_descr->min_key_code && keycode <= xkb_descr->max_key_code) {
1925  const char *id_str = xkb_descr->names->keys[keycode].name;
1926  const uint32_t id = MAKE_ID(id_str[0], id_str[1], id_str[2], id_str[3]);
1927  switch (id) {
1928  case MAKE_ID('T', 'L', 'D', 'E'):
1929  return GHOST_kKeyAccentGrave;
1930 #ifdef WITH_GHOST_DEBUG
1931  default:
1932  printf("%s unhandled keycode: %.*s\n", __func__, XkbKeyNameLength, id_str);
1933  break;
1934 #endif
1935  }
1936  }
1937  else if (keycode != 0) {
1938  GHOST_ASSERT(false, "KeyCode out of range!");
1939  }
1940  return GHOST_kKeyUnknown;
1941 }
1942 
1943 #undef MAKE_ID
1944 
1945 /* From `xclip.c` #xcout() v0.11. */
1946 
1947 #define XCLIB_XCOUT_NONE 0 /* no context */
1948 #define XCLIB_XCOUT_SENTCONVSEL 1 /* sent a request */
1949 #define XCLIB_XCOUT_INCR 2 /* in an incr loop */
1950 #define XCLIB_XCOUT_FALLBACK 3 /* STRING failed, need fallback to UTF8 */
1951 #define XCLIB_XCOUT_FALLBACK_UTF8 4 /* UTF8 failed, move to compound. */
1952 #define XCLIB_XCOUT_FALLBACK_COMP 5 /* compound failed, move to text. */
1953 #define XCLIB_XCOUT_FALLBACK_TEXT 6
1954 
1955 /* Retrieves the contents of a selections. */
1957  Atom sel,
1958  Atom target,
1959  unsigned char **txt,
1960  unsigned long *len,
1961  unsigned int *context) const
1962 {
1963  Atom pty_type;
1964  int pty_format;
1965  unsigned char *buffer;
1966  unsigned long pty_size, pty_items;
1967  unsigned char *ltxt = *txt;
1968 
1969  const vector<GHOST_IWindow *> &win_vec = m_windowManager->getWindows();
1970  vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
1971  GHOST_WindowX11 *window = static_cast<GHOST_WindowX11 *>(*win_it);
1972  Window win = window->getXWindow();
1973 
1974  switch (*context) {
1975  /* There is no context, do an XConvertSelection() */
1976  case XCLIB_XCOUT_NONE:
1977  /* Initialize return length to 0. */
1978  if (*len > 0) {
1979  free(*txt);
1980  *len = 0;
1981  }
1982 
1983  /* Send a selection request */
1984  XConvertSelection(m_display, sel, target, m_atom.XCLIP_OUT, win, CurrentTime);
1986  return;
1987 
1989  if (evt->type != SelectionNotify) {
1990  return;
1991  }
1992 
1993  if (target == m_atom.UTF8_STRING && evt->xselection.property == None) {
1995  return;
1996  }
1997  if (target == m_atom.COMPOUND_TEXT && evt->xselection.property == None) {
1999  return;
2000  }
2001  if (target == m_atom.TEXT && evt->xselection.property == None) {
2003  return;
2004  }
2005 
2006  /* find the size and format of the data in property */
2007  XGetWindowProperty(m_display,
2008  win,
2009  m_atom.XCLIP_OUT,
2010  0,
2011  0,
2012  False,
2013  AnyPropertyType,
2014  &pty_type,
2015  &pty_format,
2016  &pty_items,
2017  &pty_size,
2018  &buffer);
2019  XFree(buffer);
2020 
2021  if (pty_type == m_atom.INCR) {
2022  /* start INCR mechanism by deleting property */
2023  XDeleteProperty(m_display, win, m_atom.XCLIP_OUT);
2024  XFlush(m_display);
2026  return;
2027  }
2028 
2029  /* if it's not incr, and not format == 8, then there's
2030  * nothing in the selection (that xclip understands, anyway) */
2031 
2032  if (pty_format != 8) {
2034  return;
2035  }
2036 
2037  /* Not using INCR mechanism, just read the property. */
2038  XGetWindowProperty(m_display,
2039  win,
2040  m_atom.XCLIP_OUT,
2041  0,
2042  (long)pty_size,
2043  False,
2044  AnyPropertyType,
2045  &pty_type,
2046  &pty_format,
2047  &pty_items,
2048  &pty_size,
2049  &buffer);
2050 
2051  /* finished with property, delete it */
2052  XDeleteProperty(m_display, win, m_atom.XCLIP_OUT);
2053 
2054  /* copy the buffer to the pointer for returned data */
2055  ltxt = (unsigned char *)malloc(pty_items);
2056  memcpy(ltxt, buffer, pty_items);
2057 
2058  /* set the length of the returned data */
2059  *len = pty_items;
2060  *txt = ltxt;
2061 
2062  /* free the buffer */
2063  XFree(buffer);
2064 
2066 
2067  /* complete contents of selection fetched, return 1 */
2068  return;
2069 
2070  case XCLIB_XCOUT_INCR:
2071  /* To use the INCR method, we basically delete the
2072  * property with the selection in it, wait for an
2073  * event indicating that the property has been created,
2074  * then read it, delete it, etc. */
2075 
2076  /* make sure that the event is relevant */
2077  if (evt->type != PropertyNotify) {
2078  return;
2079  }
2080 
2081  /* skip unless the property has a new value */
2082  if (evt->xproperty.state != PropertyNewValue) {
2083  return;
2084  }
2085 
2086  /* check size and format of the property */
2087  XGetWindowProperty(m_display,
2088  win,
2089  m_atom.XCLIP_OUT,
2090  0,
2091  0,
2092  False,
2093  AnyPropertyType,
2094  &pty_type,
2095  &pty_format,
2096  &pty_items,
2097  &pty_size,
2098  &buffer);
2099 
2100  if (pty_format != 8) {
2101  /* property does not contain text, delete it
2102  * to tell the other X client that we have read
2103  * it and to send the next property */
2104  XFree(buffer);
2105  XDeleteProperty(m_display, win, m_atom.XCLIP_OUT);
2106  return;
2107  }
2108 
2109  if (pty_size == 0) {
2110  /* no more data, exit from loop */
2111  XFree(buffer);
2112  XDeleteProperty(m_display, win, m_atom.XCLIP_OUT);
2114 
2115  /* this means that an INCR transfer is now
2116  * complete, return 1 */
2117  return;
2118  }
2119 
2120  XFree(buffer);
2121 
2122  /* if we have come this far, the property contains
2123  * text, we know the size. */
2124  XGetWindowProperty(m_display,
2125  win,
2126  m_atom.XCLIP_OUT,
2127  0,
2128  (long)pty_size,
2129  False,
2130  AnyPropertyType,
2131  &pty_type,
2132  &pty_format,
2133  &pty_items,
2134  &pty_size,
2135  &buffer);
2136 
2137  /* allocate memory to accommodate data in *txt */
2138  if (*len == 0) {
2139  *len = pty_items;
2140  ltxt = (unsigned char *)malloc(*len);
2141  }
2142  else {
2143  *len += pty_items;
2144  ltxt = (unsigned char *)realloc(ltxt, *len);
2145  }
2146 
2147  /* add data to ltxt */
2148  memcpy(&ltxt[*len - pty_items], buffer, pty_items);
2149 
2150  *txt = ltxt;
2151  XFree(buffer);
2152 
2153  /* delete property to get the next item */
2154  XDeleteProperty(m_display, win, m_atom.XCLIP_OUT);
2155  XFlush(m_display);
2156  return;
2157  }
2158 }
2159 
2160 char *GHOST_SystemX11::getClipboard(bool selection) const
2161 {
2162  Atom sseln;
2163  Atom target = m_atom.UTF8_STRING;
2164  Window owner;
2165 
2166  /* from xclip.c doOut() v0.11 */
2167  char *sel_buf;
2168  unsigned long sel_len = 0;
2169  XEvent evt;
2170  unsigned int context = XCLIB_XCOUT_NONE;
2171 
2172  if (selection == True) {
2173  sseln = m_atom.PRIMARY;
2174  }
2175  else {
2176  sseln = m_atom.CLIPBOARD;
2177  }
2178 
2179  const vector<GHOST_IWindow *> &win_vec = m_windowManager->getWindows();
2180  vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
2181  GHOST_WindowX11 *window = static_cast<GHOST_WindowX11 *>(*win_it);
2182  Window win = window->getXWindow();
2183 
2184  /* check if we are the owner. */
2185  owner = XGetSelectionOwner(m_display, sseln);
2186  if (owner == win) {
2187  if (sseln == m_atom.CLIPBOARD) {
2188  sel_buf = (char *)malloc(strlen(txt_cut_buffer) + 1);
2189  strcpy(sel_buf, txt_cut_buffer);
2190  return sel_buf;
2191  }
2192  sel_buf = (char *)malloc(strlen(txt_select_buffer) + 1);
2193  strcpy(sel_buf, txt_select_buffer);
2194  return sel_buf;
2195  }
2196  if (owner == None) {
2197  return nullptr;
2198  }
2199 
2200  /* Restore events so copy doesn't swallow other event types (keyboard/mouse). */
2201  vector<XEvent> restore_events;
2202 
2203  while (true) {
2204  /* only get an event if xcout() is doing something */
2205  bool restore_this_event = false;
2206  if (context != XCLIB_XCOUT_NONE) {
2207  XNextEvent(m_display, &evt);
2208  restore_this_event = (evt.type != SelectionNotify);
2209  }
2210 
2211  /* fetch the selection, or part of it */
2212  getClipboard_xcout(&evt, sseln, target, (unsigned char **)&sel_buf, &sel_len, &context);
2213 
2214  if (restore_this_event) {
2215  restore_events.push_back(evt);
2216  }
2217 
2218  /* Fallback is needed. Set #XA_STRING to target and restart the loop. */
2219  if (context == XCLIB_XCOUT_FALLBACK) {
2221  target = m_atom.STRING;
2222  continue;
2223  }
2225  /* utf8 fail, move to compound text. */
2227  target = m_atom.COMPOUND_TEXT;
2228  continue;
2229  }
2231  /* Compound text fail, move to text. */
2233  target = m_atom.TEXT;
2234  continue;
2235  }
2237  /* Text fail, nothing else to try, break. */
2239  }
2240 
2241  /* Only continue if #xcout() is doing something. */
2242  if (context == XCLIB_XCOUT_NONE) {
2243  break;
2244  }
2245  }
2246 
2247  while (!restore_events.empty()) {
2248  XPutBackEvent(m_display, &restore_events.back());
2249  restore_events.pop_back();
2250  }
2251 
2252  if (sel_len) {
2253  /* Only print the buffer out, and free it, if it's not empty. */
2254  char *tmp_data = (char *)malloc(sel_len + 1);
2255  memcpy(tmp_data, (char *)sel_buf, sel_len);
2256  tmp_data[sel_len] = '\0';
2257 
2258  if (sseln == m_atom.STRING) {
2259  XFree(sel_buf);
2260  }
2261  else {
2262  free(sel_buf);
2263  }
2264 
2265  return tmp_data;
2266  }
2267  return nullptr;
2268 }
2269 
2270 void GHOST_SystemX11::putClipboard(const char *buffer, bool selection) const
2271 {
2272  Window m_window, owner;
2273 
2274  const vector<GHOST_IWindow *> &win_vec = m_windowManager->getWindows();
2275  vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
2276  GHOST_WindowX11 *window = static_cast<GHOST_WindowX11 *>(*win_it);
2277  m_window = window->getXWindow();
2278 
2279  if (buffer) {
2280  if (selection == False) {
2281  XSetSelectionOwner(m_display, m_atom.CLIPBOARD, m_window, CurrentTime);
2282  owner = XGetSelectionOwner(m_display, m_atom.CLIPBOARD);
2283  if (txt_cut_buffer) {
2284  free((void *)txt_cut_buffer);
2285  }
2286 
2287  txt_cut_buffer = (char *)malloc(strlen(buffer) + 1);
2288  strcpy(txt_cut_buffer, buffer);
2289  }
2290  else {
2291  XSetSelectionOwner(m_display, m_atom.PRIMARY, m_window, CurrentTime);
2292  owner = XGetSelectionOwner(m_display, m_atom.PRIMARY);
2293  if (txt_select_buffer) {
2294  free((void *)txt_select_buffer);
2295  }
2296 
2297  txt_select_buffer = (char *)malloc(strlen(buffer) + 1);
2298  strcpy(txt_select_buffer, buffer);
2299  }
2300 
2301  if (owner != m_window) {
2302  fprintf(stderr, "failed to own primary\n");
2303  }
2304  }
2305 }
2306 
2307 /* -------------------------------------------------------------------- */
2311 class DialogData {
2312  public:
2313  /* Width of the dialog. */
2315  /* Height of the dialog. */
2317  /* Default padding (x direction) between controls and edge of dialog. */
2319  /* Default padding (y direction) between controls and edge of dialog. */
2321  /* Width of a single button. */
2323  /* Height of a single button. */
2325  /* Inset of a button to its text. */
2327  /* Size of the border of the button. */
2329  /* Height of a line of text */
2331  /* Offset of the text inside the button. */
2333 
2334  /* Construct a new #DialogData with the default settings. */
2336  : width(640),
2337  height(175),
2338  padding_x(10),
2339  padding_y(5),
2340  button_width(130),
2341  button_height(24),
2342  button_inset_x(10),
2343  button_border_size(1),
2344  line_height(16)
2345  {
2347  }
2348 
2349  void drawButton(Display *display,
2350  Window &window,
2351  GC &borderGC,
2352  GC &buttonGC,
2353  uint button_num,
2354  const char *label)
2355  {
2356  XFillRectangle(display,
2357  window,
2358  borderGC,
2359  width - (padding_x + button_width) * button_num,
2361  button_width,
2362  button_height);
2363 
2364  XFillRectangle(display,
2365  window,
2366  buttonGC,
2367  width - (padding_x + button_width) * button_num + button_border_size,
2371 
2372  XDrawString(display,
2373  window,
2374  borderGC,
2375  width - (padding_x + button_width) * button_num + button_inset_x,
2377  label,
2378  strlen(label));
2379  }
2380 
2381  /* Is the mouse inside the given button */
2382  bool isInsideButton(XEvent &e, uint button_num)
2383  {
2384  return ((e.xmotion.y > height - padding_y - button_height) &&
2385  (e.xmotion.y < height - padding_y) &&
2386  (e.xmotion.x > width - (padding_x + button_width) * button_num) &&
2387  (e.xmotion.x < width - padding_x - (padding_x + button_width) * (button_num - 1)));
2388  }
2389 };
2390 
2391 static void split(const char *text, const char *seps, char ***str, int *count)
2392 {
2393  char *tok, *data;
2394  int i;
2395  *count = 0;
2396 
2397  data = strdup(text);
2398  for (tok = strtok(data, seps); tok != nullptr; tok = strtok(nullptr, seps)) {
2399  (*count)++;
2400  }
2401  free(data);
2402 
2403  data = strdup(text);
2404  *str = (char **)malloc((size_t)(*count) * sizeof(char *));
2405  for (i = 0, tok = strtok(data, seps); tok != nullptr; tok = strtok(nullptr, seps), i++) {
2406  (*str)[i] = strdup(tok);
2407  }
2408  free(data);
2409 }
2410 
2412  const char *message,
2413  const char *help_label,
2414  const char *continue_label,
2415  const char *link,
2416  GHOST_DialogOptions /*dialog_options*/) const
2417 {
2418  char **text_splitted = nullptr;
2419  int textLines = 0;
2420  split(message, "\n", &text_splitted, &textLines);
2421 
2422  DialogData dialog_data;
2423  XSizeHints hints;
2424 
2425  Window window;
2426  XEvent e;
2427  int screen = DefaultScreen(m_display);
2428  window = XCreateSimpleWindow(m_display,
2429  RootWindow(m_display, screen),
2430  0,
2431  0,
2432  dialog_data.width,
2433  dialog_data.height,
2434  1,
2435  BlackPixel(m_display, screen),
2436  WhitePixel(m_display, screen));
2437 
2438  /* Window Should not be resizable */
2439  {
2440  hints.flags = PSize | PMinSize | PMaxSize;
2441  hints.min_width = hints.max_width = hints.base_width = dialog_data.width;
2442  hints.min_height = hints.max_height = hints.base_height = dialog_data.height;
2443  XSetWMNormalHints(m_display, window, &hints);
2444  }
2445 
2446  /* Set title */
2447  {
2448  Atom wm_Name = XInternAtom(m_display, "_NET_WM_NAME", False);
2449  Atom utf8Str = XInternAtom(m_display, "UTF8_STRING", False);
2450 
2451  Atom winType = XInternAtom(m_display, "_NET_WM_WINDOW_TYPE", False);
2452  Atom typeDialog = XInternAtom(m_display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
2453 
2454  XChangeProperty(m_display,
2455  window,
2456  wm_Name,
2457  utf8Str,
2458  8,
2459  PropModeReplace,
2460  (const unsigned char *)title,
2461  (int)strlen(title));
2462 
2463  XChangeProperty(
2464  m_display, window, winType, XA_ATOM, 32, PropModeReplace, (unsigned char *)&typeDialog, 1);
2465  }
2466 
2467  /* Create buttons GC */
2468  XGCValues buttonBorderGCValues;
2469  buttonBorderGCValues.foreground = BlackPixel(m_display, screen);
2470  buttonBorderGCValues.background = WhitePixel(m_display, screen);
2471  XGCValues buttonGCValues;
2472  buttonGCValues.foreground = WhitePixel(m_display, screen);
2473  buttonGCValues.background = BlackPixel(m_display, screen);
2474 
2475  GC buttonBorderGC = XCreateGC(m_display, window, GCForeground, &buttonBorderGCValues);
2476  GC buttonGC = XCreateGC(m_display, window, GCForeground, &buttonGCValues);
2477 
2478  XSelectInput(m_display, window, ExposureMask | ButtonPressMask | ButtonReleaseMask);
2479  XMapWindow(m_display, window);
2480 
2481  while (true) {
2482  XNextEvent(m_display, &e);
2483  if (e.type == Expose) {
2484  for (int i = 0; i < textLines; i++) {
2485  XDrawString(m_display,
2486  window,
2487  DefaultGC(m_display, screen),
2488  dialog_data.padding_x,
2489  dialog_data.padding_x + (i + 1) * dialog_data.line_height,
2490  text_splitted[i],
2491  (int)strlen(text_splitted[i]));
2492  }
2493  dialog_data.drawButton(m_display, window, buttonBorderGC, buttonGC, 1, continue_label);
2494  if (strlen(link)) {
2495  dialog_data.drawButton(m_display, window, buttonBorderGC, buttonGC, 2, help_label);
2496  }
2497  }
2498  else if (e.type == ButtonRelease) {
2499  if (dialog_data.isInsideButton(e, 1)) {
2500  break;
2501  }
2502  if (dialog_data.isInsideButton(e, 2)) {
2503  if (strlen(link)) {
2504  string cmd = "xdg-open \"" + string(link) + "\"";
2505  if (system(cmd.c_str()) != 0) {
2506  GHOST_PRINTF("GHOST_SystemX11::showMessageBox: Unable to run system command [%s]",
2507  cmd.c_str());
2508  }
2509  }
2510  break;
2511  }
2512  }
2513  }
2514 
2515  for (int i = 0; i < textLines; i++) {
2516  free(text_splitted[i]);
2517  }
2518  free(text_splitted);
2519 
2520  XDestroyWindow(m_display, window);
2521  XFreeGC(m_display, buttonBorderGC);
2522  XFreeGC(m_display, buttonGC);
2523  return GHOST_kSuccess;
2524 }
2525 
2528 #ifdef WITH_XDND
2529 GHOST_TSuccess GHOST_SystemX11::pushDragDropEvent(GHOST_TEventType eventType,
2530  GHOST_TDragnDropTypes draggedObjectType,
2531  GHOST_IWindow *window,
2532  int mouseX,
2533  int mouseY,
2534  void *data)
2535 {
2536  GHOST_SystemX11 *system = ((GHOST_SystemX11 *)getSystem());
2537  return system->pushEvent(new GHOST_EventDragnDrop(
2538  system->getMilliSeconds(), eventType, draggedObjectType, window, mouseX, mouseY, data));
2539 }
2540 #endif
2548 int GHOST_X11_ApplicationErrorHandler(Display *display, XErrorEvent *event)
2549 {
2551  if (!system->isDebugEnabled()) {
2552  return 0;
2553  }
2554 
2555  char error_code_str[512];
2556 
2557  XGetErrorText(display, event->error_code, error_code_str, sizeof(error_code_str));
2558 
2559  fprintf(stderr,
2560  "Received X11 Error:\n"
2561  "\terror code: %d\n"
2562  "\trequest code: %d\n"
2563  "\tminor code: %d\n"
2564  "\terror text: %s\n",
2565  event->error_code,
2566  event->request_code,
2567  event->minor_code,
2568  error_code_str);
2569 
2570  /* No exit! - but keep lint happy */
2571  return 0;
2572 }
2573 
2574 int GHOST_X11_ApplicationIOErrorHandler(Display * /*display*/)
2575 {
2577  if (!system->isDebugEnabled()) {
2578  return 0;
2579  }
2580 
2581  fprintf(stderr, "Ignoring Xlib error: error IO\n");
2582 
2583  /* No exit! - but keep lint happy */
2584  return 0;
2585 }
2586 
2587 #ifdef WITH_X11_XINPUT
2588 
2589 static bool is_filler_char(char c)
2590 {
2591  return isspace(c) || ELEM(c, '_', '-', ';', ':');
2592 }
2593 
2594 /* These C functions are copied from Wine 3.12's `wintab.c` */
2595 static bool match_token(const char *haystack, const char *needle)
2596 {
2597  const char *h, *n;
2598  for (h = haystack; *h;) {
2599  while (*h && is_filler_char(*h)) {
2600  h++;
2601  }
2602  if (!*h) {
2603  break;
2604  }
2605 
2606  for (n = needle; *n && *h && tolower(*h) == tolower(*n); n++) {
2607  h++;
2608  }
2609  if (!*n && (is_filler_char(*h) || !*h)) {
2610  return true;
2611  }
2612 
2613  while (*h && !is_filler_char(*h)) {
2614  h++;
2615  }
2616  }
2617  return false;
2618 }
2619 
2620 /* Determining if an X device is a Tablet style device is an imperfect science.
2621  * We rely on common conventions around device names as well as the type reported
2622  * by Wacom tablets. This code will likely need to be expanded for alternate tablet types
2623  *
2624  * Wintab refers to any device that interacts with the tablet as a cursor,
2625  * (stylus, eraser, tablet mouse, airbrush, etc)
2626  * this is not to be confused with wacom x11 configuration "cursor" device.
2627  * Wacoms x11 config "cursor" refers to its device slot (which we mirror with
2628  * our gSysCursors) for puck like devices (tablet mice essentially).
2629  */
2630 static GHOST_TTabletMode tablet_mode_from_name(const char *name, const char *type)
2631 {
2632  int i;
2633  static const char *tablet_stylus_whitelist[] = {"stylus", "wizardpen", "acecad", "pen", nullptr};
2634 
2635  static const char *type_blacklist[] = {"pad", "cursor", "touch", nullptr};
2636 
2637  /* Skip some known unsupported types. */
2638  for (i = 0; type_blacklist[i] != nullptr; i++) {
2639  if (type && (strcasecmp(type, type_blacklist[i]) == 0)) {
2640  return GHOST_kTabletModeNone;
2641  }
2642  }
2643 
2644  /* First check device type to avoid cases where name is "Pen and Eraser" and type is "ERASER" */
2645  for (i = 0; tablet_stylus_whitelist[i] != nullptr; i++) {
2646  if (type && match_token(type, tablet_stylus_whitelist[i])) {
2647  return GHOST_kTabletModeStylus;
2648  }
2649  }
2650  if (type && match_token(type, "eraser")) {
2651  return GHOST_kTabletModeEraser;
2652  }
2653  for (i = 0; tablet_stylus_whitelist[i] != nullptr; i++) {
2654  if (name && match_token(name, tablet_stylus_whitelist[i])) {
2655  return GHOST_kTabletModeStylus;
2656  }
2657  }
2658  if (name && match_token(name, "eraser")) {
2659  return GHOST_kTabletModeEraser;
2660  }
2661 
2662  return GHOST_kTabletModeNone;
2663 }
2664 
2665 /* End code copied from Wine. */
2666 
2667 void GHOST_SystemX11::refreshXInputDevices()
2668 {
2669  if (m_xinput_version.present) {
2670  /* Close tablet devices. */
2671  clearXInputDevices();
2672 
2673  /* Install our error handler to override Xlib's termination behavior */
2674  GHOST_X11_ERROR_HANDLERS_OVERRIDE(handler_store);
2675 
2676  {
2677  int device_count;
2678  XDeviceInfo *device_info = XListInputDevices(m_display, &device_count);
2679 
2680  for (int i = 0; i < device_count; ++i) {
2681  char *device_type = device_info[i].type ? XGetAtomName(m_display, device_info[i].type) :
2682  nullptr;
2683  GHOST_TTabletMode tablet_mode = tablet_mode_from_name(device_info[i].name, device_type);
2684 
2685  // printf("Tablet type:'%s', name:'%s', index:%d\n", device_type, device_info[i].name, i);
2686 
2687  if (device_type) {
2688  XFree((void *)device_type);
2689  }
2690 
2692  continue;
2693  }
2694 
2695  GHOST_TabletX11 xtablet = {tablet_mode};
2696  xtablet.ID = device_info[i].id;
2697  xtablet.Device = XOpenDevice(m_display, xtablet.ID);
2698 
2699  if (xtablet.Device != nullptr) {
2700  /* Find how many pressure levels tablet has */
2701  XAnyClassPtr ici = device_info[i].inputclassinfo;
2702 
2703  if (ici != nullptr) {
2704  for (int j = 0; j < device_info[i].num_classes; ++j) {
2705  if (ici->c_class == ValuatorClass) {
2706  XValuatorInfo *xvi = (XValuatorInfo *)ici;
2707  if (xvi->axes != nullptr) {
2708  xtablet.PressureLevels = xvi->axes[2].max_value;
2709 
2710  if (xvi->num_axes > 3) {
2711  /* This is assuming that the tablet has the same tilt resolution in both
2712  * positive and negative directions. It would be rather weird if it didn't. */
2713  xtablet.XtiltLevels = xvi->axes[3].max_value;
2714  xtablet.YtiltLevels = xvi->axes[4].max_value;
2715  }
2716  else {
2717  xtablet.XtiltLevels = 0;
2718  xtablet.YtiltLevels = 0;
2719  }
2720 
2721  break;
2722  }
2723  }
2724 
2725  ici = (XAnyClassPtr)(((char *)ici) + ici->length);
2726  }
2727  }
2728 
2729  m_xtablets.push_back(xtablet);
2730  }
2731  }
2732 
2733  XFreeDeviceList(device_info);
2734  }
2735 
2736  GHOST_X11_ERROR_HANDLERS_RESTORE(handler_store);
2737  }
2738 }
2739 
2740 void GHOST_SystemX11::clearXInputDevices()
2741 {
2742  for (GHOST_TabletX11 &xtablet : m_xtablets) {
2743  if (xtablet.Device)
2744  XCloseDevice(m_display, xtablet.Device);
2745  }
2746 
2747  m_xtablets.clear();
2748 }
2749 
2750 #endif /* WITH_X11_XINPUT */
typedef float(TangentPoint)[2]
void BLI_kdtree_nd_() free(KDTree *tree)
Definition: kdtree_impl.h:102
unsigned char uchar
Definition: BLI_sys_types.h:70
unsigned int uint
Definition: BLI_sys_types.h:67
#define ARRAY_SIZE(arr)
#define ELEM(...)
#define GHOST_OPENGL_EGL_CONTEXT_FLAGS
#define GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY
#define GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY
#define GHOST_OPENGL_GLX_CONTEXT_FLAGS
#define GHOST_PRINTF(x,...)
Definition: GHOST_Debug.h:36
#define GHOST_ASSERT(x, info)
Definition: GHOST_Debug.h:54
#define GHOST_PRINT(x)
Definition: GHOST_Debug.h:35
static void DeviceAdded(uint32_t unused)
static void DeviceRemoved(uint32_t unused)
static char * txt_select_buffer
static Bool init_timestamp_scanner(Display *, XEvent *event, XPointer arg)
static void split(const char *text, const char *seps, char ***str, int *count)
int GHOST_X11_ApplicationErrorHandler(Display *display, XErrorEvent *event)
#define XCLIB_XCOUT_INCR
#define XCLIB_XCOUT_FALLBACK_UTF8
static char * txt_cut_buffer
#define GXMAP(k, x, y)
#define XCLIB_XCOUT_FALLBACK
#define GHOST_INTERN_ATOM(atom)
static void SleepTillEvent(Display *display, int64_t maxSleep)
#define XCLIB_XCOUT_FALLBACK_COMP
static GHOST_TKey ghost_key_from_keysym_or_keycode(const KeySym key, const XkbDescPtr xkb_descr, const KeyCode keycode)
#define MAKE_ID(a, b, c, d)
static uchar bit_is_on(const uchar *ptr, int bit)
#define XCLIB_XCOUT_SENTCONVSEL
#define XCLIB_XCOUT_FALLBACK_TEXT
static GHOST_TKey ghost_key_from_keycode(const XkbDescPtr xkb_descr, const KeyCode keycode)
int GHOST_X11_ApplicationIOErrorHandler(Display *)
#define XCLIB_XCOUT_NONE
static GHOST_TSuccess getCursorPosition_impl(Display *display, int32_t &x, int32_t &y, Window *child_return)
#define GHOST_INTERN_ATOM_IF_EXISTS(atom)
static GHOST_TKey ghost_key_from_keysym(const KeySym key)
#define GHOST_X11_ERROR_HANDLERS_OVERRIDE(var)
#define GHOST_X11_ERROR_HANDLERS_RESTORE(var)
GHOST_TWindowState
Definition: GHOST_Types.h:129
GHOST_TEventType
Definition: GHOST_Types.h:169
@ GHOST_kEventWindowClose
Definition: GHOST_Types.h:189
@ GHOST_kEventWindowSize
Definition: GHOST_Types.h:193
@ GHOST_kEventCursorMove
Definition: GHOST_Types.h:172
@ GHOST_kEventButtonUp
Definition: GHOST_Types.h:174
@ GHOST_kEventWindowActivate
Definition: GHOST_Types.h:190
@ GHOST_kEventWindowUpdate
Definition: GHOST_Types.h:192
@ GHOST_kEventWindowDeactivate
Definition: GHOST_Types.h:191
@ GHOST_kEventButtonDown
Definition: GHOST_Types.h:173
@ GHOST_kEventKeyDown
Definition: GHOST_Types.h:183
@ GHOST_kEventKeyUp
Definition: GHOST_Types.h:184
GHOST_TTabletMode
Definition: GHOST_Types.h:83
@ GHOST_kTabletModeEraser
Definition: GHOST_Types.h:86
@ GHOST_kTabletModeStylus
Definition: GHOST_Types.h:85
@ GHOST_kTabletModeNone
Definition: GHOST_Types.h:84
@ GHOST_glAlphaBackground
Definition: GHOST_Types.h:64
@ GHOST_glStereoVisual
Definition: GHOST_Types.h:62
@ GHOST_glDebugContext
Definition: GHOST_Types.h:63
GHOST_TKey
Definition: GHOST_Types.h:259
@ GHOST_kKeyInsert
Definition: GHOST_Types.h:346
@ GHOST_kKeySemicolon
Definition: GHOST_Types.h:288
@ GHOST_kKey5
Definition: GHOST_Types.h:282
@ GHOST_kKeyMediaPlay
Definition: GHOST_Types.h:398
@ GHOST_kKeyQuote
Definition: GHOST_Types.h:269
@ GHOST_kKey4
Definition: GHOST_Types.h:281
@ GHOST_kKeyNumpad3
Definition: GHOST_Types.h:357
@ GHOST_kKeyAccentGrave
Definition: GHOST_Types.h:322
@ GHOST_kKeyNumpad1
Definition: GHOST_Types.h:355
@ GHOST_kKeyLeftAlt
Definition: GHOST_Types.h:328
@ GHOST_kKey3
Definition: GHOST_Types.h:280
@ 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_kKeyNumpad4
Definition: GHOST_Types.h:358
@ 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_kKey6
Definition: GHOST_Types.h:283
@ GHOST_kKeyMediaStop
Definition: GHOST_Types.h:399
@ GHOST_kKeyBackSpace
Definition: GHOST_Types.h:261
@ GHOST_kKey0
Definition: GHOST_Types.h:277
@ GHOST_kKeyDownPage
Definition: GHOST_Types.h:351
@ GHOST_kKeyDownArrow
Definition: GHOST_Types.h:341
@ GHOST_kKeyNumpadPeriod
Definition: GHOST_Types.h:364
@ GHOST_kKeyF1
Definition: GHOST_Types.h:372
@ GHOST_kKeyNumpadAsterisk
Definition: GHOST_Types.h:368
@ GHOST_kKeyLeftControl
Definition: GHOST_Types.h:326
@ GHOST_kKeyLeftBracket
Definition: GHOST_Types.h:319
@ GHOST_kKey1
Definition: GHOST_Types.h:278
@ GHOST_kKeyTab
Definition: GHOST_Types.h:262
@ GHOST_kKey8
Definition: GHOST_Types.h:285
@ 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_kKeyNumpad2
Definition: GHOST_Types.h:356
@ 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_kKey9
Definition: GHOST_Types.h:286
@ GHOST_kKeyNumpad5
Definition: GHOST_Types.h:359
@ GHOST_kKeyLeftArrow
Definition: GHOST_Types.h:338
@ GHOST_kKeyEqual
Definition: GHOST_Types.h:289
@ GHOST_kKey7
Definition: GHOST_Types.h:284
@ GHOST_kKeyHome
Definition: GHOST_Types.h:348
@ GHOST_kKeyNumpad6
Definition: GHOST_Types.h:360
@ GHOST_kKeyNumpad8
Definition: GHOST_Types.h:362
@ GHOST_kKeyNumpad9
Definition: GHOST_Types.h:363
@ 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_kKeyA
Definition: GHOST_Types.h:292
@ GHOST_kKey2
Definition: GHOST_Types.h:279
@ GHOST_kKeyMediaFirst
Definition: GHOST_Types.h:400
@ GHOST_kKeyNumpad7
Definition: GHOST_Types.h:361
@ 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_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
@ GHOST_kFireTimeNever
Definition: GHOST_Types.h:116
GHOST_TDragnDropTypes
Definition: GHOST_Types.h:474
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_DialogOptions
Definition: GHOST_Types.h:67
_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
__forceinline const avxb select(const avxb &m, const avxb &t, const avxb &f)
Definition: avxb.h:154
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition: btDbvt.cpp:299
bool isInsideButton(XEvent &e, uint button_num)
void drawButton(Display *display, Window &window, GC &borderGC, GC &buttonGC, uint button_num, const char *label)
static GHOST_ISystem * getSystem()
virtual bool isDebugEnabled()=0
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=false, const bool is_dialog=false, const GHOST_IWindow *parentWindow=0)
GHOST_TSuccess disposeContext(GHOST_IContext *context)
void getClipboard_xcout(const XEvent *evt, Atom sel, Atom target, unsigned char **txt, unsigned long *len, unsigned int *context) const
GHOST_IContext * createOffscreenContext(GHOST_GLSettings glSettings)
void addDirtyWindow(GHOST_WindowX11 *bad_wind)
GHOST_TSuccess init()
char * getClipboard(bool selection) const
Atom _NET_WM_STATE_MAXIMIZED_VERT
GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const
GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const
void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const
void putClipboard(const char *buffer, bool selection) const
GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const
GHOST_TSuccess setCursorPosition(int32_t x, int32_t y)
void getAllDisplayDimensions(uint32_t &width, uint32_t &height) const
uint64_t getMilliSeconds() const
uint8_t getNumDisplays() const
bool processEvents(bool waitForEvent)
struct GHOST_SystemX11::@1294 m_atom
GHOST_TSuccess showMessageBox(const char *title, const char *message, const char *help_label, const char *continue_label, const char *link, GHOST_DialogOptions dialog_options) const
Atom _NET_WM_STATE_MAXIMIZED_HORZ
virtual GHOST_TSuccess exit()
virtual GHOST_TSuccess init()
GHOST_TimerManager * getTimerManager() const
Definition: GHOST_System.h:422
GHOST_WindowManager * m_windowManager
Definition: GHOST_System.h:395
GHOST_TSuccess pushEvent(GHOST_IEvent *event)
GHOST_DisplayManager * m_displayManager
Definition: GHOST_System.h:389
bool fireTimers(uint64_t time)
GHOST_TSuccess addWindow(GHOST_IWindow *window)
const std::vector< GHOST_IWindow * > & getWindows() const
GHOST_TSuccess setActiveWindow(GHOST_IWindow *window)
void setWindowInactive(const GHOST_IWindow *window)
GHOST_TWindowState m_post_state
void getClientBounds(GHOST_Rect &bounds) const
bool getValid() const
GHOST_TSuccess setState(GHOST_TWindowState state)
GHOST_TabletData & GetTabletData()
GHOST_TSuccess getCursorGrabBounds(GHOST_Rect &bounds)
void setCursorGrabAccum(int32_t x, int32_t y)
Definition: GHOST_Window.h:440
GHOST_TAxisFlag getCursorGrabAxis() const
Definition: GHOST_Window.h:423
bool getCursorGrabModeIsWarp() const
Definition: GHOST_Window.h:418
void getCursorGrabAccum(int32_t &x, int32_t &y) const
Definition: GHOST_Window.h:434
const char * label
SyclQueue void void size_t num_bytes void
int len
Definition: draw_manager.c:108
#define str(s)
int count
ccl_global float * buffer
const int state
static ulong * next
static int left
static unsigned c
Definition: RandGen.cpp:83
unsigned int uint32_t
Definition: stdint.h:80
__int64 int64_t
Definition: stdint.h:89
signed int int32_t
Definition: stdint.h:77
unsigned char uint8_t
Definition: stdint.h:78
unsigned __int64 uint64_t
Definition: stdint.h:90
void set(GHOST_TButton mask, bool down)
void set(GHOST_TModifierKey mask, bool down)
GHOST_TTabletMode Active
Definition: GHOST_Types.h:98
PointerRNA * ptr
Definition: wm_files.c:3480