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