svn merge ^/trunk/blender -r44204:44213
[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                         if(window->getCursorGrabMode() != GHOST_kGrabDisable && window->getCursorGrabMode() != GHOST_kGrabNormal)
476                         {
477                                 GHOST_TInt32 x_new= xme.x_root;
478                                 GHOST_TInt32 y_new= xme.y_root;
479                                 GHOST_TInt32 x_accum, y_accum;
480                                 GHOST_Rect bounds;
481
482                                 /* fallback to window bounds */
483                                 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure)
484                                         window->getClientBounds(bounds);
485
486                                 /* could also clamp to screen bounds
487                                  * wrap with a window outside the view will fail atm  */
488                                 bounds.wrapPoint(x_new, y_new, 8); /* offset of one incase blender is at screen bounds */
489                                 window->getCursorGrabAccum(x_accum, y_accum);
490
491                                 if(x_new != xme.x_root || y_new != xme.y_root) {
492                                         if (xme.time > m_last_warp) {
493                                                 /* when wrapping we don't need to add an event because the
494                                                  * setCursorPosition call will cause a new event after */
495                                                 setCursorPosition(x_new, y_new); /* wrap */
496                                                 window->setCursorGrabAccum(x_accum + (xme.x_root - x_new), y_accum + (xme.y_root - y_new));
497                                                 m_last_warp = lastEventTime(xme.time);
498                                         } else {
499                                                 setCursorPosition(x_new, y_new); /* wrap but don't accumulate */
500                                         }
501                                 }
502                                 else {
503                                         g_event = new
504                                         GHOST_EventCursor(
505                                                 getMilliSeconds(),
506                                                 GHOST_kEventCursorMove,
507                                                 window,
508                                                 xme.x_root + x_accum,
509                                                 xme.y_root + y_accum
510                                         );
511                                 }
512                         }
513                         else {
514                                 g_event = new
515                                 GHOST_EventCursor(
516                                         getMilliSeconds(),
517                                         GHOST_kEventCursorMove,
518                                         window,
519                                         xme.x_root,
520                                         xme.y_root
521                                 );
522                         }
523                         break;
524                 }
525
526                 case KeyPress:
527                 case KeyRelease:
528                 {
529                         XKeyEvent *xke = &(xe->xkey);
530                         KeySym key_sym = XLookupKeysym(xke,0);
531                         char ascii;
532                         char utf8_buf[6]; /* 6 is enough for a utf8 char */
533                         
534                         GHOST_TKey gkey = convertXKey(key_sym);
535                         GHOST_TEventType type = (xke->type == KeyPress) ? 
536                                 GHOST_kEventKeyDown : GHOST_kEventKeyUp;
537                         
538                         if (!XLookupString(xke, &ascii, 1, NULL, NULL)) {
539                                 ascii = '\0';
540                         }
541                         
542 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
543                         /* getting unicode on key-up events gives XLookupNone status */
544                         if (xke->type == KeyPress) {
545                                 Status status;
546                                 int len;
547
548                                 /* use utf8 because its not locale depentant, from xorg docs */
549                                 if (!(len= Xutf8LookupString(window->getX11_XIC(), xke, utf8_buf, sizeof(utf8_buf), &key_sym, &status))) {
550                                         utf8_buf[0]= '\0';
551                                 }
552
553                                 if ((status == XLookupChars || status == XLookupBoth)) {
554                                         if ((unsigned char)utf8_buf[0] >= 32) { /* not an ascii control character */
555                                                 /* do nothing for now, this is valid utf8 */
556                                         }
557                                         else {
558                                                 utf8_buf[0]= '\0';
559                                         }
560                                 }
561                                 else if (status == XLookupKeySym) {
562                                         /* this key doesn't have a text representation, it is a command
563                                            key of some sort */;
564                                 }
565                                 else {
566                                         printf("Bad keycode lookup. Keysym 0x%x Status: %s\n",
567                                                           (unsigned int) key_sym,
568                                                           (status == XBufferOverflow ? "BufferOverflow" :
569                                                            status == XLookupNone ? "XLookupNone" :
570                                                            status == XLookupKeySym ? "XLookupKeySym" :
571                                                            "Unknown status"));
572
573                                         printf("'%.*s' %p %p\n", len, utf8_buf, window->getX11_XIC(), m_xim);
574                                 }
575                         }
576                         else {
577                                 utf8_buf[0]= '\0';
578                         }
579 #else
580                         utf8_buf[0]= '\0';
581 #endif
582
583                         g_event = new
584                         GHOST_EventKey(
585                                 getMilliSeconds(),
586                                 type,
587                                 window,
588                                 gkey,
589                                 ascii,
590                             utf8_buf
591                         );
592                         
593                 break;
594                 }
595
596                 case ButtonPress:
597                 case ButtonRelease:
598                 {
599                         XButtonEvent & xbe = xe->xbutton;
600                         GHOST_TButtonMask gbmask = GHOST_kButtonMaskLeft;
601                         GHOST_TEventType type = (xbe.type == ButtonPress) ? 
602                                 GHOST_kEventButtonDown : GHOST_kEventButtonUp;
603
604                         /* process wheel mouse events and break, only pass on press events */
605                         if(xbe.button == Button4) {
606                                 if(xbe.type == ButtonPress)
607                                         g_event = new GHOST_EventWheel(getMilliSeconds(), window, 1);
608                                 break;
609                         }
610                         else if(xbe.button == Button5) {
611                                 if(xbe.type == ButtonPress)
612                                         g_event = new GHOST_EventWheel(getMilliSeconds(), window, -1);
613                                 break;
614                         }
615                         
616                         /* process rest of normal mouse buttons */
617                         if(xbe.button == Button1)
618                                 gbmask = GHOST_kButtonMaskLeft;
619                         else if(xbe.button == Button2)
620                                 gbmask = GHOST_kButtonMaskMiddle;
621                         else if(xbe.button == Button3)
622                                 gbmask = GHOST_kButtonMaskRight;
623                         /* It seems events 6 and 7 are for horizontal scrolling.
624                         * you can re-order button mapping like this... (swaps 6,7 with 8,9)
625                         *   xmodmap -e "pointer = 1 2 3 4 5 8 9 6 7" 
626                         */
627                         else if(xbe.button == 8)
628                                 gbmask = GHOST_kButtonMaskButton4;
629                         else if(xbe.button == 9)
630                                 gbmask = GHOST_kButtonMaskButton5;
631                         else
632                                 break;
633
634                         g_event = new
635                         GHOST_EventButton(
636                                 getMilliSeconds(),
637                                 type,
638                                 window,
639                                 gbmask
640                         );
641                         break;
642                 }
643                         
644                         // change of size, border, layer etc.
645                 case ConfigureNotify:
646                 {
647                         /* XConfigureEvent & xce = xe->xconfigure; */
648
649                         g_event = new 
650                         GHOST_Event(
651                                 getMilliSeconds(),
652                                 GHOST_kEventWindowSize,
653                                 window
654                         );                      
655                         break;
656                 }
657
658                 case FocusIn:
659                 case FocusOut:
660                 {
661                         XFocusChangeEvent &xfe = xe->xfocus;
662
663                         // TODO: make sure this is the correct place for activate/deactivate
664                         // printf("X: focus %s for window %d\n", xfe.type == FocusIn ? "in" : "out", (int) xfe.window);
665                 
666                         // May have to look at the type of event and filter some
667                         // out.
668                                                                         
669                         GHOST_TEventType gtype = (xfe.type == FocusIn) ? 
670                                 GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate;
671
672                         g_event = new 
673                         GHOST_Event(    
674                                 getMilliSeconds(),
675                                 gtype,
676                                 window
677                         );
678                         break;
679
680                 }
681                 case ClientMessage:
682                 {
683                         XClientMessageEvent & xcme = xe->xclient;
684
685                         if (((Atom)xcme.data.l[0]) == m_delete_window_atom) {
686                                 g_event = new 
687                                 GHOST_Event(    
688                                         getMilliSeconds(),
689                                         GHOST_kEventWindowClose,
690                                         window
691                                 );
692                         }
693                         else if (((Atom)xcme.data.l[0]) == m_wm_take_focus) {
694                                 XWindowAttributes attr;
695                                 Window fwin;
696                                 int revert_to;
697
698                                 /* as ICCCM say, we need reply this event
699                                  * with a SetInputFocus, the data[1] have
700                                  * the valid timestamp (send by the wm).
701                                  *
702                                  * Some WM send this event before the
703                                  * window is really mapped (for example
704                                  * change from virtual desktop), so we need
705                                  * to be sure that our windows is mapped
706                                  * or this call fail and close blender.
707                                  */
708                                 if (XGetWindowAttributes(m_display, xcme.window, &attr) == True) {
709                                         if (XGetInputFocus(m_display, &fwin, &revert_to) == True) {
710                                                 if (attr.map_state == IsViewable) {
711                                                         if (fwin != xcme.window)
712                                                                 XSetInputFocus(m_display, xcme.window, RevertToParent, xcme.data.l[1]);
713                                                 }
714                                         }
715                                 }
716                         } else {
717 #ifdef WITH_XDND
718                                 /* try to handle drag event (if there's no such events, GHOST_HandleClientMessage will return zero) */
719                                 if (window->getDropTarget()->GHOST_HandleClientMessage(xe) == false) {
720                                         /* Unknown client message, ignore */
721                                 }
722 #else
723                                 /* Unknown client message, ignore */
724 #endif
725                         }
726
727                         break;
728                 }
729                 
730                 case DestroyNotify:
731                         ::exit(-1);     
732                 // We're not interested in the following things.(yet...)
733                 case NoExpose : 
734                 case GraphicsExpose :
735                         break;
736                 
737                 case EnterNotify:
738                 case LeaveNotify:
739                 {
740                         /* XCrossingEvents pointer leave enter window.
741                            also do cursor move here, MotionNotify only
742                            happens when motion starts & ends inside window.
743                            we only do moves when the crossing mode is 'normal'
744                            (really crossing between windows) since some windowmanagers
745                            also send grab/ungrab crossings for mousewheel events.
746                         */
747                         XCrossingEvent &xce = xe->xcrossing;
748                         if( xce.mode == NotifyNormal ) {
749                                 g_event = new 
750                                 GHOST_EventCursor(
751                                         getMilliSeconds(),
752                                         GHOST_kEventCursorMove,
753                                         window,
754                                         xce.x_root,
755                                         xce.y_root
756                                 );
757                         }
758
759                         // printf("X: %s window %d\n", xce.type == EnterNotify ? "entering" : "leaving", (int) xce.window);
760
761                         if (xce.type == EnterNotify)
762                                 m_windowManager->setActiveWindow(window);
763                         else
764                                 m_windowManager->setWindowInactive(window);
765
766                         break;
767                 }
768                 case MapNotify:
769                         /*
770                          * From ICCCM:
771                          * [ Clients can select for StructureNotify on their
772                          *   top-level windows to track transition between
773                          *   Normal and Iconic states. Receipt of a MapNotify
774                          *   event will indicate a transition to the Normal
775                          *   state, and receipt of an UnmapNotify event will
776                          *   indicate a transition to the Iconic state. ]
777                          */
778                         if (window->m_post_init == True) {
779                                 /*
780                                  * Now we are sure that the window is
781                                  * mapped, so only need change the state.
782                                  */
783                                 window->setState (window->m_post_state);
784                                 window->m_post_init = False;
785                         }
786                         break;
787                 case UnmapNotify:
788                         break;
789                 case MappingNotify:
790                 case ReparentNotify:
791                         break;
792                 case SelectionRequest:
793                 {
794                         XEvent nxe;
795                         Atom target, utf8_string, string, compound_text, c_string;
796                         XSelectionRequestEvent *xse = &xe->xselectionrequest;
797                         
798                         target = XInternAtom(m_display, "TARGETS", False);
799                         utf8_string = XInternAtom(m_display, "UTF8_STRING", False);
800                         string = XInternAtom(m_display, "STRING", False);
801                         compound_text = XInternAtom(m_display, "COMPOUND_TEXT", False);
802                         c_string = XInternAtom(m_display, "C_STRING", False);
803                         
804                         /* support obsolete clients */
805                         if (xse->property == None) {
806                                 xse->property = xse->target;
807                         }
808                         
809                         nxe.xselection.type = SelectionNotify;
810                         nxe.xselection.requestor = xse->requestor;
811                         nxe.xselection.property = xse->property;
812                         nxe.xselection.display = xse->display;
813                         nxe.xselection.selection = xse->selection;
814                         nxe.xselection.target = xse->target;
815                         nxe.xselection.time = xse->time;
816                         
817                         /*Check to see if the requestor is asking for String*/
818                         if(xse->target == utf8_string || xse->target == string || xse->target == compound_text || xse->target == c_string) {
819                                 if (xse->selection == XInternAtom(m_display, "PRIMARY", False)) {
820                                         XChangeProperty(m_display, xse->requestor, xse->property, xse->target, 8, PropModeReplace,
821                                                         (unsigned char*)txt_select_buffer, strlen(txt_select_buffer));
822                                 } else if (xse->selection == XInternAtom(m_display, "CLIPBOARD", False)) {
823                                         XChangeProperty(m_display, xse->requestor, xse->property, xse->target, 8, PropModeReplace,
824                                                         (unsigned char*)txt_cut_buffer, strlen(txt_cut_buffer));
825                                 }
826                         } else if (xse->target == target) {
827                                 Atom alist[5];
828                                 alist[0] = target;
829                                 alist[1] = utf8_string;
830                                 alist[2] = string;
831                                 alist[3] = compound_text;
832                                 alist[4] = c_string;
833                                 XChangeProperty(m_display, xse->requestor, xse->property, xse->target, 32, PropModeReplace,
834                                                 (unsigned char*)alist, 5);
835                                 XFlush(m_display);
836                         } else  {
837                                 //Change property to None because we do not support anything but STRING
838                                 nxe.xselection.property = None;
839                         }
840                         
841                         //Send the event to the client 0 0 == False, SelectionNotify
842                         XSendEvent(m_display, xse->requestor, 0, 0, &nxe);
843                         XFlush(m_display);
844                         break;
845                 }
846                 
847                 default: {
848 #ifdef WITH_X11_XINPUT
849                         if(xe->type == window->GetXTablet().MotionEvent) 
850                         {
851                                 XDeviceMotionEvent* data = (XDeviceMotionEvent*)xe;
852
853                                 /* stroke might begin without leading ProxyIn event,
854                                  * this happens when window is opened when stylus is already hovering
855                                  * around tablet surface */
856                                 setTabletMode(window, data->deviceid);
857
858                                 window->GetXTablet().CommonData.Pressure= 
859                                         data->axis_data[2]/((float)window->GetXTablet().PressureLevels);
860                         
861                         /* the (short) cast and the &0xffff is bizarre and unexplained anywhere,
862                          * but I got garbage data without it. Found it in the xidump.c source --matt */
863                                 window->GetXTablet().CommonData.Xtilt= 
864                                         (short)(data->axis_data[3]&0xffff)/((float)window->GetXTablet().XtiltLevels);
865                                 window->GetXTablet().CommonData.Ytilt= 
866                                         (short)(data->axis_data[4]&0xffff)/((float)window->GetXTablet().YtiltLevels);
867                         }
868                         else if(xe->type == window->GetXTablet().ProxInEvent) 
869                         {
870                                 XProximityNotifyEvent* data = (XProximityNotifyEvent*)xe;
871
872                                 setTabletMode(window, data->deviceid);
873                         }
874                         else if(xe->type == window->GetXTablet().ProxOutEvent)
875                                 window->GetXTablet().CommonData.Active= GHOST_kTabletModeNone;
876 #endif // WITH_X11_XINPUT
877                         break;
878                 }
879         }
880
881         if (g_event) {
882                 pushEvent(g_event);
883         }
884 }
885
886         GHOST_TSuccess 
887 GHOST_SystemX11::
888 getModifierKeys(
889         GHOST_ModifierKeys& keys
890 ) const {
891
892         // analyse the masks retuned from XQueryPointer.
893
894         memset((void *)m_keyboard_vector,0,sizeof(m_keyboard_vector));
895
896         XQueryKeymap(m_display,(char *)m_keyboard_vector);
897
898         // now translate key symobols into keycodes and
899         // test with vector.
900
901         const static KeyCode shift_l = XKeysymToKeycode(m_display,XK_Shift_L);
902         const static KeyCode shift_r = XKeysymToKeycode(m_display,XK_Shift_R);
903         const static KeyCode control_l = XKeysymToKeycode(m_display,XK_Control_L);
904         const static KeyCode control_r = XKeysymToKeycode(m_display,XK_Control_R);
905         const static KeyCode alt_l = XKeysymToKeycode(m_display,XK_Alt_L);
906         const static KeyCode alt_r = XKeysymToKeycode(m_display,XK_Alt_R);
907         const static KeyCode super_l = XKeysymToKeycode(m_display,XK_Super_L);
908         const static KeyCode super_r = XKeysymToKeycode(m_display,XK_Super_R);
909
910         // shift
911         keys.set(GHOST_kModifierKeyLeftShift, ((m_keyboard_vector[shift_l >> 3] >> (shift_l & 7)) & 1) != 0);
912         keys.set(GHOST_kModifierKeyRightShift, ((m_keyboard_vector[shift_r >> 3] >> (shift_r & 7)) & 1) != 0);
913         // control
914         keys.set(GHOST_kModifierKeyLeftControl, ((m_keyboard_vector[control_l >> 3] >> (control_l & 7)) & 1) != 0);
915         keys.set(GHOST_kModifierKeyRightControl, ((m_keyboard_vector[control_r >> 3] >> (control_r & 7)) & 1) != 0);
916         // alt
917         keys.set(GHOST_kModifierKeyLeftAlt, ((m_keyboard_vector[alt_l >> 3] >> (alt_l & 7)) & 1) != 0);
918         keys.set(GHOST_kModifierKeyRightAlt, ((m_keyboard_vector[alt_r >> 3] >> (alt_r & 7)) & 1) != 0);
919         // super (windows) - only one GHOST-kModifierKeyOS, so mapping to either
920         keys.set(GHOST_kModifierKeyOS, ( ((m_keyboard_vector[super_l >> 3] >> (super_l & 7)) & 1) ||
921                                          ((m_keyboard_vector[super_r >> 3] >> (super_r & 7)) & 1) ) != 0);
922
923         return GHOST_kSuccess;
924 }
925
926         GHOST_TSuccess 
927 GHOST_SystemX11::
928 getButtons(
929         GHOST_Buttons& buttons
930 ) const {
931
932         Window root_return, child_return;
933         int rx,ry,wx,wy;
934         unsigned int mask_return;
935
936         if (XQueryPointer(m_display,
937                           RootWindow(m_display,DefaultScreen(m_display)),
938                           &root_return,
939                           &child_return,
940                           &rx,&ry,
941                           &wx,&wy,
942                           &mask_return) == True)
943         {
944                 buttons.set(GHOST_kButtonMaskLeft,   (mask_return & Button1Mask) != 0);
945                 buttons.set(GHOST_kButtonMaskMiddle, (mask_return & Button2Mask) != 0);
946                 buttons.set(GHOST_kButtonMaskRight,  (mask_return & Button3Mask) != 0);
947         }
948         else {
949                 return GHOST_kFailure;
950         }       
951
952         return GHOST_kSuccess;
953 }
954
955
956         GHOST_TSuccess 
957 GHOST_SystemX11::
958 getCursorPosition(
959         GHOST_TInt32& x,
960         GHOST_TInt32& y
961 ) const {
962
963         Window root_return, child_return;
964         int rx,ry,wx,wy;
965         unsigned int mask_return;
966
967         if (XQueryPointer(
968                 m_display,
969                 RootWindow(m_display,DefaultScreen(m_display)),
970                 &root_return,
971                 &child_return,
972                 &rx,&ry,
973                 &wx,&wy,
974                 &mask_return
975         ) == False) {
976                 return GHOST_kFailure;
977         } else {
978                 x = rx;
979                 y = ry;
980         }       
981         return GHOST_kSuccess;
982 }
983
984
985         GHOST_TSuccess 
986 GHOST_SystemX11::
987 setCursorPosition(
988         GHOST_TInt32 x,
989         GHOST_TInt32 y
990 ) {
991
992         // This is a brute force move in screen coordinates
993         // XWarpPointer does relative moves so first determine the
994         // current pointer position.
995
996         int cx,cy;
997         if (getCursorPosition(cx,cy) == GHOST_kFailure) {
998                 return GHOST_kFailure;
999         }
1000
1001         int relx = x-cx;
1002         int rely = y-cy;
1003
1004         XWarpPointer(m_display,None,None,0,0,0,0,relx,rely);
1005         XSync(m_display, 0); /* Sync to process all requests */
1006         
1007         return GHOST_kSuccess;
1008 }
1009
1010
1011         void
1012 GHOST_SystemX11::
1013 addDirtyWindow(
1014         GHOST_WindowX11 * bad_wind
1015 ){
1016
1017         GHOST_ASSERT((bad_wind != NULL), "addDirtyWindow() NULL ptr trapped (window)");
1018         
1019         m_dirty_windows.push_back(bad_wind);
1020 }
1021
1022
1023         bool
1024 GHOST_SystemX11::
1025 generateWindowExposeEvents(
1026 ){
1027
1028         vector<GHOST_WindowX11 *>::iterator w_start = m_dirty_windows.begin();
1029         vector<GHOST_WindowX11 *>::const_iterator w_end = m_dirty_windows.end();
1030         bool anyProcessed = false;
1031         
1032         for (;w_start != w_end; ++w_start) {
1033                 GHOST_Event * g_event = new 
1034                         GHOST_Event(
1035                                 getMilliSeconds(),
1036                                 GHOST_kEventWindowUpdate,
1037                                 *w_start
1038                         );                      
1039
1040                 (*w_start)->validate(); 
1041                 
1042                 if (g_event) {
1043                         pushEvent(g_event);
1044                         anyProcessed = true;
1045                 }
1046         }
1047
1048         m_dirty_windows.clear();
1049         return anyProcessed;
1050 }
1051
1052 #define GXMAP(k,x,y) case x: k = y; break; 
1053
1054 static GHOST_TKey
1055 convertXKey(KeySym key)
1056 {
1057         GHOST_TKey type;
1058
1059         if ((key >= XK_A) && (key <= XK_Z)) {
1060                 type = GHOST_TKey( key - XK_A + int(GHOST_kKeyA));
1061         } else if ((key >= XK_a) && (key <= XK_z)) {
1062                 type = GHOST_TKey(key - XK_a + int(GHOST_kKeyA));
1063         } else if ((key >= XK_0) && (key <= XK_9)) {
1064                 type = GHOST_TKey(key - XK_0 + int(GHOST_kKey0));
1065         } else if ((key >= XK_F1) && (key <= XK_F24)) {
1066                 type = GHOST_TKey(key - XK_F1 + int(GHOST_kKeyF1));
1067 #if defined(__sun) || defined(__sun__) 
1068                 /* This is a bit of a hack, but it looks like sun
1069                    Used F11 and friends for its special keys Stop,again etc..
1070                    So this little patch enables F11 and F12 to work as expected
1071                    following link has documentation on it: 
1072                    http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4734408
1073                    also from /usr/include/X11/Sunkeysym.h 
1074 #define SunXK_F36               0x1005FF10      // Labeled F11
1075 #define SunXK_F37               0x1005FF11      // Labeled F12 
1076
1077                                 mein@cs.umn.edu
1078                  */
1079                 
1080         } else if (key == 268828432) {
1081                 type = GHOST_kKeyF11;
1082         } else if (key == 268828433) {
1083                 type = GHOST_kKeyF12;
1084 #endif
1085         } else {
1086                 switch(key) {
1087                         GXMAP(type,XK_BackSpace,        GHOST_kKeyBackSpace);
1088                         GXMAP(type,XK_Tab,              GHOST_kKeyTab);
1089                         GXMAP(type,XK_Return,           GHOST_kKeyEnter);
1090                         GXMAP(type,XK_Escape,           GHOST_kKeyEsc);
1091                         GXMAP(type,XK_space,            GHOST_kKeySpace);
1092                         
1093                         GXMAP(type,XK_Linefeed,         GHOST_kKeyLinefeed);
1094                         GXMAP(type,XK_semicolon,        GHOST_kKeySemicolon);
1095                         GXMAP(type,XK_period,           GHOST_kKeyPeriod);
1096                         GXMAP(type,XK_comma,            GHOST_kKeyComma);
1097                         GXMAP(type,XK_quoteright,       GHOST_kKeyQuote);
1098                         GXMAP(type,XK_quoteleft,        GHOST_kKeyAccentGrave);
1099                         GXMAP(type,XK_minus,            GHOST_kKeyMinus);
1100                         GXMAP(type,XK_slash,            GHOST_kKeySlash);
1101                         GXMAP(type,XK_backslash,        GHOST_kKeyBackslash);
1102                         GXMAP(type,XK_equal,            GHOST_kKeyEqual);
1103                         GXMAP(type,XK_bracketleft,      GHOST_kKeyLeftBracket);
1104                         GXMAP(type,XK_bracketright,     GHOST_kKeyRightBracket);
1105                         GXMAP(type,XK_Pause,            GHOST_kKeyPause);
1106                         
1107                         GXMAP(type,XK_Shift_L,          GHOST_kKeyLeftShift);
1108                         GXMAP(type,XK_Shift_R,          GHOST_kKeyRightShift);
1109                         GXMAP(type,XK_Control_L,        GHOST_kKeyLeftControl);
1110                         GXMAP(type,XK_Control_R,        GHOST_kKeyRightControl);
1111                         GXMAP(type,XK_Alt_L,            GHOST_kKeyLeftAlt);
1112                         GXMAP(type,XK_Alt_R,            GHOST_kKeyRightAlt);
1113                         GXMAP(type,XK_Super_L,          GHOST_kKeyOS);
1114                         GXMAP(type,XK_Super_R,          GHOST_kKeyOS);
1115
1116                         GXMAP(type,XK_Insert,           GHOST_kKeyInsert);
1117                         GXMAP(type,XK_Delete,           GHOST_kKeyDelete);
1118                         GXMAP(type,XK_Home,                     GHOST_kKeyHome);
1119                         GXMAP(type,XK_End,                      GHOST_kKeyEnd);
1120                         GXMAP(type,XK_Page_Up,          GHOST_kKeyUpPage);
1121                         GXMAP(type,XK_Page_Down,        GHOST_kKeyDownPage);
1122
1123                         GXMAP(type,XK_Left,                     GHOST_kKeyLeftArrow);
1124                         GXMAP(type,XK_Right,            GHOST_kKeyRightArrow);
1125                         GXMAP(type,XK_Up,                       GHOST_kKeyUpArrow);
1126                         GXMAP(type,XK_Down,                     GHOST_kKeyDownArrow);
1127
1128                         GXMAP(type,XK_Caps_Lock,        GHOST_kKeyCapsLock);
1129                         GXMAP(type,XK_Scroll_Lock,      GHOST_kKeyScrollLock);
1130                         GXMAP(type,XK_Num_Lock,         GHOST_kKeyNumLock);
1131                         
1132                                 /* keypad events */
1133                                 
1134                         GXMAP(type,XK_KP_0,                     GHOST_kKeyNumpad0);
1135                         GXMAP(type,XK_KP_1,                     GHOST_kKeyNumpad1);
1136                         GXMAP(type,XK_KP_2,                     GHOST_kKeyNumpad2);
1137                         GXMAP(type,XK_KP_3,                     GHOST_kKeyNumpad3);
1138                         GXMAP(type,XK_KP_4,                     GHOST_kKeyNumpad4);
1139                         GXMAP(type,XK_KP_5,                     GHOST_kKeyNumpad5);
1140                         GXMAP(type,XK_KP_6,                     GHOST_kKeyNumpad6);
1141                         GXMAP(type,XK_KP_7,                     GHOST_kKeyNumpad7);
1142                         GXMAP(type,XK_KP_8,                     GHOST_kKeyNumpad8);
1143                         GXMAP(type,XK_KP_9,                     GHOST_kKeyNumpad9);
1144                         GXMAP(type,XK_KP_Decimal,       GHOST_kKeyNumpadPeriod);
1145
1146                         GXMAP(type,XK_KP_Insert,        GHOST_kKeyNumpad0);
1147                         GXMAP(type,XK_KP_End,           GHOST_kKeyNumpad1);
1148                         GXMAP(type,XK_KP_Down,          GHOST_kKeyNumpad2);
1149                         GXMAP(type,XK_KP_Page_Down,     GHOST_kKeyNumpad3);
1150                         GXMAP(type,XK_KP_Left,          GHOST_kKeyNumpad4);
1151                         GXMAP(type,XK_KP_Begin,         GHOST_kKeyNumpad5);
1152                         GXMAP(type,XK_KP_Right,         GHOST_kKeyNumpad6);
1153                         GXMAP(type,XK_KP_Home,          GHOST_kKeyNumpad7);
1154                         GXMAP(type,XK_KP_Up,            GHOST_kKeyNumpad8);
1155                         GXMAP(type,XK_KP_Page_Up,       GHOST_kKeyNumpad9);
1156                         GXMAP(type,XK_KP_Delete,        GHOST_kKeyNumpadPeriod);
1157
1158                         GXMAP(type,XK_KP_Enter,         GHOST_kKeyNumpadEnter);
1159                         GXMAP(type,XK_KP_Add,           GHOST_kKeyNumpadPlus);
1160                         GXMAP(type,XK_KP_Subtract,      GHOST_kKeyNumpadMinus);
1161                         GXMAP(type,XK_KP_Multiply,      GHOST_kKeyNumpadAsterisk);
1162                         GXMAP(type,XK_KP_Divide,        GHOST_kKeyNumpadSlash);
1163
1164                         /* Media keys in some keyboards and laptops with XFree86/Xorg */
1165 #ifdef WITH_XF86KEYSYM
1166                         GXMAP(type,XF86XK_AudioPlay,    GHOST_kKeyMediaPlay);
1167                         GXMAP(type,XF86XK_AudioStop,    GHOST_kKeyMediaStop);
1168                         GXMAP(type,XF86XK_AudioPrev,    GHOST_kKeyMediaFirst);
1169                         GXMAP(type,XF86XK_AudioRewind,  GHOST_kKeyMediaFirst);
1170                         GXMAP(type,XF86XK_AudioNext,    GHOST_kKeyMediaLast);
1171 #ifdef XF86XK_AudioForward /* Debian lenny's XF86keysym.h has no XF86XK_AudioForward define */
1172                         GXMAP(type,XF86XK_AudioForward, GHOST_kKeyMediaLast);
1173 #endif
1174 #endif
1175
1176                                 /* some extra sun cruft (NICE KEYBOARD!) */
1177 #ifdef __sun__
1178                         GXMAP(type,0xffde,                      GHOST_kKeyNumpad1);
1179                         GXMAP(type,0xffe0,                      GHOST_kKeyNumpad3);
1180                         GXMAP(type,0xffdc,                      GHOST_kKeyNumpad5);
1181                         GXMAP(type,0xffd8,                      GHOST_kKeyNumpad7);
1182                         GXMAP(type,0xffda,                      GHOST_kKeyNumpad9);
1183
1184                         GXMAP(type,0xffd6,                      GHOST_kKeyNumpadSlash);
1185                         GXMAP(type,0xffd7,                      GHOST_kKeyNumpadAsterisk);
1186 #endif
1187
1188                         default :
1189                                 type = GHOST_kKeyUnknown;
1190                                 break;
1191                 }
1192         }
1193
1194         return type;
1195 }
1196
1197 #undef GXMAP
1198
1199 /* from xclip.c xcout() v0.11 */
1200
1201 #define XCLIB_XCOUT_NONE                0 /* no context */
1202 #define XCLIB_XCOUT_SENTCONVSEL         1 /* sent a request */
1203 #define XCLIB_XCOUT_INCR                2 /* in an incr loop */
1204 #define XCLIB_XCOUT_FALLBACK            3 /* STRING failed, need fallback to UTF8 */
1205 #define XCLIB_XCOUT_FALLBACK_UTF8       4 /* UTF8 failed, move to compouned */
1206 #define XCLIB_XCOUT_FALLBACK_COMP       5 /* compouned failed, move to text. */
1207 #define XCLIB_XCOUT_FALLBACK_TEXT       6
1208
1209 // Retrieves the contents of a selections.
1210 void GHOST_SystemX11::getClipboard_xcout(XEvent evt,
1211         Atom sel, Atom target, unsigned char **txt,
1212         unsigned long *len, unsigned int *context) const
1213 {
1214         Atom pty_type;
1215         int pty_format;
1216         unsigned char *buffer;
1217         unsigned long pty_size, pty_items;
1218         unsigned char *ltxt= *txt;
1219
1220         vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
1221         vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
1222         GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
1223         Window win = window->getXWindow();
1224
1225         switch (*context) {
1226                 // There is no context, do an XConvertSelection()
1227                 case XCLIB_XCOUT_NONE:
1228                         // Initialise return length to 0
1229                         if (*len > 0) {
1230                                 free(*txt);
1231                                 *len = 0;
1232                         }
1233
1234                         // Send a selection request
1235                         XConvertSelection(m_display, sel, target, m_xclip_out, win, CurrentTime);
1236                         *context = XCLIB_XCOUT_SENTCONVSEL;
1237                         return;
1238
1239                 case XCLIB_XCOUT_SENTCONVSEL:
1240                         if (evt.type != SelectionNotify)
1241                                 return;
1242
1243                         if (target == m_utf8_string && evt.xselection.property == None) {
1244                                 *context= XCLIB_XCOUT_FALLBACK_UTF8;
1245                                 return;
1246                         }
1247                         else if (target == m_compound_text && evt.xselection.property == None) {
1248                                 *context= XCLIB_XCOUT_FALLBACK_COMP;
1249                                 return;
1250                         }
1251                         else if (target == m_text && evt.xselection.property == None) {
1252                                 *context= XCLIB_XCOUT_FALLBACK_TEXT;
1253                                 return;
1254                         }
1255
1256                         // find the size and format of the data in property
1257                         XGetWindowProperty(m_display, win, m_xclip_out, 0, 0, False,
1258                                 AnyPropertyType, &pty_type, &pty_format,
1259                                 &pty_items, &pty_size, &buffer);
1260                         XFree(buffer);
1261
1262                         if (pty_type == m_incr) {
1263                                 // start INCR mechanism by deleting property
1264                                 XDeleteProperty(m_display, win, m_xclip_out);
1265                                 XFlush(m_display);
1266                                 *context = XCLIB_XCOUT_INCR;
1267                                 return;
1268                         }
1269
1270                         // if it's not incr, and not format == 8, then there's
1271                         // nothing in the selection (that xclip understands, anyway)
1272
1273                         if (pty_format != 8) {
1274                                 *context = XCLIB_XCOUT_NONE;
1275                                 return;
1276                         }
1277
1278                         // not using INCR mechanism, just read the property
1279                         XGetWindowProperty(m_display, win, m_xclip_out, 0, (long) pty_size,
1280                                         False, AnyPropertyType, &pty_type,
1281                                         &pty_format, &pty_items, &pty_size, &buffer);
1282
1283                         // finished with property, delete it
1284                         XDeleteProperty(m_display, win, m_xclip_out);
1285
1286                         // copy the buffer to the pointer for returned data
1287                         ltxt = (unsigned char *) malloc(pty_items);
1288                         memcpy(ltxt, buffer, pty_items);
1289
1290                         // set the length of the returned data
1291                         *len = pty_items;
1292                         *txt = ltxt;
1293
1294                         // free the buffer
1295                         XFree(buffer);
1296
1297                         *context = XCLIB_XCOUT_NONE;
1298
1299                         // complete contents of selection fetched, return 1
1300                         return;
1301
1302                 case XCLIB_XCOUT_INCR:
1303                         // To use the INCR method, we basically delete the
1304                         // property with the selection in it, wait for an
1305                         // event indicating that the property has been created,
1306                         // then read it, delete it, etc.
1307
1308                         // make sure that the event is relevant
1309                         if (evt.type != PropertyNotify)
1310                                 return;
1311
1312                         // skip unless the property has a new value
1313                         if (evt.xproperty.state != PropertyNewValue)
1314                                 return;
1315
1316                         // check size and format of the property
1317                         XGetWindowProperty(m_display, win, m_xclip_out, 0, 0, False,
1318                                 AnyPropertyType, &pty_type, &pty_format,
1319                                 &pty_items, &pty_size, (unsigned char **) &buffer);
1320
1321                         if (pty_format != 8) {
1322                                 // property does not contain text, delete it
1323                                 // to tell the other X client that we have read 
1324                                 // it and to send the next property
1325                                 XFree(buffer);
1326                                 XDeleteProperty(m_display, win, m_xclip_out);
1327                                 return;
1328                         }
1329
1330                         if (pty_size == 0) {
1331                                 // no more data, exit from loop
1332                                 XFree(buffer);
1333                                 XDeleteProperty(m_display, win, m_xclip_out);
1334                                 *context = XCLIB_XCOUT_NONE;
1335
1336                                 // this means that an INCR transfer is now
1337                                 // complete, return 1
1338                                 return;
1339                         }
1340
1341                         XFree(buffer);
1342
1343                         // if we have come this far, the propery contains
1344                         // text, we know the size.
1345                         XGetWindowProperty(m_display, win, m_xclip_out, 0, (long) pty_size,
1346                                 False, AnyPropertyType, &pty_type, &pty_format,
1347                                 &pty_items, &pty_size, (unsigned char **) &buffer);
1348
1349                         // allocate memory to accommodate data in *txt
1350                         if (*len == 0) {
1351                                 *len = pty_items;
1352                                 ltxt = (unsigned char *) malloc(*len);
1353                         }
1354                         else {
1355                                 *len += pty_items;
1356                                 ltxt = (unsigned char *) realloc(ltxt, *len);
1357                         }
1358
1359                         // add data to ltxt
1360                         memcpy(&ltxt[*len - pty_items], buffer, pty_items);
1361
1362                         *txt = ltxt;
1363                         XFree(buffer);
1364
1365                         // delete property to get the next item
1366                         XDeleteProperty(m_display, win, m_xclip_out);
1367                         XFlush(m_display);
1368                         return;
1369         }
1370         return;
1371 }
1372
1373 GHOST_TUns8 *GHOST_SystemX11::getClipboard(bool selection) const
1374 {
1375         Atom sseln;
1376         Atom target= m_utf8_string;
1377         Window owner;
1378
1379         // from xclip.c doOut() v0.11
1380         unsigned char *sel_buf;
1381         unsigned long sel_len= 0;
1382         XEvent evt;
1383         unsigned int context= XCLIB_XCOUT_NONE;
1384
1385         if (selection == True)
1386                 sseln= m_primary;
1387         else
1388                 sseln= m_clipboard;
1389
1390         vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
1391         vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
1392         GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
1393         Window win = window->getXWindow();
1394
1395         /* check if we are the owner. */
1396         owner= XGetSelectionOwner(m_display, sseln);
1397         if (owner == win) {
1398                 if (sseln == m_clipboard) {
1399                         sel_buf= (unsigned char *)malloc(strlen(txt_cut_buffer)+1);
1400                         strcpy((char *)sel_buf, txt_cut_buffer);
1401                         return((GHOST_TUns8*)sel_buf);
1402                 }
1403                 else {
1404                         sel_buf= (unsigned char *)malloc(strlen(txt_select_buffer)+1);
1405                         strcpy((char *)sel_buf, txt_select_buffer);
1406                         return((GHOST_TUns8*)sel_buf);
1407                 }
1408         }
1409         else if (owner == None)
1410                 return(NULL);
1411
1412         while (1) {
1413                 /* only get an event if xcout() is doing something */
1414                 if (context != XCLIB_XCOUT_NONE)
1415                         XNextEvent(m_display, &evt);
1416
1417                 /* fetch the selection, or part of it */
1418                 getClipboard_xcout(evt, sseln, target, &sel_buf, &sel_len, &context);
1419
1420                 /* fallback is needed. set XA_STRING to target and restart the loop. */
1421                 if (context == XCLIB_XCOUT_FALLBACK) {
1422                         context= XCLIB_XCOUT_NONE;
1423                         target= m_string;
1424                         continue;
1425                 }
1426                 else if (context == XCLIB_XCOUT_FALLBACK_UTF8) {
1427                         /* utf8 fail, move to compouned text. */
1428                         context= XCLIB_XCOUT_NONE;
1429                         target= m_compound_text;
1430                         continue;
1431                 }
1432                 else if (context == XCLIB_XCOUT_FALLBACK_COMP) {
1433                         /* compouned text faile, move to text. */
1434                         context= XCLIB_XCOUT_NONE;
1435                         target= m_text;
1436                         continue;
1437                 }
1438
1439                 /* only continue if xcout() is doing something */
1440                 if (context == XCLIB_XCOUT_NONE)
1441                         break;
1442         }
1443
1444         if (sel_len) {
1445                 /* only print the buffer out, and free it, if it's not
1446                  * empty
1447                  */
1448                 unsigned char *tmp_data = (unsigned char*) malloc(sel_len+1);
1449                 memcpy((char*)tmp_data, (char*)sel_buf, sel_len);
1450                 tmp_data[sel_len] = '\0';
1451                 
1452                 if (sseln == m_string)
1453                         XFree(sel_buf);
1454                 else
1455                         free(sel_buf);
1456                 
1457                 return (GHOST_TUns8*)tmp_data;
1458         }
1459         return(NULL);
1460 }
1461
1462 void GHOST_SystemX11::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1463 {
1464         Window m_window, owner;
1465
1466         vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();      
1467         vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
1468         GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
1469         m_window = window->getXWindow();
1470
1471         if (buffer) {
1472                 if (selection == False) {
1473                         XSetSelectionOwner(m_display, m_clipboard, m_window, CurrentTime);
1474                         owner= XGetSelectionOwner(m_display, m_clipboard);
1475                         if (txt_cut_buffer)
1476                                 free((void*)txt_cut_buffer);
1477
1478                         txt_cut_buffer = (char*) malloc(strlen(buffer)+1);
1479                         strcpy(txt_cut_buffer, buffer);
1480                 } else {
1481                         XSetSelectionOwner(m_display, m_primary, m_window, CurrentTime);
1482                         owner= XGetSelectionOwner(m_display, m_primary);
1483                         if (txt_select_buffer)
1484                                 free((void*)txt_select_buffer);
1485
1486                         txt_select_buffer = (char*) malloc(strlen(buffer)+1);
1487                         strcpy(txt_select_buffer, buffer);
1488                 }
1489
1490                 if (owner != m_window)
1491                         fprintf(stderr, "failed to own primary\n");
1492         }
1493 }
1494
1495 #ifdef WITH_XDND
1496 GHOST_TSuccess GHOST_SystemX11::pushDragDropEvent(GHOST_TEventType eventType, 
1497                                                                                                         GHOST_TDragnDropTypes draggedObjectType,
1498                                                                                                         GHOST_IWindow* window,
1499                                                                                                         int mouseX, int mouseY,
1500                                                                                                         void* data)
1501 {
1502         GHOST_SystemX11* system = ((GHOST_SystemX11*)getSystem());
1503         return system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(),
1504                                                                                                           eventType,
1505                                                                                                           draggedObjectType,
1506                                                                                                           window,mouseX,mouseY,data)
1507                         );
1508 }
1509 #endif