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