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