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