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