Add support for GHOST_kWindowState* to Linux.
[blender.git] / intern / ghost / intern / GHOST_SystemX11.cpp
1 /**
2  * $Id$
3  * ***** BEGIN GPL/BL DUAL 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. The Blender
9  * Foundation also sells licenses for use in proprietary software under
10  * the Blender License.  See http://www.blender.org/BL/ for information
11  * about this.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL/BL DUAL LICENSE BLOCK *****
30  */
31
32 /**
33  * $Id$
34  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
35  *
36  * This program is free software; you can redistribute it and/or
37  * modify it under the terms of the GNU General Public License
38  * as published by the Free Software Foundation; either version 2
39  * of the License, or (at your option) any later version. The Blender
40  * Foundation also sells licenses for use in proprietary software under
41  * the Blender License.  See http://www.blender.org/BL/ for information
42  * about this.
43  *
44  * This program is distributed in the hope that it will be useful,
45  * but WITHOUT ANY WARRANTY; without even the implied warranty of
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
47  * GNU General Public License for more details.
48  *
49  * You should have received a copy of the GNU General Public License
50  * along with this program; if not, write to the Free Software Foundation,
51  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
52  *
53  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
54  * All rights reserved.
55  *
56  * The Original Code is: all of this file.
57  *
58  * Contributor(s): none yet.
59  *
60  * ***** END GPL/BL DUAL LICENSE BLOCK *****
61  */
62
63 #ifdef HAVE_CONFIG_H
64 #include <config.h>
65 #endif
66
67 #include "GHOST_SystemX11.h"
68 #include "GHOST_WindowX11.h"
69 #include "GHOST_WindowManager.h"
70 #include "GHOST_TimerManager.h"
71 #include "GHOST_EventCursor.h"
72 #include "GHOST_EventKey.h"
73 #include "GHOST_EventButton.h"
74 #include "GHOST_EventWheel.h"
75 #include "GHOST_DisplayManagerX11.h"
76
77 #include "GHOST_Debug.h"
78
79 #include <X11/Xatom.h>
80 #include <X11/keysym.h>
81
82 #ifdef __sgi
83
84 #if defined(_SGI_EXTRA_PREDEFINES) && !defined(NO_FAST_ATOMS)
85 #include <X11/SGIFastAtom.h>
86 #else
87 #define XSGIFastInternAtom(dpy,string,fast_name,how) XInternAtom(dpy,string,how)
88 #endif
89
90 #endif
91
92 // For timing
93
94 #include <sys/time.h>
95 #include <unistd.h>
96
97 #include <vector>
98
99 using namespace std;
100
101 GHOST_SystemX11::
102 GHOST_SystemX11(
103 ) : 
104         GHOST_System(),
105         m_start_time(0)
106 {
107         m_display = XOpenDisplay(NULL);
108         
109         if (!m_display) return;
110         
111 #ifdef __sgi
112         m_delete_window_atom 
113           = XSGIFastInternAtom(m_display,
114                                "WM_DELETE_WINDOW", 
115                                SGI_XA_WM_DELETE_WINDOW, False);
116         /* Some one with SGI can tell me about this ? */
117         m_wm_state= None;
118         m_wm_change_state= None;
119         m_net_state= None;
120         m_net_max_horz= None;
121         m_net_max_vert= None;
122         m_net_fullscreen= None;
123         m_motif = None;
124 #else
125         m_delete_window_atom= XInternAtom(m_display, "WM_DELETE_WINDOW", False);
126         m_wm_state= XInternAtom(m_display, "WM_STATE", False);
127         m_wm_change_state= XInternAtom(m_display, "WM_CHANGE_STATE", False);
128         m_net_state= XInternAtom(m_display, "_NET_WM_STATE", False);
129         m_net_max_horz= XInternAtom(m_display,
130                                         "_NET_WM_STATE_MAXIMIZED_HORZ", False);
131         m_net_max_vert= XInternAtom(m_display,
132                                         "_NET_WM_STATE_MAXIMIZED_VERT", False);
133         m_net_fullscreen= XInternAtom(m_display,
134                                         "_NET_WM_STATE_FULLSCREEN", False);
135         m_motif= XInternAtom(m_display, "_MOTIF_WM_HINTS", False);
136 #endif
137
138         // compute the initial time
139         timeval tv;
140         if (gettimeofday(&tv,NULL) == -1) {
141                 GHOST_ASSERT(false,"Could not instantiate timer!");
142         }
143
144         m_start_time = GHOST_TUns64(tv.tv_sec*1000 + tv.tv_usec/1000);
145 }
146
147         GHOST_TSuccess 
148 GHOST_SystemX11::
149 init(
150 ){
151         GHOST_TSuccess success = GHOST_System::init();
152
153         if (success) {
154                 m_keyboard_vector = new char[32];
155
156                 m_displayManager = new GHOST_DisplayManagerX11(this);
157
158                 if (m_keyboard_vector && m_displayManager) {
159                         return GHOST_kSuccess;
160                 }
161         }
162
163         return GHOST_kFailure;
164 }
165         
166
167
168         GHOST_TUns64
169 GHOST_SystemX11::
170 getMilliSeconds(
171 ) const {
172         timeval tv;
173         if (gettimeofday(&tv,NULL) == -1) {
174                 GHOST_ASSERT(false,"Could not compute time!");
175         }
176
177         return  GHOST_TUns64(tv.tv_sec*1000 + tv.tv_usec/1000) - m_start_time;
178 }
179         
180         GHOST_TUns8 
181 GHOST_SystemX11::
182 getNumDisplays(
183 ) const {
184         return GHOST_TUns8(1);
185 }
186
187         /**
188          * Returns the dimensions of the main display on this system.
189          * @return The dimension of the main display.
190          */
191         void 
192 GHOST_SystemX11::
193 getMainDisplayDimensions(
194         GHOST_TUns32& width,
195         GHOST_TUns32& height
196 ) const {       
197         if (m_display) {
198                 width  = DisplayWidth(m_display, DefaultScreen(m_display));
199                 height = DisplayHeight(m_display, DefaultScreen(m_display));
200         }
201 }
202
203         /**
204          * Create a new window.
205          * The new window is added to the list of windows managed.
206          * Never explicitly delete the window, use disposeWindow() instead.
207          * @param       title   The name of the window (displayed in the title bar of the window if the OS supports it).
208          * @param       left    The coordinate of the left edge of the window.
209          * @param       top             The coordinate of the top edge of the window.
210          * @param       width   The width the window.
211          * @param       height  The height the window.
212          * @param       state   The state of the window when opened.
213          * @param       type    The type of drawing context installed in this window.
214          * @return      The new window (or 0 if creation failed).
215          */
216         GHOST_IWindow* 
217 GHOST_SystemX11::
218 createWindow(
219         const STR_String& title,
220         GHOST_TInt32 left,
221         GHOST_TInt32 top,
222         GHOST_TUns32 width,
223         GHOST_TUns32 height,
224         GHOST_TWindowState state,
225         GHOST_TDrawingContextType type,
226         bool stereoVisual
227 ){
228         GHOST_WindowX11 * window = 0;
229         
230         if (!m_display) return 0;
231         
232         window = new GHOST_WindowX11 (
233                 this,m_display,title, left, top, width, height, state, type, stereoVisual
234         );
235
236         if (window) {
237
238                 // Install a new protocol for this window - so we can overide
239                 // the default window closure mechanism.
240
241                 XSetWMProtocols(m_display, window->getXWindow(), &m_delete_window_atom, 1);
242
243                 if (window->getValid()) {
244                         // Store the pointer to the window 
245                         m_windowManager->addWindow(window);
246                         
247                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
248                 }
249                 else {
250                         delete window;
251                         window = 0;
252                 }
253         }
254         return window;
255
256 }
257
258         GHOST_WindowX11 * 
259 GHOST_SystemX11::
260 findGhostWindow(
261         Window xwind
262 ) const {
263         
264         if (xwind == 0) return NULL;
265
266         // It is not entirely safe to do this as the backptr may point
267         // to a window that has recently been removed. 
268         // We should always check the window manager's list of windows 
269         // and only process events on these windows.
270
271         vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
272
273         vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
274         vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
275         
276         for (; win_it != win_end; ++win_it) {
277                 GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
278                 if (window->getXWindow() == xwind) {
279                         return window;
280                 }
281         }
282         return NULL;
283         
284 }
285
286 static void SleepTillEvent(Display *display, GHOST_TInt64 maxSleep) {
287         int fd = ConnectionNumber(display);
288         fd_set fds;
289         
290         FD_ZERO(&fds);
291         FD_SET(fd, &fds);
292
293         if (maxSleep == -1) {
294             select(fd + 1, &fds, NULL, NULL, NULL);
295         } else {
296                 timeval tv;
297
298                 tv.tv_sec = maxSleep/1000;
299                 tv.tv_usec = (maxSleep - tv.tv_sec*1000)*1000;
300         
301             select(fd + 1, &fds, NULL, NULL, &tv);
302         }
303 }
304
305         bool 
306 GHOST_SystemX11::
307 processEvents(
308         bool waitForEvent
309 ){
310         // Get all the current events -- translate them into 
311         // ghost events and call base class pushEvent() method.
312         
313         bool anyProcessed = false;
314         
315         do {
316                 GHOST_TimerManager* timerMgr = getTimerManager();
317                 
318                 if (waitForEvent && m_dirty_windows.empty() && !XPending(m_display)) {
319                         GHOST_TUns64 next = timerMgr->nextFireTime();
320                         
321                         if (next==GHOST_kFireTimeNever) {
322                                 SleepTillEvent(m_display, -1);
323                         } else {
324                                 SleepTillEvent(m_display, next - getMilliSeconds());
325                         }
326                 }
327                 
328                 if (timerMgr->fireTimers(getMilliSeconds())) {
329                         anyProcessed = true;
330                 }
331                 
332                 while (XPending(m_display)) {
333                         XEvent xevent;
334                         XNextEvent(m_display, &xevent);
335                         processEvent(&xevent);
336                         anyProcessed = true;
337                 }
338                 
339                 if (generateWindowExposeEvents()) {
340                         anyProcessed = true;
341                 }
342         } while (waitForEvent && !anyProcessed);
343         
344         return anyProcessed;
345 }
346
347         void
348 GHOST_SystemX11::processEvent(XEvent *xe)
349 {
350         GHOST_WindowX11 * window = findGhostWindow(xe->xany.window);    
351         GHOST_Event * g_event = NULL;
352
353         if (!window) {
354                 return;
355         }
356
357         switch (xe->type) {
358                 case Expose:
359                 {
360                         XExposeEvent & xee = xe->xexpose;
361
362                         if (xee.count == 0) {
363                                 // Only generate a single expose event
364                                 // per read of the event queue.
365
366                                 g_event = new 
367                                 GHOST_Event(
368                                         getMilliSeconds(),
369                                         GHOST_kEventWindowUpdate,
370                                         window
371                                 );                      
372                         }
373                         break;
374                 }
375
376                 case MotionNotify:
377                 {
378                         XMotionEvent &xme = xe->xmotion;
379                         
380                         g_event = new 
381                         GHOST_EventCursor(
382                                 getMilliSeconds(),
383                                 GHOST_kEventCursorMove,
384                                 window,
385                                 xme.x_root,
386                                 xme.y_root
387                         );
388                         break;
389                 }
390
391                 case KeyPress:
392                 case KeyRelease:
393                 {
394                         XKeyEvent *xke = &(xe->xkey);
395                 
396                         KeySym key_sym = XLookupKeysym(xke,0);
397                         char ascii;
398                         
399                         GHOST_TKey gkey = convertXKey(key_sym);
400                         GHOST_TEventType type = (xke->type == KeyPress) ? 
401                                 GHOST_kEventKeyDown : GHOST_kEventKeyUp;
402                         
403                         if (!XLookupString(xke, &ascii, 1, NULL, NULL)) {
404                                 ascii = '\0';
405                         }
406                         
407                         g_event = new
408                         GHOST_EventKey(
409                                 getMilliSeconds(),
410                                 type,
411                                 window,
412                                 gkey,
413                                 ascii
414                         );
415                         
416                 break;
417                 }
418
419                 case ButtonPress:
420                 {
421                         /* process wheel mouse events and break */
422                         if (xe->xbutton.button == 4) {
423                                 g_event = new GHOST_EventWheel(getMilliSeconds(), window, 1);
424                                 break;
425                         }
426                         if (xe->xbutton.button == 5) {
427                                 g_event = new GHOST_EventWheel(getMilliSeconds(), window, -1);
428                                 break;
429                         }
430                 }
431                 case ButtonRelease:
432                 {
433
434                         XButtonEvent & xbe = xe->xbutton;
435                         GHOST_TButtonMask gbmask = GHOST_kButtonMaskLeft;
436
437                         switch (xbe.button) {
438                                 case Button1 : gbmask = GHOST_kButtonMaskLeft; break;
439                                 case Button3 : gbmask = GHOST_kButtonMaskRight; break;
440                                 default:
441                                 case Button2 : gbmask = GHOST_kButtonMaskMiddle; break;
442                         }
443                         
444                         GHOST_TEventType type = (xbe.type == ButtonPress) ? 
445                                 GHOST_kEventButtonDown : GHOST_kEventButtonUp;
446                         
447                         g_event = new
448                         GHOST_EventButton(
449                                 getMilliSeconds(),
450                                 type,
451                                 window,
452                                 gbmask
453                         );
454                         break;
455                 }
456                         
457                         // change of size, border, layer etc.
458                 case ConfigureNotify:
459                 {
460                         /* XConfigureEvent & xce = xe->xconfigure; */
461
462                         g_event = new 
463                         GHOST_Event(
464                                 getMilliSeconds(),
465                                 GHOST_kEventWindowSize,
466                                 window
467                         );                      
468                         break;
469                 }
470
471                 case FocusIn:
472                 case FocusOut:
473                 {
474                         XFocusChangeEvent &xfe = xe->xfocus;
475                 
476                         // May have to look at the type of event and filter some
477                         // out.
478                                                                         
479                         GHOST_TEventType gtype = (xfe.type == FocusIn) ? 
480                                 GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate;
481
482                         g_event = new 
483                         GHOST_Event(    
484                                 getMilliSeconds(),
485                                 gtype,
486                                 window
487                         );
488                         break;
489
490                 }
491                 case ClientMessage:
492                 {
493                         XClientMessageEvent & xcme = xe->xclient;
494
495 #ifndef __sgi                   
496                         if (xcme.data.l[0] == m_delete_window_atom) {
497                                 g_event = new 
498                                 GHOST_Event(    
499                                         getMilliSeconds(),
500                                         GHOST_kEventWindowClose,
501                                         window
502                                 );
503                         } else {
504                                 /* Unknown client message, ignore */
505                         }
506 #endif
507                         break;
508                 }
509                         
510                 // We're not interested in the following things.(yet...)
511                 case NoExpose : 
512                 case GraphicsExpose :
513                 
514                 case EnterNotify:
515                 case LeaveNotify:
516                         // XCrossingEvents pointer leave enter window.
517                         break;
518                 case MapNotify:
519                         /*
520                          * From ICCCM:
521                          * [ Clients can select for StructureNotify on their
522                          *   top-level windows to track transition between
523                          *   Normal and Iconic states. Receipt of a MapNotify
524                          *   event will indicate a transition to the Normal
525                          *   state, and receipt of an UnmapNotify event will
526                          *   indicate a transition to the Iconic state. ]
527                          */
528                         if (window->m_post_init == True) {
529                                 /*
530                                  * Now we are sure that the window is
531                                  * mapped, so only need change the state.
532                                  */
533                                 window->setState (window->m_post_state);
534                                 window->m_post_init = False;
535                         }
536                         break;
537                 case UnmapNotify:
538                         break;
539                 case MappingNotify:
540                 case ReparentNotify:
541                         break;
542
543         default: {
544                         if(xe->type == window->GetXTablet().MotionEvent) 
545                         {
546                                 XDeviceMotionEvent* data = (XDeviceMotionEvent*)xe;
547                                 window->GetXTablet().CommonData.Pressure= 
548                                         data->axis_data[2]/((float)window->GetXTablet().PressureLevels);
549                         
550                         /* the (short) cast and the &0xffff is bizarre and unexplained anywhere,
551                          * but I got garbage data without it. Found it in the xidump.c source --matt */
552                                 window->GetXTablet().CommonData.Xtilt= 
553                                         (short)(data->axis_data[3]&0xffff)/((float)window->GetXTablet().XtiltLevels);
554                                 window->GetXTablet().CommonData.Ytilt= 
555                                         (short)(data->axis_data[4]&0xffff)/((float)window->GetXTablet().YtiltLevels);
556                         }
557                         else if(xe->type == window->GetXTablet().ProxInEvent) 
558                         {
559                                 XProximityNotifyEvent* data = (XProximityNotifyEvent*)xe;
560                                 if(data->deviceid == window->GetXTablet().StylusID)
561                                         window->GetXTablet().CommonData.Active= 1;
562                                 else if(data->deviceid == window->GetXTablet().EraserID)
563                                         window->GetXTablet().CommonData.Active= 2;
564                         }
565                         else if(xe->type == window->GetXTablet().ProxOutEvent)
566                                 window->GetXTablet().CommonData.Active= 0;
567
568                         break;
569                 }
570         }
571
572         if (g_event) {
573                 pushEvent(g_event);
574         }
575 }
576
577
578         GHOST_TSuccess 
579 GHOST_SystemX11::
580 getModifierKeys(
581         GHOST_ModifierKeys& keys
582 ) const {
583
584         // analyse the masks retuned from XQueryPointer.
585
586         memset(m_keyboard_vector,0,sizeof(m_keyboard_vector));
587
588         XQueryKeymap(m_display,m_keyboard_vector);
589
590         // now translate key symobols into keycodes and
591         // test with vector.
592
593         const KeyCode shift_l = XKeysymToKeycode(m_display,XK_Shift_L);
594         const KeyCode shift_r = XKeysymToKeycode(m_display,XK_Shift_R);
595         const KeyCode control_l = XKeysymToKeycode(m_display,XK_Control_L);
596         const KeyCode control_r = XKeysymToKeycode(m_display,XK_Control_R);
597         const KeyCode alt_l = XKeysymToKeycode(m_display,XK_Alt_L);
598         const KeyCode alt_r = XKeysymToKeycode(m_display,XK_Alt_R);
599
600         // Shift
601         if ((m_keyboard_vector[shift_l >> 3] >> (shift_l & 7)) & 1) {
602                 keys.set(GHOST_kModifierKeyLeftShift,true);
603         } else {
604                 keys.set(GHOST_kModifierKeyLeftShift,false);
605         }
606         if ((m_keyboard_vector[shift_r >> 3] >> (shift_r & 7)) & 1) {
607
608                 keys.set(GHOST_kModifierKeyRightShift,true);
609         } else {
610                 keys.set(GHOST_kModifierKeyRightShift,false);
611         }
612
613         // control (weep)
614         if ((m_keyboard_vector[control_l >> 3] >> (control_l & 7)) & 1) {
615                 keys.set(GHOST_kModifierKeyLeftControl,true);
616         } else {
617                 keys.set(GHOST_kModifierKeyLeftControl,false);
618         }
619         if ((m_keyboard_vector[control_r >> 3] >> (control_r & 7)) & 1) {
620                 keys.set(GHOST_kModifierKeyRightControl,true);
621         } else {
622                 keys.set(GHOST_kModifierKeyRightControl,false);
623         }
624
625         // Alt (yawn)
626         if ((m_keyboard_vector[alt_l >> 3] >> (alt_l & 7)) & 1) {
627                 keys.set(GHOST_kModifierKeyLeftAlt,true);
628         } else {
629                 keys.set(GHOST_kModifierKeyLeftAlt,false);
630         }       
631         if ((m_keyboard_vector[alt_r >> 3] >> (alt_r & 7)) & 1) {
632                 keys.set(GHOST_kModifierKeyRightAlt,true);
633         } else {
634                 keys.set(GHOST_kModifierKeyRightAlt,false);
635         }
636         return GHOST_kSuccess;
637 }
638
639         GHOST_TSuccess 
640 GHOST_SystemX11::
641 getButtons(
642         GHOST_Buttons& buttons
643 ) const {
644
645         Window root_return, child_return;
646         int rx,ry,wx,wy;
647         unsigned int mask_return;
648
649         if (XQueryPointer(
650                 m_display,
651                 RootWindow(m_display,DefaultScreen(m_display)),
652                 &root_return,
653                 &child_return,
654                 &rx,&ry,
655                 &wx,&wy,
656                 &mask_return
657         ) == False) {
658                 return GHOST_kFailure;
659         } else {
660
661                 if (mask_return & Button1Mask) {
662                         buttons.set(GHOST_kButtonMaskLeft,true);
663                 } else {
664                         buttons.set(GHOST_kButtonMaskLeft,false);
665                 }
666
667                 if (mask_return & Button2Mask) {
668                         buttons.set(GHOST_kButtonMaskMiddle,true);
669                 } else {
670                         buttons.set(GHOST_kButtonMaskMiddle,false);
671                 }
672
673                 if (mask_return & Button3Mask) {
674                         buttons.set(GHOST_kButtonMaskRight,true);
675                 } else {
676                         buttons.set(GHOST_kButtonMaskRight,false);
677                 }
678         }       
679
680         return GHOST_kSuccess;
681 }
682
683
684         GHOST_TSuccess 
685 GHOST_SystemX11::
686 getCursorPosition(
687         GHOST_TInt32& x,
688         GHOST_TInt32& y
689 ) const {
690
691         Window root_return, child_return;
692         int rx,ry,wx,wy;
693         unsigned int mask_return;
694
695         if (XQueryPointer(
696                 m_display,
697                 RootWindow(m_display,DefaultScreen(m_display)),
698                 &root_return,
699                 &child_return,
700                 &rx,&ry,
701                 &wx,&wy,
702                 &mask_return
703         ) == False) {
704                 return GHOST_kFailure;
705         } else {
706                 x = rx;
707                 y = ry;
708         }       
709         return GHOST_kSuccess;
710 }
711
712
713         GHOST_TSuccess 
714 GHOST_SystemX11::
715 setCursorPosition(
716         GHOST_TInt32 x,
717         GHOST_TInt32 y
718 ) const {
719
720         // This is a brute force move in screen coordinates
721         // XWarpPointer does relative moves so first determine the
722         // current pointer position.
723
724         int cx,cy;
725         if (getCursorPosition(cx,cy) == GHOST_kFailure) {
726                 return GHOST_kFailure;
727         }
728
729         int relx = x-cx;
730         int rely = y-cy;
731
732         XWarpPointer(m_display,None,None,0,0,0,0,relx,rely);
733         XFlush(m_display);
734         
735         return GHOST_kSuccess;
736 }
737
738
739         void
740 GHOST_SystemX11::
741 addDirtyWindow(
742         GHOST_WindowX11 * bad_wind
743 ){
744
745         GHOST_ASSERT((bad_wind != NULL), "addDirtyWindow() NULL ptr trapped (window)");
746         
747         m_dirty_windows.push_back(bad_wind);
748 }
749
750
751         bool
752 GHOST_SystemX11::
753 generateWindowExposeEvents(
754 ){
755
756         vector<GHOST_WindowX11 *>::iterator w_start = m_dirty_windows.begin();
757         vector<GHOST_WindowX11 *>::const_iterator w_end = m_dirty_windows.end();
758         bool anyProcessed = false;
759         
760         for (;w_start != w_end; ++w_start) {
761                 GHOST_Event * g_event = new 
762                         GHOST_Event(
763                                 getMilliSeconds(),
764                                 GHOST_kEventWindowUpdate,
765                                 *w_start
766                         );                      
767
768                 (*w_start)->validate(); 
769                 
770                 if (g_event) {
771                         pushEvent(g_event);
772                         anyProcessed = true;
773                 }
774         }
775
776         m_dirty_windows.clear();
777         return anyProcessed;
778 }
779
780 #define GXMAP(k,x,y) case x: k = y; break; 
781
782         GHOST_TKey
783 GHOST_SystemX11::
784 convertXKey(
785         KeySym key
786 ){
787         GHOST_TKey type;
788
789         if ((key >= XK_A) && (key <= XK_Z)) {
790                 type = GHOST_TKey( key - XK_A + int(GHOST_kKeyA));
791         } else if ((key >= XK_a) && (key <= XK_z)) {
792                 type = GHOST_TKey(key - XK_a + int(GHOST_kKeyA));
793         } else if ((key >= XK_0) && (key <= XK_9)) {
794                 type = GHOST_TKey(key - XK_0 + int(GHOST_kKey0));
795         } else if ((key >= XK_F1) && (key <= XK_F24)) {
796                 type = GHOST_TKey(key - XK_F1 + int(GHOST_kKeyF1));
797 #if defined(__sun) || defined(__sun__) 
798                 /* This is a bit of a hack, but it looks like sun
799                    Used F11 and friends for its special keys Stop,again etc..
800                    So this little patch enables F11 and F12 to work as expected
801                    following link has documentation on it: 
802                    http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4734408
803                    also from /usr/include/X11/Sunkeysym.h 
804 #define SunXK_F36               0x1005FF10      // Labeled F11
805 #define SunXK_F37               0x1005FF11      // Labeled F12 
806
807                                 mein@cs.umn.edu
808                  */
809                 
810         } else if (key == 268828432) {
811                 type = GHOST_kKeyF11;
812         } else if (key == 268828433) {
813                 type = GHOST_kKeyF12;
814 #endif
815         } else {
816                 switch(key) {
817                         GXMAP(type,XK_BackSpace,        GHOST_kKeyBackSpace);
818                         GXMAP(type,XK_Tab,              GHOST_kKeyTab);
819                         GXMAP(type,XK_Return,           GHOST_kKeyEnter);
820                         GXMAP(type,XK_Escape,           GHOST_kKeyEsc);
821                         GXMAP(type,XK_space,            GHOST_kKeySpace);
822                         
823                         GXMAP(type,XK_Linefeed,         GHOST_kKeyLinefeed);
824                         GXMAP(type,XK_semicolon,        GHOST_kKeySemicolon);
825                         GXMAP(type,XK_period,           GHOST_kKeyPeriod);
826                         GXMAP(type,XK_comma,            GHOST_kKeyComma);
827                         GXMAP(type,XK_quoteright,       GHOST_kKeyQuote);
828                         GXMAP(type,XK_quoteleft,        GHOST_kKeyAccentGrave);
829                         GXMAP(type,XK_minus,            GHOST_kKeyMinus);
830                         GXMAP(type,XK_slash,            GHOST_kKeySlash);
831                         GXMAP(type,XK_backslash,        GHOST_kKeyBackslash);
832                         GXMAP(type,XK_equal,            GHOST_kKeyEqual);
833                         GXMAP(type,XK_bracketleft,      GHOST_kKeyLeftBracket);
834                         GXMAP(type,XK_bracketright,     GHOST_kKeyRightBracket);
835                         GXMAP(type,XK_Pause,            GHOST_kKeyPause);
836                         
837                         GXMAP(type,XK_Shift_L,          GHOST_kKeyLeftShift);
838                         GXMAP(type,XK_Shift_R,          GHOST_kKeyRightShift);
839                         GXMAP(type,XK_Control_L,        GHOST_kKeyLeftControl);
840                         GXMAP(type,XK_Control_R,        GHOST_kKeyRightControl);
841                         GXMAP(type,XK_Alt_L,            GHOST_kKeyLeftAlt);
842                         GXMAP(type,XK_Alt_R,            GHOST_kKeyRightAlt);
843
844                         GXMAP(type,XK_Insert,           GHOST_kKeyInsert);
845                         GXMAP(type,XK_Delete,           GHOST_kKeyDelete);
846                         GXMAP(type,XK_Home,                     GHOST_kKeyHome);
847                         GXMAP(type,XK_End,                      GHOST_kKeyEnd);
848                         GXMAP(type,XK_Page_Up,          GHOST_kKeyUpPage);
849                         GXMAP(type,XK_Page_Down,        GHOST_kKeyDownPage);
850
851                         GXMAP(type,XK_Left,                     GHOST_kKeyLeftArrow);
852                         GXMAP(type,XK_Right,            GHOST_kKeyRightArrow);
853                         GXMAP(type,XK_Up,                       GHOST_kKeyUpArrow);
854                         GXMAP(type,XK_Down,                     GHOST_kKeyDownArrow);
855
856                         GXMAP(type,XK_Caps_Lock,        GHOST_kKeyCapsLock);
857                         GXMAP(type,XK_Scroll_Lock,      GHOST_kKeyScrollLock);
858                         GXMAP(type,XK_Num_Lock,         GHOST_kKeyNumLock);
859                         
860                                 /* keypad events */
861                                 
862                         GXMAP(type,XK_KP_0,                     GHOST_kKeyNumpad0);
863                         GXMAP(type,XK_KP_1,                     GHOST_kKeyNumpad1);
864                         GXMAP(type,XK_KP_2,                     GHOST_kKeyNumpad2);
865                         GXMAP(type,XK_KP_3,                     GHOST_kKeyNumpad3);
866                         GXMAP(type,XK_KP_4,                     GHOST_kKeyNumpad4);
867                         GXMAP(type,XK_KP_5,                     GHOST_kKeyNumpad5);
868                         GXMAP(type,XK_KP_6,                     GHOST_kKeyNumpad6);
869                         GXMAP(type,XK_KP_7,                     GHOST_kKeyNumpad7);
870                         GXMAP(type,XK_KP_8,                     GHOST_kKeyNumpad8);
871                         GXMAP(type,XK_KP_9,                     GHOST_kKeyNumpad9);
872                         GXMAP(type,XK_KP_Decimal,       GHOST_kKeyNumpadPeriod);
873
874                         GXMAP(type,XK_KP_Insert,        GHOST_kKeyNumpad0);
875                         GXMAP(type,XK_KP_End,           GHOST_kKeyNumpad1);
876                         GXMAP(type,XK_KP_Down,          GHOST_kKeyNumpad2);
877                         GXMAP(type,XK_KP_Page_Down,     GHOST_kKeyNumpad3);
878                         GXMAP(type,XK_KP_Left,          GHOST_kKeyNumpad4);
879                         GXMAP(type,XK_KP_Begin,         GHOST_kKeyNumpad5);
880                         GXMAP(type,XK_KP_Right,         GHOST_kKeyNumpad6);
881                         GXMAP(type,XK_KP_Home,          GHOST_kKeyNumpad7);
882                         GXMAP(type,XK_KP_Up,            GHOST_kKeyNumpad8);
883                         GXMAP(type,XK_KP_Page_Up,       GHOST_kKeyNumpad9);
884                         GXMAP(type,XK_KP_Delete,        GHOST_kKeyNumpadPeriod);
885
886                         GXMAP(type,XK_KP_Enter,         GHOST_kKeyNumpadEnter);
887                         GXMAP(type,XK_KP_Add,           GHOST_kKeyNumpadPlus);
888                         GXMAP(type,XK_KP_Subtract,      GHOST_kKeyNumpadMinus);
889                         GXMAP(type,XK_KP_Multiply,      GHOST_kKeyNumpadAsterisk);
890                         GXMAP(type,XK_KP_Divide,        GHOST_kKeyNumpadSlash);
891
892                                 /* some extra sun cruft (NICE KEYBOARD!) */
893 #ifdef __sun__
894                         GXMAP(type,0xffde,                      GHOST_kKeyNumpad1);
895                         GXMAP(type,0xffe0,                      GHOST_kKeyNumpad3);
896                         GXMAP(type,0xffdc,                      GHOST_kKeyNumpad5);
897                         GXMAP(type,0xffd8,                      GHOST_kKeyNumpad7);
898                         GXMAP(type,0xffda,                      GHOST_kKeyNumpad9);
899
900                         GXMAP(type,0xffd6,                      GHOST_kKeyNumpadSlash);
901                         GXMAP(type,0xffd7,                      GHOST_kKeyNumpadAsterisk);
902 #endif
903
904                         default :
905                                 type = GHOST_kKeyUnknown;
906                                 break;
907                 }
908         }
909
910         return type;
911 }
912
913 #undef GXMAP