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