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