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