disable warping for tablet events (X11 only), added TODO's for OSX and win32.
[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
36 #include "GHOST_SystemX11.h"
37 #include "GHOST_WindowX11.h"
38 #include "GHOST_WindowManager.h"
39 #include "GHOST_TimerManager.h"
40 #include "GHOST_EventCursor.h"
41 #include "GHOST_EventKey.h"
42 #include "GHOST_EventButton.h"
43 #include "GHOST_EventWheel.h"
44 #include "GHOST_DisplayManagerX11.h"
45 #include "GHOST_EventDragnDrop.h"
46 #ifdef WITH_INPUT_NDOF
47 #include "GHOST_NDOFManagerX11.h"
48 #endif
49
50 #ifdef WITH_XDND
51 #include "GHOST_DropTargetX11.h"
52 #endif
53
54 #include "GHOST_Debug.h"
55
56 #include <X11/Xatom.h>
57 #include <X11/keysym.h>
58 #include <X11/XKBlib.h> /* allow detectable autorepeate */
59
60 #ifdef WITH_XF86KEYSYM
61 #include <X11/XF86keysym.h>
62 #endif
63
64 // For timing
65
66 #include <sys/time.h>
67 #include <unistd.h>
68
69 #include <iostream>
70 #include <vector>
71 #include <stdio.h> // for fprintf only
72 #include <cstdlib> // for exit
73
74 static GHOST_TKey
75 convertXKey(KeySym key);
76
77 //these are for copy and select copy
78 static char *txt_cut_buffer= NULL;
79 static char *txt_select_buffer= NULL;
80
81 using namespace std;
82
83 GHOST_SystemX11::
84 GHOST_SystemX11(
85 ) : 
86         GHOST_System(),
87         m_start_time(0)
88 {
89         m_display = XOpenDisplay(NULL);
90         
91         if (!m_display) {
92                 std::cerr << "Unable to open a display" << std::endl;
93                 abort(); //was return before, but this would just mean it will crash later
94         }
95
96         /* Open a connection to the X input manager */
97 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
98         m_xim = XOpenIM(m_display, NULL, (char *)GHOST_X11_RES_NAME, (char *)GHOST_X11_RES_CLASS);
99 #endif
100
101         m_delete_window_atom 
102           = XInternAtom(m_display, "WM_DELETE_WINDOW", True);
103
104         m_wm_protocols= XInternAtom(m_display, "WM_PROTOCOLS", False);
105         m_wm_take_focus= XInternAtom(m_display, "WM_TAKE_FOCUS", False);
106         m_wm_state= XInternAtom(m_display, "WM_STATE", False);
107         m_wm_change_state= XInternAtom(m_display, "WM_CHANGE_STATE", False);
108         m_net_state= XInternAtom(m_display, "_NET_WM_STATE", False);
109         m_net_max_horz= XInternAtom(m_display,
110                                         "_NET_WM_STATE_MAXIMIZED_HORZ", False);
111         m_net_max_vert= XInternAtom(m_display,
112                                         "_NET_WM_STATE_MAXIMIZED_VERT", False);
113         m_net_fullscreen= XInternAtom(m_display,
114                                         "_NET_WM_STATE_FULLSCREEN", False);
115         m_motif= XInternAtom(m_display, "_MOTIF_WM_HINTS", False);
116         m_targets= XInternAtom(m_display, "TARGETS", False);
117         m_string= XInternAtom(m_display, "STRING", False);
118         m_compound_text= XInternAtom(m_display, "COMPOUND_TEXT", False);
119         m_text= XInternAtom(m_display, "TEXT", False);
120         m_clipboard= XInternAtom(m_display, "CLIPBOARD", False);
121         m_primary= XInternAtom(m_display, "PRIMARY", False);
122         m_xclip_out= XInternAtom(m_display, "XCLIP_OUT", False);
123         m_incr= XInternAtom(m_display, "INCR", False);
124         m_utf8_string= XInternAtom(m_display, "UTF8_STRING", False);
125         m_last_warp = 0;
126
127
128         // compute the initial time
129         timeval tv;
130         if (gettimeofday(&tv,NULL) == -1) {
131                 GHOST_ASSERT(false,"Could not instantiate timer!");
132         }
133         
134         // Taking care not to overflow the tv.tv_sec*1000
135         m_start_time = GHOST_TUns64(tv.tv_sec)*1000 + tv.tv_usec/1000;
136         
137         
138         /* use detectable autorepeate, mac and windows also do this */
139         int use_xkb;
140         int xkb_opcode, xkb_event, xkb_error;
141         int xkb_major = XkbMajorVersion, xkb_minor = XkbMinorVersion;
142         
143         use_xkb = XkbQueryExtension(m_display, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor);
144         if (use_xkb) {
145                 XkbSetDetectableAutoRepeat(m_display, true, NULL);
146         }
147         
148 }
149
150 GHOST_SystemX11::
151 ~GHOST_SystemX11()
152 {
153 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
154         XCloseIM(m_xim);
155 #endif
156
157         XCloseDisplay(m_display);
158 }
159
160
161         GHOST_TSuccess 
162 GHOST_SystemX11::
163 init(
164 ){
165         GHOST_TSuccess success = GHOST_System::init();
166
167         if (success) {
168 #ifdef WITH_INPUT_NDOF
169                 m_ndofManager = new GHOST_NDOFManagerX11(*this);
170 #endif
171                 m_displayManager = new GHOST_DisplayManagerX11(this);
172
173                 if (m_displayManager) {
174                         return GHOST_kSuccess;
175                 }
176         }
177
178         return GHOST_kFailure;
179 }
180
181         GHOST_TUns64
182 GHOST_SystemX11::
183 getMilliSeconds(
184 ) const {
185         timeval tv;
186         if (gettimeofday(&tv,NULL) == -1) {
187                 GHOST_ASSERT(false,"Could not compute time!");
188         }
189
190         // Taking care not to overflow the tv.tv_sec*1000
191         return  GHOST_TUns64(tv.tv_sec)*1000 + tv.tv_usec/1000 - m_start_time;
192 }
193         
194         GHOST_TUns8 
195 GHOST_SystemX11::
196 getNumDisplays(
197 ) const {
198         return GHOST_TUns8(1);
199 }
200
201         /**
202          * Returns the dimensions of the main display on this system.
203          * @return The dimension of the main display.
204          */
205         void 
206 GHOST_SystemX11::
207 getMainDisplayDimensions(
208         GHOST_TUns32& width,
209         GHOST_TUns32& height
210 ) const {       
211         if (m_display) {
212                 width  = DisplayWidth(m_display, DefaultScreen(m_display));
213                 height = DisplayHeight(m_display, DefaultScreen(m_display));
214         }
215 }
216
217         /**
218          * Create a new window.
219          * The new window is added to the list of windows managed.
220          * Never explicitly delete the window, use disposeWindow() instead.
221          * @param       title   The name of the window (displayed in the title bar of the window if the OS supports it).
222          * @param       left    The coordinate of the left edge of the window.
223          * @param       top             The coordinate of the top edge of the window.
224          * @param       width   The width the window.
225          * @param       height  The height the window.
226          * @param       state   The state of the window when opened.
227          * @param       type    The type of drawing context installed in this window.
228          * @param       stereoVisual    Stereo visual for quad buffered stereo.
229          * @param       numOfAASamples  Number of samples used for AA (zero if no AA)
230          * @param       parentWindow    Parent (embedder) window
231          * @return      The new window (or 0 if creation failed).
232          */
233         GHOST_IWindow* 
234 GHOST_SystemX11::
235 createWindow(
236         const STR_String& title,
237         GHOST_TInt32 left,
238         GHOST_TInt32 top,
239         GHOST_TUns32 width,
240         GHOST_TUns32 height,
241         GHOST_TWindowState state,
242         GHOST_TDrawingContextType type,
243         bool stereoVisual,
244         const GHOST_TUns16 numOfAASamples,
245         const GHOST_TEmbedderWindowID parentWindow
246 ){
247         GHOST_WindowX11 * window = 0;
248         
249         if (!m_display) return 0;
250         
251
252         
253
254         window = new GHOST_WindowX11 (
255                 this,m_display,title, left, top, width, height, state, parentWindow, type, stereoVisual
256         );
257
258         if (window) {
259                 // Both are now handle in GHOST_WindowX11.cpp
260                 // Focus and Delete atoms.
261
262                 if (window->getValid()) {
263                         // Store the pointer to the window 
264                         m_windowManager->addWindow(window);
265                         m_windowManager->setActiveWindow(window);
266                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
267                 }
268                 else {
269                         delete window;
270                         window = 0;
271                 }
272         }
273         return window;
274 }
275
276         GHOST_WindowX11 * 
277 GHOST_SystemX11::
278 findGhostWindow(
279         Window xwind
280 ) const {
281         
282         if (xwind == 0) return NULL;
283
284         // It is not entirely safe to do this as the backptr may point
285         // to a window that has recently been removed. 
286         // We should always check the window manager's list of windows 
287         // and only process events on these windows.
288
289         vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
290
291         vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
292         vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
293         
294         for (; win_it != win_end; ++win_it) {
295                 GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
296                 if (window->getXWindow() == xwind) {
297                         return window;
298                 }
299         }
300         return NULL;
301         
302 }
303
304 static void SleepTillEvent(Display *display, GHOST_TInt64 maxSleep)
305 {
306         int fd = ConnectionNumber(display);
307         fd_set fds;
308         
309         FD_ZERO(&fds);
310         FD_SET(fd, &fds);
311
312         if (maxSleep == -1) {
313                 select(fd + 1, &fds, NULL, NULL, NULL);
314         }
315         else {
316                 timeval tv;
317
318                 tv.tv_sec = maxSleep/1000;
319                 tv.tv_usec = (maxSleep - tv.tv_sec*1000)*1000;
320         
321                 select(fd + 1, &fds, NULL, NULL, &tv);
322         }
323 }
324
325 /* This function borrowed from Qt's X11 support
326  * qclipboard_x11.cpp
327  *  */
328 struct init_timestamp_data
329 {
330     Time timestamp;
331 };
332
333 static Bool init_timestamp_scanner(Display*, XEvent *event, XPointer arg)
334 {
335         init_timestamp_data *data =
336         reinterpret_cast<init_timestamp_data*>(arg);
337     switch(event->type)
338     {
339     case ButtonPress:
340     case ButtonRelease:
341         data->timestamp = event->xbutton.time;
342         break;
343     case MotionNotify:
344         data->timestamp = event->xmotion.time;
345         break;
346     case KeyPress:
347     case KeyRelease:
348         data->timestamp = event->xkey.time;
349         break;
350     case PropertyNotify:
351         data->timestamp = event->xproperty.time;
352         break;
353     case EnterNotify:
354     case LeaveNotify:
355         data->timestamp = event->xcrossing.time;
356         break;
357     case SelectionClear:
358         data->timestamp = event->xselectionclear.time;
359         break;
360     default:
361         break;
362     }
363
364     return false;
365 }
366
367 Time
368 GHOST_SystemX11::
369 lastEventTime(Time default_time) {
370     init_timestamp_data data;
371     data.timestamp = default_time;
372     XEvent ev;
373     XCheckIfEvent(m_display, &ev, &init_timestamp_scanner, (XPointer)&data);
374
375     return data.timestamp;
376 }
377
378         bool 
379 GHOST_SystemX11::
380 processEvents(
381         bool waitForEvent
382 ){
383         // Get all the current events -- translate them into 
384         // ghost events and call base class pushEvent() method.
385         
386         bool anyProcessed = false;
387         
388         do {
389                 GHOST_TimerManager* timerMgr = getTimerManager();
390                 
391                 if (waitForEvent && m_dirty_windows.empty() && !XPending(m_display)) {
392                         GHOST_TUns64 next = timerMgr->nextFireTime();
393                         
394                         if (next==GHOST_kFireTimeNever) {
395                                 SleepTillEvent(m_display, -1);
396                         } else {
397                                 GHOST_TInt64 maxSleep = next - getMilliSeconds();
398
399                                 if(maxSleep >= 0)
400                                         SleepTillEvent(m_display, next - getMilliSeconds());
401                         }
402                 }
403                 
404                 if (timerMgr->fireTimers(getMilliSeconds())) {
405                         anyProcessed = true;
406                 }
407                 
408                 while (XPending(m_display)) {
409                         XEvent xevent;
410                         XNextEvent(m_display, &xevent);
411                         processEvent(&xevent);
412                         anyProcessed = true;
413                 }
414                 
415                 if (generateWindowExposeEvents()) {
416                         anyProcessed = true;
417                 }
418
419 #ifdef WITH_INPUT_NDOF
420                 if (dynamic_cast<GHOST_NDOFManagerX11*>(m_ndofManager)->processEvents()) {
421                         anyProcessed = true;
422                 }
423 #endif
424                 
425         } while (waitForEvent && !anyProcessed);
426         
427         return anyProcessed;
428 }
429
430
431 #ifdef WITH_X11_XINPUT
432 /* set currently using tablet mode (stylus or eraser) depending on device ID */
433 static void setTabletMode(GHOST_WindowX11 * window, XID deviceid)
434 {
435         if(deviceid == window->GetXTablet().StylusID)
436                 window->GetXTablet().CommonData.Active= GHOST_kTabletModeStylus;
437         else if(deviceid == window->GetXTablet().EraserID)
438                 window->GetXTablet().CommonData.Active= GHOST_kTabletModeEraser;
439 }
440 #endif /* WITH_X11_XINPUT */
441
442         void
443 GHOST_SystemX11::processEvent(XEvent *xe)
444 {
445         GHOST_WindowX11 * window = findGhostWindow(xe->xany.window);    
446         GHOST_Event * g_event = NULL;
447
448         if (!window) {
449                 return;
450         }
451         
452         switch (xe->type) {
453                 case Expose:
454                 {
455                         XExposeEvent & xee = xe->xexpose;
456
457                         if (xee.count == 0) {
458                                 // Only generate a single expose event
459                                 // per read of the event queue.
460
461                                 g_event = new
462                                 GHOST_Event(
463                                         getMilliSeconds(),
464                                         GHOST_kEventWindowUpdate,
465                                         window
466                                 );
467                         }
468                         break;
469                 }
470
471                 case MotionNotify:
472                 {
473                         XMotionEvent &xme = xe->xmotion;
474
475 #ifdef WITH_X11_XINPUT
476                         bool is_tablet = window->GetXTablet().CommonData.Active != GHOST_kTabletModeNone;
477 #else
478                         bool is_tablet = false;
479 #endif
480
481                         if(is_tablet == false &&
482                            window->getCursorGrabMode() != GHOST_kGrabDisable &&
483                            window->getCursorGrabMode() != GHOST_kGrabNormal)
484                         {
485                                 GHOST_TInt32 x_new= xme.x_root;
486                                 GHOST_TInt32 y_new= xme.y_root;
487                                 GHOST_TInt32 x_accum, y_accum;
488                                 GHOST_Rect bounds;
489
490                                 /* fallback to window bounds */
491                                 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure)
492                                         window->getClientBounds(bounds);
493
494                                 /* could also clamp to screen bounds
495                                  * wrap with a window outside the view will fail atm  */
496                                 bounds.wrapPoint(x_new, y_new, 8); /* offset of one incase blender is at screen bounds */
497                                 window->getCursorGrabAccum(x_accum, y_accum);
498
499                                 if(x_new != xme.x_root || y_new != xme.y_root) {
500                                         if (xme.time > m_last_warp) {
501                                                 /* when wrapping we don't need to add an event because the
502                                                  * setCursorPosition call will cause a new event after */
503                                                 setCursorPosition(x_new, y_new); /* wrap */
504                                                 window->setCursorGrabAccum(x_accum + (xme.x_root - x_new), y_accum + (xme.y_root - y_new));
505                                                 m_last_warp = lastEventTime(xme.time);
506                                         } else {
507                                                 setCursorPosition(x_new, y_new); /* wrap but don't accumulate */
508                                         }
509                                 }
510                                 else {
511                                         g_event = new
512                                         GHOST_EventCursor(
513                                                 getMilliSeconds(),
514                                                 GHOST_kEventCursorMove,
515                                                 window,
516                                                 xme.x_root + x_accum,
517                                                 xme.y_root + y_accum
518                                         );
519                                 }
520                         }
521                         else {
522                                 g_event = new
523                                 GHOST_EventCursor(
524                                         getMilliSeconds(),
525                                         GHOST_kEventCursorMove,
526                                         window,
527                                         xme.x_root,
528                                         xme.y_root
529                                 );
530                         }
531                         break;
532                 }
533
534                 case KeyPress:
535                 case KeyRelease:
536                 {
537                         XKeyEvent *xke = &(xe->xkey);
538                         KeySym key_sym = XLookupKeysym(xke,0);
539                         char ascii;
540                         char utf8_buf[6]; /* 6 is enough for a utf8 char */
541                         
542                         GHOST_TKey gkey = convertXKey(key_sym);
543                         GHOST_TEventType type = (xke->type == KeyPress) ? 
544                                 GHOST_kEventKeyDown : GHOST_kEventKeyUp;
545                         
546                         if (!XLookupString(xke, &ascii, 1, NULL, NULL)) {
547                                 ascii = '\0';
548                         }
549                         
550 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
551                         /* getting unicode on key-up events gives XLookupNone status */
552                         if (xke->type == KeyPress) {
553                                 Status status;
554                                 int len;
555
556                                 /* use utf8 because its not locale depentant, from xorg docs */
557                                 if (!(len= Xutf8LookupString(window->getX11_XIC(), xke, utf8_buf, sizeof(utf8_buf), &key_sym, &status))) {
558                                         utf8_buf[0]= '\0';
559                                 }
560
561                                 if ((status == XLookupChars || status == XLookupBoth)) {
562                                         if ((unsigned char)utf8_buf[0] >= 32) { /* not an ascii control character */
563                                                 /* do nothing for now, this is valid utf8 */
564                                         }
565                                         else {
566                                                 utf8_buf[0]= '\0';
567                                         }
568                                 }
569                                 else if (status == XLookupKeySym) {
570                                         /* this key doesn't have a text representation, it is a command
571                                            key of some sort */;
572                                 }
573                                 else {
574                                         printf("Bad keycode lookup. Keysym 0x%x Status: %s\n",
575                                                           (unsigned int) key_sym,
576                                                           (status == XBufferOverflow ? "BufferOverflow" :
577                                                            status == XLookupNone ? "XLookupNone" :
578                                                            status == XLookupKeySym ? "XLookupKeySym" :
579                                                            "Unknown status"));
580
581                                         printf("'%.*s' %p %p\n", len, utf8_buf, window->getX11_XIC(), m_xim);
582                                 }
583                         }
584                         else {
585                                 utf8_buf[0]= '\0';
586                         }
587 #else
588                         utf8_buf[0]= '\0';
589 #endif
590
591                         g_event = new
592                         GHOST_EventKey(
593                                 getMilliSeconds(),
594                                 type,
595                                 window,
596                                 gkey,
597                                 ascii,
598                             utf8_buf
599                         );
600                         
601                 break;
602                 }
603
604                 case ButtonPress:
605                 case ButtonRelease:
606                 {
607                         XButtonEvent & xbe = xe->xbutton;
608                         GHOST_TButtonMask gbmask = GHOST_kButtonMaskLeft;
609                         GHOST_TEventType type = (xbe.type == ButtonPress) ? 
610                                 GHOST_kEventButtonDown : GHOST_kEventButtonUp;
611
612                         /* process wheel mouse events and break, only pass on press events */
613                         if(xbe.button == Button4) {
614                                 if(xbe.type == ButtonPress)
615                                         g_event = new GHOST_EventWheel(getMilliSeconds(), window, 1);
616                                 break;
617                         }
618                         else if(xbe.button == Button5) {
619                                 if(xbe.type == ButtonPress)
620                                         g_event = new GHOST_EventWheel(getMilliSeconds(), window, -1);
621                                 break;
622                         }
623                         
624                         /* process rest of normal mouse buttons */
625                         if(xbe.button == Button1)
626                                 gbmask = GHOST_kButtonMaskLeft;
627                         else if(xbe.button == Button2)
628                                 gbmask = GHOST_kButtonMaskMiddle;
629                         else if(xbe.button == Button3)
630                                 gbmask = GHOST_kButtonMaskRight;
631                         /* It seems events 6 and 7 are for horizontal scrolling.
632                         * you can re-order button mapping like this... (swaps 6,7 with 8,9)
633                         *   xmodmap -e "pointer = 1 2 3 4 5 8 9 6 7" 
634                         */
635                         else if(xbe.button == 8)
636                                 gbmask = GHOST_kButtonMaskButton4;
637                         else if(xbe.button == 9)
638                                 gbmask = GHOST_kButtonMaskButton5;
639                         else
640                                 break;
641
642                         g_event = new
643                         GHOST_EventButton(
644                                 getMilliSeconds(),
645                                 type,
646                                 window,
647                                 gbmask
648                         );
649                         break;
650                 }
651                         
652                         // change of size, border, layer etc.
653                 case ConfigureNotify:
654                 {
655                         /* XConfigureEvent & xce = xe->xconfigure; */
656
657                         g_event = new 
658                         GHOST_Event(
659                                 getMilliSeconds(),
660                                 GHOST_kEventWindowSize,
661                                 window
662                         );                      
663                         break;
664                 }
665
666                 case FocusIn:
667                 case FocusOut:
668                 {
669                         XFocusChangeEvent &xfe = xe->xfocus;
670
671                         // TODO: make sure this is the correct place for activate/deactivate
672                         // printf("X: focus %s for window %d\n", xfe.type == FocusIn ? "in" : "out", (int) xfe.window);
673                 
674                         // May have to look at the type of event and filter some
675                         // out.
676                                                                         
677                         GHOST_TEventType gtype = (xfe.type == FocusIn) ? 
678                                 GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate;
679
680                         g_event = new 
681                         GHOST_Event(    
682                                 getMilliSeconds(),
683                                 gtype,
684                                 window
685                         );
686                         break;
687
688                 }
689                 case ClientMessage:
690                 {
691                         XClientMessageEvent & xcme = xe->xclient;
692
693                         if (((Atom)xcme.data.l[0]) == m_delete_window_atom) {
694                                 g_event = new 
695                                 GHOST_Event(    
696                                         getMilliSeconds(),
697                                         GHOST_kEventWindowClose,
698                                         window
699                                 );
700                         }
701                         else if (((Atom)xcme.data.l[0]) == m_wm_take_focus) {
702                                 XWindowAttributes attr;
703                                 Window fwin;
704                                 int revert_to;
705
706                                 /* as ICCCM say, we need reply this event
707                                  * with a SetInputFocus, the data[1] have
708                                  * the valid timestamp (send by the wm).
709                                  *
710                                  * Some WM send this event before the
711                                  * window is really mapped (for example
712                                  * change from virtual desktop), so we need
713                                  * to be sure that our windows is mapped
714                                  * or this call fail and close blender.
715                                  */
716                                 if (XGetWindowAttributes(m_display, xcme.window, &attr) == True) {
717                                         if (XGetInputFocus(m_display, &fwin, &revert_to) == True) {
718                                                 if (attr.map_state == IsViewable) {
719                                                         if (fwin != xcme.window)
720                                                                 XSetInputFocus(m_display, xcme.window, RevertToParent, xcme.data.l[1]);
721                                                 }
722                                         }
723                                 }
724                         } else {
725 #ifdef WITH_XDND
726                                 /* try to handle drag event (if there's no such events, GHOST_HandleClientMessage will return zero) */
727                                 if (window->getDropTarget()->GHOST_HandleClientMessage(xe) == false) {
728                                         /* Unknown client message, ignore */
729                                 }
730 #else
731                                 /* Unknown client message, ignore */
732 #endif
733                         }
734
735                         break;
736                 }
737                 
738                 case DestroyNotify:
739                         ::exit(-1);     
740                 // We're not interested in the following things.(yet...)
741                 case NoExpose : 
742                 case GraphicsExpose :
743                         break;
744                 
745                 case EnterNotify:
746                 case LeaveNotify:
747                 {
748                         /* XCrossingEvents pointer leave enter window.
749                            also do cursor move here, MotionNotify only
750                            happens when motion starts & ends inside window.
751                            we only do moves when the crossing mode is 'normal'
752                            (really crossing between windows) since some windowmanagers
753                            also send grab/ungrab crossings for mousewheel events.
754                         */
755                         XCrossingEvent &xce = xe->xcrossing;
756                         if( xce.mode == NotifyNormal ) {
757                                 g_event = new 
758                                 GHOST_EventCursor(
759                                         getMilliSeconds(),
760                                         GHOST_kEventCursorMove,
761                                         window,
762                                         xce.x_root,
763                                         xce.y_root
764                                 );
765                         }
766
767                         // printf("X: %s window %d\n", xce.type == EnterNotify ? "entering" : "leaving", (int) xce.window);
768
769                         if (xce.type == EnterNotify)
770                                 m_windowManager->setActiveWindow(window);
771                         else
772                                 m_windowManager->setWindowInactive(window);
773
774                         break;
775                 }
776                 case MapNotify:
777                         /*
778                          * From ICCCM:
779                          * [ Clients can select for StructureNotify on their
780                          *   top-level windows to track transition between
781                          *   Normal and Iconic states. Receipt of a MapNotify
782                          *   event will indicate a transition to the Normal
783                          *   state, and receipt of an UnmapNotify event will
784                          *   indicate a transition to the Iconic state. ]
785                          */
786                         if (window->m_post_init == True) {
787                                 /*
788                                  * Now we are sure that the window is
789                                  * mapped, so only need change the state.
790                                  */
791                                 window->setState (window->m_post_state);
792                                 window->m_post_init = False;
793                         }
794                         break;
795                 case UnmapNotify:
796                         break;
797                 case MappingNotify:
798                 case ReparentNotify:
799                         break;
800                 case SelectionRequest:
801                 {
802                         XEvent nxe;
803                         Atom target, utf8_string, string, compound_text, c_string;
804                         XSelectionRequestEvent *xse = &xe->xselectionrequest;
805                         
806                         target = XInternAtom(m_display, "TARGETS", False);
807                         utf8_string = XInternAtom(m_display, "UTF8_STRING", False);
808                         string = XInternAtom(m_display, "STRING", False);
809                         compound_text = XInternAtom(m_display, "COMPOUND_TEXT", False);
810                         c_string = XInternAtom(m_display, "C_STRING", False);
811                         
812                         /* support obsolete clients */
813                         if (xse->property == None) {
814                                 xse->property = xse->target;
815                         }
816                         
817                         nxe.xselection.type = SelectionNotify;
818                         nxe.xselection.requestor = xse->requestor;
819                         nxe.xselection.property = xse->property;
820                         nxe.xselection.display = xse->display;
821                         nxe.xselection.selection = xse->selection;
822                         nxe.xselection.target = xse->target;
823                         nxe.xselection.time = xse->time;
824                         
825                         /*Check to see if the requestor is asking for String*/
826                         if(xse->target == utf8_string || xse->target == string || xse->target == compound_text || xse->target == c_string) {
827                                 if (xse->selection == XInternAtom(m_display, "PRIMARY", False)) {
828                                         XChangeProperty(m_display, xse->requestor, xse->property, xse->target, 8, PropModeReplace,
829                                                         (unsigned char*)txt_select_buffer, strlen(txt_select_buffer));
830                                 } else if (xse->selection == XInternAtom(m_display, "CLIPBOARD", False)) {
831                                         XChangeProperty(m_display, xse->requestor, xse->property, xse->target, 8, PropModeReplace,
832                                                         (unsigned char*)txt_cut_buffer, strlen(txt_cut_buffer));
833                                 }
834                         } else if (xse->target == target) {
835                                 Atom alist[5];
836                                 alist[0] = target;
837                                 alist[1] = utf8_string;
838                                 alist[2] = string;
839                                 alist[3] = compound_text;
840                                 alist[4] = c_string;
841                                 XChangeProperty(m_display, xse->requestor, xse->property, xse->target, 32, PropModeReplace,
842                                                 (unsigned char*)alist, 5);
843                                 XFlush(m_display);
844                         } else  {
845                                 //Change property to None because we do not support anything but STRING
846                                 nxe.xselection.property = None;
847                         }
848                         
849                         //Send the event to the client 0 0 == False, SelectionNotify
850                         XSendEvent(m_display, xse->requestor, 0, 0, &nxe);
851                         XFlush(m_display);
852                         break;
853                 }
854                 
855                 default: {
856 #ifdef WITH_X11_XINPUT
857                         if(xe->type == window->GetXTablet().MotionEvent) 
858                         {
859                                 XDeviceMotionEvent* data = (XDeviceMotionEvent*)xe;
860
861                                 /* stroke might begin without leading ProxyIn event,
862                                  * this happens when window is opened when stylus is already hovering
863                                  * around tablet surface */
864                                 setTabletMode(window, data->deviceid);
865
866                                 window->GetXTablet().CommonData.Pressure= 
867                                         data->axis_data[2]/((float)window->GetXTablet().PressureLevels);
868                         
869                         /* the (short) cast and the &0xffff is bizarre and unexplained anywhere,
870                          * but I got garbage data without it. Found it in the xidump.c source --matt */
871                                 window->GetXTablet().CommonData.Xtilt= 
872                                         (short)(data->axis_data[3]&0xffff)/((float)window->GetXTablet().XtiltLevels);
873                                 window->GetXTablet().CommonData.Ytilt= 
874                                         (short)(data->axis_data[4]&0xffff)/((float)window->GetXTablet().YtiltLevels);
875                         }
876                         else if(xe->type == window->GetXTablet().ProxInEvent) 
877                         {
878                                 XProximityNotifyEvent* data = (XProximityNotifyEvent*)xe;
879
880                                 setTabletMode(window, data->deviceid);
881                         }
882                         else if(xe->type == window->GetXTablet().ProxOutEvent)
883                                 window->GetXTablet().CommonData.Active= GHOST_kTabletModeNone;
884 #endif // WITH_X11_XINPUT
885                         break;
886                 }
887         }
888
889         if (g_event) {
890                 pushEvent(g_event);
891         }
892 }
893
894         GHOST_TSuccess 
895 GHOST_SystemX11::
896 getModifierKeys(
897         GHOST_ModifierKeys& keys
898 ) const {
899
900         // analyse the masks retuned from XQueryPointer.
901
902         memset((void *)m_keyboard_vector,0,sizeof(m_keyboard_vector));
903
904         XQueryKeymap(m_display,(char *)m_keyboard_vector);
905
906         // now translate key symobols into keycodes and
907         // test with vector.
908
909         const static KeyCode shift_l = XKeysymToKeycode(m_display,XK_Shift_L);
910         const static KeyCode shift_r = XKeysymToKeycode(m_display,XK_Shift_R);
911         const static KeyCode control_l = XKeysymToKeycode(m_display,XK_Control_L);
912         const static KeyCode control_r = XKeysymToKeycode(m_display,XK_Control_R);
913         const static KeyCode alt_l = XKeysymToKeycode(m_display,XK_Alt_L);
914         const static KeyCode alt_r = XKeysymToKeycode(m_display,XK_Alt_R);
915         const static KeyCode super_l = XKeysymToKeycode(m_display,XK_Super_L);
916         const static KeyCode super_r = XKeysymToKeycode(m_display,XK_Super_R);
917
918         // shift
919         keys.set(GHOST_kModifierKeyLeftShift, ((m_keyboard_vector[shift_l >> 3] >> (shift_l & 7)) & 1) != 0);
920         keys.set(GHOST_kModifierKeyRightShift, ((m_keyboard_vector[shift_r >> 3] >> (shift_r & 7)) & 1) != 0);
921         // control
922         keys.set(GHOST_kModifierKeyLeftControl, ((m_keyboard_vector[control_l >> 3] >> (control_l & 7)) & 1) != 0);
923         keys.set(GHOST_kModifierKeyRightControl, ((m_keyboard_vector[control_r >> 3] >> (control_r & 7)) & 1) != 0);
924         // alt
925         keys.set(GHOST_kModifierKeyLeftAlt, ((m_keyboard_vector[alt_l >> 3] >> (alt_l & 7)) & 1) != 0);
926         keys.set(GHOST_kModifierKeyRightAlt, ((m_keyboard_vector[alt_r >> 3] >> (alt_r & 7)) & 1) != 0);
927         // super (windows) - only one GHOST-kModifierKeyOS, so mapping to either
928         keys.set(GHOST_kModifierKeyOS, ( ((m_keyboard_vector[super_l >> 3] >> (super_l & 7)) & 1) ||
929                                          ((m_keyboard_vector[super_r >> 3] >> (super_r & 7)) & 1) ) != 0);
930
931         return GHOST_kSuccess;
932 }
933
934         GHOST_TSuccess 
935 GHOST_SystemX11::
936 getButtons(
937         GHOST_Buttons& buttons
938 ) const {
939
940         Window root_return, child_return;
941         int rx,ry,wx,wy;
942         unsigned int mask_return;
943
944         if (XQueryPointer(m_display,
945                           RootWindow(m_display,DefaultScreen(m_display)),
946                           &root_return,
947                           &child_return,
948                           &rx,&ry,
949                           &wx,&wy,
950                           &mask_return) == True)
951         {
952                 buttons.set(GHOST_kButtonMaskLeft,   (mask_return & Button1Mask) != 0);
953                 buttons.set(GHOST_kButtonMaskMiddle, (mask_return & Button2Mask) != 0);
954                 buttons.set(GHOST_kButtonMaskRight,  (mask_return & Button3Mask) != 0);
955         }
956         else {
957                 return GHOST_kFailure;
958         }       
959
960         return GHOST_kSuccess;
961 }
962
963
964         GHOST_TSuccess 
965 GHOST_SystemX11::
966 getCursorPosition(
967         GHOST_TInt32& x,
968         GHOST_TInt32& y
969 ) const {
970
971         Window root_return, child_return;
972         int rx,ry,wx,wy;
973         unsigned int mask_return;
974
975         if (XQueryPointer(
976                 m_display,
977                 RootWindow(m_display,DefaultScreen(m_display)),
978                 &root_return,
979                 &child_return,
980                 &rx,&ry,
981                 &wx,&wy,
982                 &mask_return
983         ) == False) {
984                 return GHOST_kFailure;
985         } else {
986                 x = rx;
987                 y = ry;
988         }       
989         return GHOST_kSuccess;
990 }
991
992
993         GHOST_TSuccess 
994 GHOST_SystemX11::
995 setCursorPosition(
996         GHOST_TInt32 x,
997         GHOST_TInt32 y
998 ) {
999
1000         // This is a brute force move in screen coordinates
1001         // XWarpPointer does relative moves so first determine the
1002         // current pointer position.
1003
1004         int cx,cy;
1005         if (getCursorPosition(cx,cy) == GHOST_kFailure) {
1006                 return GHOST_kFailure;
1007         }
1008
1009         int relx = x-cx;
1010         int rely = y-cy;
1011
1012         XWarpPointer(m_display,None,None,0,0,0,0,relx,rely);
1013         XSync(m_display, 0); /* Sync to process all requests */
1014         
1015         return GHOST_kSuccess;
1016 }
1017
1018
1019         void
1020 GHOST_SystemX11::
1021 addDirtyWindow(
1022         GHOST_WindowX11 * bad_wind
1023 ){
1024
1025         GHOST_ASSERT((bad_wind != NULL), "addDirtyWindow() NULL ptr trapped (window)");
1026         
1027         m_dirty_windows.push_back(bad_wind);
1028 }
1029
1030
1031         bool
1032 GHOST_SystemX11::
1033 generateWindowExposeEvents(
1034 ){
1035
1036         vector<GHOST_WindowX11 *>::iterator w_start = m_dirty_windows.begin();
1037         vector<GHOST_WindowX11 *>::const_iterator w_end = m_dirty_windows.end();
1038         bool anyProcessed = false;
1039         
1040         for (;w_start != w_end; ++w_start) {
1041                 GHOST_Event * g_event = new 
1042                         GHOST_Event(
1043                                 getMilliSeconds(),
1044                                 GHOST_kEventWindowUpdate,
1045                                 *w_start
1046                         );                      
1047
1048                 (*w_start)->validate(); 
1049                 
1050                 if (g_event) {
1051                         pushEvent(g_event);
1052                         anyProcessed = true;
1053                 }
1054         }
1055
1056         m_dirty_windows.clear();
1057         return anyProcessed;
1058 }
1059
1060 #define GXMAP(k,x,y) case x: k = y; break; 
1061
1062 static GHOST_TKey
1063 convertXKey(KeySym key)
1064 {
1065         GHOST_TKey type;
1066
1067         if ((key >= XK_A) && (key <= XK_Z)) {
1068                 type = GHOST_TKey( key - XK_A + int(GHOST_kKeyA));
1069         } else if ((key >= XK_a) && (key <= XK_z)) {
1070                 type = GHOST_TKey(key - XK_a + int(GHOST_kKeyA));
1071         } else if ((key >= XK_0) && (key <= XK_9)) {
1072                 type = GHOST_TKey(key - XK_0 + int(GHOST_kKey0));
1073         } else if ((key >= XK_F1) && (key <= XK_F24)) {
1074                 type = GHOST_TKey(key - XK_F1 + int(GHOST_kKeyF1));
1075 #if defined(__sun) || defined(__sun__) 
1076                 /* This is a bit of a hack, but it looks like sun
1077                    Used F11 and friends for its special keys Stop,again etc..
1078                    So this little patch enables F11 and F12 to work as expected
1079                    following link has documentation on it: 
1080                    http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4734408
1081                    also from /usr/include/X11/Sunkeysym.h 
1082 #define SunXK_F36               0x1005FF10      // Labeled F11
1083 #define SunXK_F37               0x1005FF11      // Labeled F12 
1084
1085                                 mein@cs.umn.edu
1086                  */
1087                 
1088         } else if (key == 268828432) {
1089                 type = GHOST_kKeyF11;
1090         } else if (key == 268828433) {
1091                 type = GHOST_kKeyF12;
1092 #endif
1093         } else {
1094                 switch(key) {
1095                         GXMAP(type,XK_BackSpace,        GHOST_kKeyBackSpace);
1096                         GXMAP(type,XK_Tab,              GHOST_kKeyTab);
1097                         GXMAP(type,XK_Return,           GHOST_kKeyEnter);
1098                         GXMAP(type,XK_Escape,           GHOST_kKeyEsc);
1099                         GXMAP(type,XK_space,            GHOST_kKeySpace);
1100                         
1101                         GXMAP(type,XK_Linefeed,         GHOST_kKeyLinefeed);
1102                         GXMAP(type,XK_semicolon,        GHOST_kKeySemicolon);
1103                         GXMAP(type,XK_period,           GHOST_kKeyPeriod);
1104                         GXMAP(type,XK_comma,            GHOST_kKeyComma);
1105                         GXMAP(type,XK_quoteright,       GHOST_kKeyQuote);
1106                         GXMAP(type,XK_quoteleft,        GHOST_kKeyAccentGrave);
1107                         GXMAP(type,XK_minus,            GHOST_kKeyMinus);
1108                         GXMAP(type,XK_slash,            GHOST_kKeySlash);
1109                         GXMAP(type,XK_backslash,        GHOST_kKeyBackslash);
1110                         GXMAP(type,XK_equal,            GHOST_kKeyEqual);
1111                         GXMAP(type,XK_bracketleft,      GHOST_kKeyLeftBracket);
1112                         GXMAP(type,XK_bracketright,     GHOST_kKeyRightBracket);
1113                         GXMAP(type,XK_Pause,            GHOST_kKeyPause);
1114                         
1115                         GXMAP(type,XK_Shift_L,          GHOST_kKeyLeftShift);
1116                         GXMAP(type,XK_Shift_R,          GHOST_kKeyRightShift);
1117                         GXMAP(type,XK_Control_L,        GHOST_kKeyLeftControl);
1118                         GXMAP(type,XK_Control_R,        GHOST_kKeyRightControl);
1119                         GXMAP(type,XK_Alt_L,            GHOST_kKeyLeftAlt);
1120                         GXMAP(type,XK_Alt_R,            GHOST_kKeyRightAlt);
1121                         GXMAP(type,XK_Super_L,          GHOST_kKeyOS);
1122                         GXMAP(type,XK_Super_R,          GHOST_kKeyOS);
1123
1124                         GXMAP(type,XK_Insert,           GHOST_kKeyInsert);
1125                         GXMAP(type,XK_Delete,           GHOST_kKeyDelete);
1126                         GXMAP(type,XK_Home,                     GHOST_kKeyHome);
1127                         GXMAP(type,XK_End,                      GHOST_kKeyEnd);
1128                         GXMAP(type,XK_Page_Up,          GHOST_kKeyUpPage);
1129                         GXMAP(type,XK_Page_Down,        GHOST_kKeyDownPage);
1130
1131                         GXMAP(type,XK_Left,                     GHOST_kKeyLeftArrow);
1132                         GXMAP(type,XK_Right,            GHOST_kKeyRightArrow);
1133                         GXMAP(type,XK_Up,                       GHOST_kKeyUpArrow);
1134                         GXMAP(type,XK_Down,                     GHOST_kKeyDownArrow);
1135
1136                         GXMAP(type,XK_Caps_Lock,        GHOST_kKeyCapsLock);
1137                         GXMAP(type,XK_Scroll_Lock,      GHOST_kKeyScrollLock);
1138                         GXMAP(type,XK_Num_Lock,         GHOST_kKeyNumLock);
1139                         
1140                                 /* keypad events */
1141                                 
1142                         GXMAP(type,XK_KP_0,                     GHOST_kKeyNumpad0);
1143                         GXMAP(type,XK_KP_1,                     GHOST_kKeyNumpad1);
1144                         GXMAP(type,XK_KP_2,                     GHOST_kKeyNumpad2);
1145                         GXMAP(type,XK_KP_3,                     GHOST_kKeyNumpad3);
1146                         GXMAP(type,XK_KP_4,                     GHOST_kKeyNumpad4);
1147                         GXMAP(type,XK_KP_5,                     GHOST_kKeyNumpad5);
1148                         GXMAP(type,XK_KP_6,                     GHOST_kKeyNumpad6);
1149                         GXMAP(type,XK_KP_7,                     GHOST_kKeyNumpad7);
1150                         GXMAP(type,XK_KP_8,                     GHOST_kKeyNumpad8);
1151                         GXMAP(type,XK_KP_9,                     GHOST_kKeyNumpad9);
1152                         GXMAP(type,XK_KP_Decimal,       GHOST_kKeyNumpadPeriod);
1153
1154                         GXMAP(type,XK_KP_Insert,        GHOST_kKeyNumpad0);
1155                         GXMAP(type,XK_KP_End,           GHOST_kKeyNumpad1);
1156                         GXMAP(type,XK_KP_Down,          GHOST_kKeyNumpad2);
1157                         GXMAP(type,XK_KP_Page_Down,     GHOST_kKeyNumpad3);
1158                         GXMAP(type,XK_KP_Left,          GHOST_kKeyNumpad4);
1159                         GXMAP(type,XK_KP_Begin,         GHOST_kKeyNumpad5);
1160                         GXMAP(type,XK_KP_Right,         GHOST_kKeyNumpad6);
1161                         GXMAP(type,XK_KP_Home,          GHOST_kKeyNumpad7);
1162                         GXMAP(type,XK_KP_Up,            GHOST_kKeyNumpad8);
1163                         GXMAP(type,XK_KP_Page_Up,       GHOST_kKeyNumpad9);
1164                         GXMAP(type,XK_KP_Delete,        GHOST_kKeyNumpadPeriod);
1165
1166                         GXMAP(type,XK_KP_Enter,         GHOST_kKeyNumpadEnter);
1167                         GXMAP(type,XK_KP_Add,           GHOST_kKeyNumpadPlus);
1168                         GXMAP(type,XK_KP_Subtract,      GHOST_kKeyNumpadMinus);
1169                         GXMAP(type,XK_KP_Multiply,      GHOST_kKeyNumpadAsterisk);
1170                         GXMAP(type,XK_KP_Divide,        GHOST_kKeyNumpadSlash);
1171
1172                         /* Media keys in some keyboards and laptops with XFree86/Xorg */
1173 #ifdef WITH_XF86KEYSYM
1174                         GXMAP(type,XF86XK_AudioPlay,    GHOST_kKeyMediaPlay);
1175                         GXMAP(type,XF86XK_AudioStop,    GHOST_kKeyMediaStop);
1176                         GXMAP(type,XF86XK_AudioPrev,    GHOST_kKeyMediaFirst);
1177                         GXMAP(type,XF86XK_AudioRewind,  GHOST_kKeyMediaFirst);
1178                         GXMAP(type,XF86XK_AudioNext,    GHOST_kKeyMediaLast);
1179 #ifdef XF86XK_AudioForward /* Debian lenny's XF86keysym.h has no XF86XK_AudioForward define */
1180                         GXMAP(type,XF86XK_AudioForward, GHOST_kKeyMediaLast);
1181 #endif
1182 #endif
1183
1184                                 /* some extra sun cruft (NICE KEYBOARD!) */
1185 #ifdef __sun__
1186                         GXMAP(type,0xffde,                      GHOST_kKeyNumpad1);
1187                         GXMAP(type,0xffe0,                      GHOST_kKeyNumpad3);
1188                         GXMAP(type,0xffdc,                      GHOST_kKeyNumpad5);
1189                         GXMAP(type,0xffd8,                      GHOST_kKeyNumpad7);
1190                         GXMAP(type,0xffda,                      GHOST_kKeyNumpad9);
1191
1192                         GXMAP(type,0xffd6,                      GHOST_kKeyNumpadSlash);
1193                         GXMAP(type,0xffd7,                      GHOST_kKeyNumpadAsterisk);
1194 #endif
1195
1196                         default :
1197                                 type = GHOST_kKeyUnknown;
1198                                 break;
1199                 }
1200         }
1201
1202         return type;
1203 }
1204
1205 #undef GXMAP
1206
1207 /* from xclip.c xcout() v0.11 */
1208
1209 #define XCLIB_XCOUT_NONE                0 /* no context */
1210 #define XCLIB_XCOUT_SENTCONVSEL         1 /* sent a request */
1211 #define XCLIB_XCOUT_INCR                2 /* in an incr loop */
1212 #define XCLIB_XCOUT_FALLBACK            3 /* STRING failed, need fallback to UTF8 */
1213 #define XCLIB_XCOUT_FALLBACK_UTF8       4 /* UTF8 failed, move to compouned */
1214 #define XCLIB_XCOUT_FALLBACK_COMP       5 /* compouned failed, move to text. */
1215 #define XCLIB_XCOUT_FALLBACK_TEXT       6
1216
1217 // Retrieves the contents of a selections.
1218 void GHOST_SystemX11::getClipboard_xcout(XEvent evt,
1219         Atom sel, Atom target, unsigned char **txt,
1220         unsigned long *len, unsigned int *context) const
1221 {
1222         Atom pty_type;
1223         int pty_format;
1224         unsigned char *buffer;
1225         unsigned long pty_size, pty_items;
1226         unsigned char *ltxt= *txt;
1227
1228         vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
1229         vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
1230         GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
1231         Window win = window->getXWindow();
1232
1233         switch (*context) {
1234                 // There is no context, do an XConvertSelection()
1235                 case XCLIB_XCOUT_NONE:
1236                         // Initialise return length to 0
1237                         if (*len > 0) {
1238                                 free(*txt);
1239                                 *len = 0;
1240                         }
1241
1242                         // Send a selection request
1243                         XConvertSelection(m_display, sel, target, m_xclip_out, win, CurrentTime);
1244                         *context = XCLIB_XCOUT_SENTCONVSEL;
1245                         return;
1246
1247                 case XCLIB_XCOUT_SENTCONVSEL:
1248                         if (evt.type != SelectionNotify)
1249                                 return;
1250
1251                         if (target == m_utf8_string && evt.xselection.property == None) {
1252                                 *context= XCLIB_XCOUT_FALLBACK_UTF8;
1253                                 return;
1254                         }
1255                         else if (target == m_compound_text && evt.xselection.property == None) {
1256                                 *context= XCLIB_XCOUT_FALLBACK_COMP;
1257                                 return;
1258                         }
1259                         else if (target == m_text && evt.xselection.property == None) {
1260                                 *context= XCLIB_XCOUT_FALLBACK_TEXT;
1261                                 return;
1262                         }
1263
1264                         // find the size and format of the data in property
1265                         XGetWindowProperty(m_display, win, m_xclip_out, 0, 0, False,
1266                                 AnyPropertyType, &pty_type, &pty_format,
1267                                 &pty_items, &pty_size, &buffer);
1268                         XFree(buffer);
1269
1270                         if (pty_type == m_incr) {
1271                                 // start INCR mechanism by deleting property
1272                                 XDeleteProperty(m_display, win, m_xclip_out);
1273                                 XFlush(m_display);
1274                                 *context = XCLIB_XCOUT_INCR;
1275                                 return;
1276                         }
1277
1278                         // if it's not incr, and not format == 8, then there's
1279                         // nothing in the selection (that xclip understands, anyway)
1280
1281                         if (pty_format != 8) {
1282                                 *context = XCLIB_XCOUT_NONE;
1283                                 return;
1284                         }
1285
1286                         // not using INCR mechanism, just read the property
1287                         XGetWindowProperty(m_display, win, m_xclip_out, 0, (long) pty_size,
1288                                         False, AnyPropertyType, &pty_type,
1289                                         &pty_format, &pty_items, &pty_size, &buffer);
1290
1291                         // finished with property, delete it
1292                         XDeleteProperty(m_display, win, m_xclip_out);
1293
1294                         // copy the buffer to the pointer for returned data
1295                         ltxt = (unsigned char *) malloc(pty_items);
1296                         memcpy(ltxt, buffer, pty_items);
1297
1298                         // set the length of the returned data
1299                         *len = pty_items;
1300                         *txt = ltxt;
1301
1302                         // free the buffer
1303                         XFree(buffer);
1304
1305                         *context = XCLIB_XCOUT_NONE;
1306
1307                         // complete contents of selection fetched, return 1
1308                         return;
1309
1310                 case XCLIB_XCOUT_INCR:
1311                         // To use the INCR method, we basically delete the
1312                         // property with the selection in it, wait for an
1313                         // event indicating that the property has been created,
1314                         // then read it, delete it, etc.
1315
1316                         // make sure that the event is relevant
1317                         if (evt.type != PropertyNotify)
1318                                 return;
1319
1320                         // skip unless the property has a new value
1321                         if (evt.xproperty.state != PropertyNewValue)
1322                                 return;
1323
1324                         // check size and format of the property
1325                         XGetWindowProperty(m_display, win, m_xclip_out, 0, 0, False,
1326                                 AnyPropertyType, &pty_type, &pty_format,
1327                                 &pty_items, &pty_size, (unsigned char **) &buffer);
1328
1329                         if (pty_format != 8) {
1330                                 // property does not contain text, delete it
1331                                 // to tell the other X client that we have read 
1332                                 // it and to send the next property
1333                                 XFree(buffer);
1334                                 XDeleteProperty(m_display, win, m_xclip_out);
1335                                 return;
1336                         }
1337
1338                         if (pty_size == 0) {
1339                                 // no more data, exit from loop
1340                                 XFree(buffer);
1341                                 XDeleteProperty(m_display, win, m_xclip_out);
1342                                 *context = XCLIB_XCOUT_NONE;
1343
1344                                 // this means that an INCR transfer is now
1345                                 // complete, return 1
1346                                 return;
1347                         }
1348
1349                         XFree(buffer);
1350
1351                         // if we have come this far, the propery contains
1352                         // text, we know the size.
1353                         XGetWindowProperty(m_display, win, m_xclip_out, 0, (long) pty_size,
1354                                 False, AnyPropertyType, &pty_type, &pty_format,
1355                                 &pty_items, &pty_size, (unsigned char **) &buffer);
1356
1357                         // allocate memory to accommodate data in *txt
1358                         if (*len == 0) {
1359                                 *len = pty_items;
1360                                 ltxt = (unsigned char *) malloc(*len);
1361                         }
1362                         else {
1363                                 *len += pty_items;
1364                                 ltxt = (unsigned char *) realloc(ltxt, *len);
1365                         }
1366
1367                         // add data to ltxt
1368                         memcpy(&ltxt[*len - pty_items], buffer, pty_items);
1369
1370                         *txt = ltxt;
1371                         XFree(buffer);
1372
1373                         // delete property to get the next item
1374                         XDeleteProperty(m_display, win, m_xclip_out);
1375                         XFlush(m_display);
1376                         return;
1377         }
1378         return;
1379 }
1380
1381 GHOST_TUns8 *GHOST_SystemX11::getClipboard(bool selection) const
1382 {
1383         Atom sseln;
1384         Atom target= m_utf8_string;
1385         Window owner;
1386
1387         // from xclip.c doOut() v0.11
1388         unsigned char *sel_buf;
1389         unsigned long sel_len= 0;
1390         XEvent evt;
1391         unsigned int context= XCLIB_XCOUT_NONE;
1392
1393         if (selection == True)
1394                 sseln= m_primary;
1395         else
1396                 sseln= m_clipboard;
1397
1398         vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
1399         vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
1400         GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
1401         Window win = window->getXWindow();
1402
1403         /* check if we are the owner. */
1404         owner= XGetSelectionOwner(m_display, sseln);
1405         if (owner == win) {
1406                 if (sseln == m_clipboard) {
1407                         sel_buf= (unsigned char *)malloc(strlen(txt_cut_buffer)+1);
1408                         strcpy((char *)sel_buf, txt_cut_buffer);
1409                         return((GHOST_TUns8*)sel_buf);
1410                 }
1411                 else {
1412                         sel_buf= (unsigned char *)malloc(strlen(txt_select_buffer)+1);
1413                         strcpy((char *)sel_buf, txt_select_buffer);
1414                         return((GHOST_TUns8*)sel_buf);
1415                 }
1416         }
1417         else if (owner == None)
1418                 return(NULL);
1419
1420         while (1) {
1421                 /* only get an event if xcout() is doing something */
1422                 if (context != XCLIB_XCOUT_NONE)
1423                         XNextEvent(m_display, &evt);
1424
1425                 /* fetch the selection, or part of it */
1426                 getClipboard_xcout(evt, sseln, target, &sel_buf, &sel_len, &context);
1427
1428                 /* fallback is needed. set XA_STRING to target and restart the loop. */
1429                 if (context == XCLIB_XCOUT_FALLBACK) {
1430                         context= XCLIB_XCOUT_NONE;
1431                         target= m_string;
1432                         continue;
1433                 }
1434                 else if (context == XCLIB_XCOUT_FALLBACK_UTF8) {
1435                         /* utf8 fail, move to compouned text. */
1436                         context= XCLIB_XCOUT_NONE;
1437                         target= m_compound_text;
1438                         continue;
1439                 }
1440                 else if (context == XCLIB_XCOUT_FALLBACK_COMP) {
1441                         /* compouned text faile, move to text. */
1442                         context= XCLIB_XCOUT_NONE;
1443                         target= m_text;
1444                         continue;
1445                 }
1446
1447                 /* only continue if xcout() is doing something */
1448                 if (context == XCLIB_XCOUT_NONE)
1449                         break;
1450         }
1451
1452         if (sel_len) {
1453                 /* only print the buffer out, and free it, if it's not
1454                  * empty
1455                  */
1456                 unsigned char *tmp_data = (unsigned char*) malloc(sel_len+1);
1457                 memcpy((char*)tmp_data, (char*)sel_buf, sel_len);
1458                 tmp_data[sel_len] = '\0';
1459                 
1460                 if (sseln == m_string)
1461                         XFree(sel_buf);
1462                 else
1463                         free(sel_buf);
1464                 
1465                 return (GHOST_TUns8*)tmp_data;
1466         }
1467         return(NULL);
1468 }
1469
1470 void GHOST_SystemX11::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1471 {
1472         Window m_window, owner;
1473
1474         vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();      
1475         vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
1476         GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
1477         m_window = window->getXWindow();
1478
1479         if (buffer) {
1480                 if (selection == False) {
1481                         XSetSelectionOwner(m_display, m_clipboard, m_window, CurrentTime);
1482                         owner= XGetSelectionOwner(m_display, m_clipboard);
1483                         if (txt_cut_buffer)
1484                                 free((void*)txt_cut_buffer);
1485
1486                         txt_cut_buffer = (char*) malloc(strlen(buffer)+1);
1487                         strcpy(txt_cut_buffer, buffer);
1488                 } else {
1489                         XSetSelectionOwner(m_display, m_primary, m_window, CurrentTime);
1490                         owner= XGetSelectionOwner(m_display, m_primary);
1491                         if (txt_select_buffer)
1492                                 free((void*)txt_select_buffer);
1493
1494                         txt_select_buffer = (char*) malloc(strlen(buffer)+1);
1495                         strcpy(txt_select_buffer, buffer);
1496                 }
1497
1498                 if (owner != m_window)
1499                         fprintf(stderr, "failed to own primary\n");
1500         }
1501 }
1502
1503 #ifdef WITH_XDND
1504 GHOST_TSuccess GHOST_SystemX11::pushDragDropEvent(GHOST_TEventType eventType, 
1505                                                                                                         GHOST_TDragnDropTypes draggedObjectType,
1506                                                                                                         GHOST_IWindow* window,
1507                                                                                                         int mouseX, int mouseY,
1508                                                                                                         void* data)
1509 {
1510         GHOST_SystemX11* system = ((GHOST_SystemX11*)getSystem());
1511         return system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(),
1512                                                                                                           eventType,
1513                                                                                                           draggedObjectType,
1514                                                                                                           window,mouseX,mouseY,data)
1515                         );
1516 }
1517 #endif