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