Initial revision
[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
64 #include "GHOST_SystemX11.h"
65 #include "GHOST_WindowX11.h"
66 #include "GHOST_WindowManager.h"
67 #include "GHOST_TimerManager.h"
68 #include "GHOST_EventCursor.h"
69 #include "GHOST_EventKey.h"
70 #include "GHOST_EventButton.h"
71 #include "GHOST_DisplayManagerX11.h"
72
73 #include "GHOST_Debug.h"
74
75 #include <X11/Xatom.h>
76 #include <X11/keysym.h>
77
78 // For timing
79
80 #include <sys/time.h>
81 #include <unistd.h>
82
83 #include <vector>
84
85 using namespace std;
86
87 GHOST_SystemX11::
88 GHOST_SystemX11(
89 ) : 
90         GHOST_System(),
91         m_start_time(0)
92 {
93         m_display = XOpenDisplay(NULL);
94         
95         if (!m_display) return;
96         
97         m_delete_window_atom = XInternAtom(m_display, "WM_DELETE_WINDOW", True);
98
99         // compute the initial time
100         timeval tv;
101         if (gettimeofday(&tv,NULL) == -1) {
102                 GHOST_ASSERT(false,"Could not instantiate timer!");
103         }
104
105         m_start_time = GHOST_TUns64(tv.tv_sec*1000 + tv.tv_usec/1000);
106 }
107
108         GHOST_TSuccess 
109 GHOST_SystemX11::
110 init(
111 ){
112         GHOST_TSuccess success = GHOST_System::init();
113
114         if (success) {
115                 m_keyboard_vector = new char[32];
116
117                 m_displayManager = new GHOST_DisplayManagerX11(this);
118
119                 if (m_keyboard_vector && m_displayManager) {
120                         return GHOST_kSuccess;
121                 }
122         }
123
124         return GHOST_kFailure;
125 }
126         
127
128
129         GHOST_TUns64
130 GHOST_SystemX11::
131 getMilliSeconds(
132 ) const {
133         timeval tv;
134         if (gettimeofday(&tv,NULL) == -1) {
135                 GHOST_ASSERT(false,"Could not compute time!");
136         }
137
138         return  GHOST_TUns64(tv.tv_sec*1000 + tv.tv_usec/1000) - m_start_time;
139 }
140         
141         GHOST_TUns8 
142 GHOST_SystemX11::
143 getNumDisplays(
144 ) const {
145         return GHOST_TUns8(1);
146 }
147
148         /**
149          * Returns the dimensions of the main display on this system.
150          * @return The dimension of the main display.
151          */
152         void 
153 GHOST_SystemX11::
154 getMainDisplayDimensions(
155         GHOST_TUns32& width,
156         GHOST_TUns32& height
157 ) const {       
158         if (m_display) {
159                 width  = DisplayWidth(m_display, DefaultScreen(m_display));
160                 height = DisplayHeight(m_display, DefaultScreen(m_display));
161         }
162 }
163
164         /**
165          * Create a new window.
166          * The new window is added to the list of windows managed.
167          * Never explicitly delete the window, use disposeWindow() instead.
168          * @param       title   The name of the window (displayed in the title bar of the window if the OS supports it).
169          * @param       left    The coordinate of the left edge of the window.
170          * @param       top             The coordinate of the top edge of the window.
171          * @param       width   The width the window.
172          * @param       height  The height the window.
173          * @param       state   The state of the window when opened.
174          * @param       type    The type of drawing context installed in this window.
175          * @return      The new window (or 0 if creation failed).
176          */
177         GHOST_IWindow* 
178 GHOST_SystemX11::
179 createWindow(
180         const STR_String& title,
181         GHOST_TInt32 left,
182         GHOST_TInt32 top,
183         GHOST_TUns32 width,
184         GHOST_TUns32 height,
185         GHOST_TWindowState state,
186         GHOST_TDrawingContextType type,
187         bool stereoVisual
188 ){
189         GHOST_WindowX11 * window = 0;
190         
191         if (!m_display) return 0;
192         
193         window = new GHOST_WindowX11 (
194                 this,m_display,title, left, top, width, height, state, type
195         );
196
197         if (window) {
198
199                 // Install a new protocol for this window - so we can overide
200                 // the default window closure mechanism.
201
202                 XSetWMProtocols(m_display, window->getXWindow(), &m_delete_window_atom, 1);
203
204                 if (window->getValid()) {
205                         // Store the pointer to the window 
206                         m_windowManager->addWindow(window);
207                         
208                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
209                 }
210                 else {
211                         delete window;
212                         window = 0;
213                 }
214         }
215         return window;
216
217 }
218
219         GHOST_WindowX11 * 
220 GHOST_SystemX11::
221 findGhostWindow(
222         Window xwind
223 ) const {
224         
225         if (xwind == 0) return NULL;
226
227         // It is not entirely safe to do this as the backptr may point
228         // to a window that has recently been removed. 
229         // We should always check the window manager's list of windows 
230         // and only process events on these windows.
231
232         vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
233
234         vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
235         vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
236         
237         for (; win_it != win_end; ++win_it) {
238                 GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
239                 if (window->getXWindow() == xwind) {
240                         return window;
241                 }
242         }
243         return NULL;
244         
245 }
246
247 static void SleepTillEvent(Display *display, GHOST_TUns64 maxSleep) {
248         int fd = ConnectionNumber(display);
249         fd_set fds;
250         
251         FD_ZERO(&fds);
252         FD_SET(fd, &fds);
253
254         if (maxSleep == -1) {
255             select(fd + 1, &fds, NULL, NULL, NULL);
256         } else {
257                 timeval tv;
258
259                 tv.tv_sec = maxSleep/1000;
260                 tv.tv_usec = (maxSleep - tv.tv_sec*1000)*1000;
261         
262             select(fd + 1, &fds, NULL, NULL, &tv);
263         }
264 }
265
266         bool 
267 GHOST_SystemX11::
268 processEvents(
269         bool waitForEvent
270 ){
271         // Get all the current events -- translate them into 
272         // ghost events and call base class pushEvent() method.
273         
274         bool anyProcessed = false;
275         
276         do {
277                 GHOST_TimerManager* timerMgr = getTimerManager();
278                 
279                 if (waitForEvent && m_dirty_windows.empty() && !XPending(m_display)) {
280                         GHOST_TUns64 next = timerMgr->nextFireTime();
281                         
282                         if (next==GHOST_kFireTimeNever) {
283                                 SleepTillEvent(m_display, -1);
284                         } else {
285                                 SleepTillEvent(m_display, next - getMilliSeconds());
286                         }
287                 }
288                 
289                 if (timerMgr->fireTimers(getMilliSeconds())) {
290                         anyProcessed = true;
291                 }
292                 
293                 while (XPending(m_display)) {
294                         XEvent xevent;
295                         XNextEvent(m_display, &xevent);
296                         processEvent(&xevent);
297                         anyProcessed = true;
298                 }
299                 
300                 if (generateWindowExposeEvents()) {
301                         anyProcessed = true;
302                 }
303         } while (waitForEvent && !anyProcessed);
304         
305         return anyProcessed;
306 }
307
308         void
309 GHOST_SystemX11::
310 processEvent(
311         XEvent *xe
312 ){
313         GHOST_WindowX11 * window = findGhostWindow(xe->xany.window);    
314         GHOST_Event * g_event = NULL;
315
316         if (!window) {
317                 return;
318         }
319                 
320         switch (xe->type) {
321                 case Expose:
322                 {
323                         XExposeEvent & xee = xe->xexpose;
324
325                         if (xee.count == 0) {
326                                 // Only generate a single expose event
327                                 // per read of the event queue.
328
329                                 g_event = new 
330                                 GHOST_Event(
331                                         getMilliSeconds(),
332                                         GHOST_kEventWindowUpdate,
333                                         window
334                                 );                      
335                         }
336                         break;
337                 }
338                 case MotionNotify:
339                 {
340                         XMotionEvent &xme = xe->xmotion;
341                         
342                         g_event = new 
343                         GHOST_EventCursor(
344                                 getMilliSeconds(),
345                                 GHOST_kEventCursorMove,
346                                 window,
347                                 xme.x_root,
348                                 xme.y_root
349                         );
350                         break;
351                 }
352
353                 case KeyPress:
354                 case KeyRelease:
355                 {
356                         XKeyEvent *xke = &(xe->xkey);
357                 
358                         KeySym key_sym = XLookupKeysym(xke,0);
359                         char ascii;
360                         
361                         GHOST_TKey gkey = convertXKey(key_sym);
362                         GHOST_TEventType type = (xke->type == KeyPress) ? 
363                                 GHOST_kEventKeyDown : GHOST_kEventKeyUp;
364                         
365                         if (!XLookupString(xke, &ascii, 1, NULL, NULL)) {
366                                 ascii = '\0';
367                         }
368                         
369                         g_event = new
370                         GHOST_EventKey(
371                                 getMilliSeconds(),
372                                 type,
373                                 window,
374                                 gkey,
375                                 ascii
376                         );
377                         
378                 break;
379                 }
380                 case ButtonPress:
381                 case ButtonRelease:
382                 {
383
384                         XButtonEvent & xbe = xe->xbutton;
385                         GHOST_TButtonMask gbmask = GHOST_kButtonMaskLeft;
386
387                         switch (xbe.button) {
388                                 case Button1 : gbmask = GHOST_kButtonMaskLeft; break;
389                                 case Button3 : gbmask = GHOST_kButtonMaskRight; break;
390                                 default:
391                                 case Button2 : gbmask = GHOST_kButtonMaskMiddle; break;
392                         }
393                         
394                         GHOST_TEventType type = (xbe.type == ButtonPress) ? 
395                                 GHOST_kEventButtonDown : GHOST_kEventButtonUp;
396                         
397                         g_event = new
398                         GHOST_EventButton(
399                                 getMilliSeconds(),
400                                 type,
401                                 window,
402                                 gbmask
403                         );
404                         break;
405                 }
406                         
407                         // change of size, border, layer etc.
408                 case ConfigureNotify:
409                 {
410                         /* XConfigureEvent & xce = xe->xconfigure; */
411
412                         g_event = new 
413                         GHOST_Event(
414                                 getMilliSeconds(),
415                                 GHOST_kEventWindowSize,
416                                 window
417                         );                      
418                         break;
419                 }
420
421                 case FocusIn:
422                 case FocusOut:
423                 {
424                         XFocusChangeEvent &xfe = xe->xfocus;
425                 
426                         // May have to look at the type of event and filter some
427                         // out.
428                                                                         
429                         GHOST_TEventType gtype = (xfe.type == FocusIn) ? 
430                                 GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate;
431
432                         g_event = new 
433                         GHOST_Event(    
434                                 getMilliSeconds(),
435                                 gtype,
436                                 window
437                         );
438                         break;
439
440                 }
441                 case ClientMessage:
442                 {
443                         XClientMessageEvent & xcme = xe->xclient;
444                         
445                         if (xcme.data.l[0] == m_delete_window_atom) {
446                                 g_event = new 
447                                 GHOST_Event(    
448                                         getMilliSeconds(),
449                                         GHOST_kEventWindowClose,
450                                         window
451                                 );
452                         } else {
453                                 /* Unknown client message, ignore */
454                         }
455
456                         break;
457                 }
458                         
459                 // We're not interested in the following things.(yet...)
460                 case NoExpose : 
461                 case GraphicsExpose :
462                 
463                 case EnterNotify:
464                 case LeaveNotify:
465                         // XCrossingEvents pointer leave enter window.
466                         break;
467                 case MapNotify:
468                 case UnmapNotify:
469                         break;
470                 case MappingNotify:
471                 case ReparentNotify:
472                         break;
473
474                 default:
475                         break;
476         }
477
478         if (g_event) {
479                 pushEvent(g_event);
480         }
481 }
482
483
484         GHOST_TSuccess 
485 GHOST_SystemX11::
486 getModifierKeys(
487         GHOST_ModifierKeys& keys
488 ) const {
489
490         // analyse the masks retuned from XQueryPointer.
491
492         memset(m_keyboard_vector,32,0);
493
494         XQueryKeymap(m_display,m_keyboard_vector);
495
496         // now translate key symobols into keycodes and
497         // test with vector.
498
499         const KeyCode shift_l = XKeysymToKeycode(m_display,XK_Shift_L);
500         const KeyCode shift_r = XKeysymToKeycode(m_display,XK_Shift_R);
501         const KeyCode control_l = XKeysymToKeycode(m_display,XK_Control_L);
502         const KeyCode control_r = XKeysymToKeycode(m_display,XK_Control_R);
503         const KeyCode alt_l = XKeysymToKeycode(m_display,XK_Alt_L);
504         const KeyCode alt_r = XKeysymToKeycode(m_display,XK_Alt_R);
505
506         // Shift
507         if ((m_keyboard_vector[shift_l >> 3] >> (shift_l & 7)) & 1) {
508                 keys.set(GHOST_kModifierKeyLeftShift,true);
509         } else {
510                 keys.set(GHOST_kModifierKeyLeftShift,false);
511         }
512         if ((m_keyboard_vector[shift_r >> 3] >> (shift_r & 7)) & 1) {
513
514                 keys.set(GHOST_kModifierKeyRightShift,true);
515         } else {
516                 keys.set(GHOST_kModifierKeyRightShift,false);
517         }
518
519         // control (weep)
520         if ((m_keyboard_vector[control_l >> 3] >> (control_l & 7)) & 1) {
521                 keys.set(GHOST_kModifierKeyLeftControl,true);
522         } else {
523                 keys.set(GHOST_kModifierKeyLeftControl,false);
524         }
525         if ((m_keyboard_vector[control_r >> 3] >> (control_r & 7)) & 1) {
526                 keys.set(GHOST_kModifierKeyRightControl,true);
527         } else {
528                 keys.set(GHOST_kModifierKeyRightControl,false);
529         }
530
531         // Alt (yawn)
532         if ((m_keyboard_vector[alt_l >> 3] >> (alt_l & 7)) & 1) {
533                 keys.set(GHOST_kModifierKeyLeftAlt,true);
534         } else {
535                 keys.set(GHOST_kModifierKeyLeftAlt,false);
536         }       
537         if ((m_keyboard_vector[alt_r >> 3] >> (alt_r & 7)) & 1) {
538                 keys.set(GHOST_kModifierKeyRightAlt,true);
539         } else {
540                 keys.set(GHOST_kModifierKeyRightAlt,false);
541         }
542         return GHOST_kSuccess;
543 }
544
545         GHOST_TSuccess 
546 GHOST_SystemX11::
547 getButtons(
548         GHOST_Buttons& buttons
549 ) const {
550
551         Window root_return, child_return;
552         int rx,ry,wx,wy;
553         unsigned int mask_return;
554
555         if (XQueryPointer(
556                 m_display,
557                 RootWindow(m_display,DefaultScreen(m_display)),
558                 &root_return,
559                 &child_return,
560                 &rx,&ry,
561                 &wx,&wy,
562                 &mask_return
563         ) == False) {
564                 return GHOST_kFailure;
565         } else {
566
567                 if (mask_return & Button1Mask) {
568                         buttons.set(GHOST_kButtonMaskLeft,true);
569                 } else {
570                         buttons.set(GHOST_kButtonMaskLeft,false);
571                 }
572
573                 if (mask_return & Button2Mask) {
574                         buttons.set(GHOST_kButtonMaskMiddle,true);
575                 } else {
576                         buttons.set(GHOST_kButtonMaskMiddle,false);
577                 }
578
579                 if (mask_return & Button3Mask) {
580                         buttons.set(GHOST_kButtonMaskRight,true);
581                 } else {
582                         buttons.set(GHOST_kButtonMaskRight,false);
583                 }
584         }       
585
586         return GHOST_kSuccess;
587 }
588
589
590         GHOST_TSuccess 
591 GHOST_SystemX11::
592 getCursorPosition(
593         GHOST_TInt32& x,
594         GHOST_TInt32& y
595 ) const {
596
597         Window root_return, child_return;
598         int rx,ry,wx,wy;
599         unsigned int mask_return;
600
601         if (XQueryPointer(
602                 m_display,
603                 RootWindow(m_display,DefaultScreen(m_display)),
604                 &root_return,
605                 &child_return,
606                 &rx,&ry,
607                 &wx,&wy,
608                 &mask_return
609         ) == False) {
610                 return GHOST_kFailure;
611         } else {
612                 x = rx;
613                 y = ry;
614         }       
615         return GHOST_kSuccess;
616 }
617
618
619         GHOST_TSuccess 
620 GHOST_SystemX11::
621 setCursorPosition(
622         GHOST_TInt32 x,
623         GHOST_TInt32 y
624 ) const {
625
626         // This is a brute force move in screen coordinates
627         // XWarpPointer does relative moves so first determine the
628         // current pointer position.
629
630         int cx,cy;
631         if (getCursorPosition(cx,cy) == GHOST_kFailure) {
632                 return GHOST_kFailure;
633         }
634
635         int relx = x-cx;
636         int rely = y-cy;
637
638         XWarpPointer(m_display,None,None,0,0,0,0,relx,rely);
639         XFlush(m_display);
640         
641         return GHOST_kSuccess;
642 }
643
644
645         void
646 GHOST_SystemX11::
647 addDirtyWindow(
648         GHOST_WindowX11 * bad_wind
649 ){
650
651         GHOST_ASSERT((bad_wind != NULL), "addDirtyWindow() NULL ptr trapped (window)");
652         
653         m_dirty_windows.push_back(bad_wind);
654 }
655
656
657         bool
658 GHOST_SystemX11::
659 generateWindowExposeEvents(
660 ){
661
662         vector<GHOST_WindowX11 *>::iterator w_start = m_dirty_windows.begin();
663         vector<GHOST_WindowX11 *>::const_iterator w_end = m_dirty_windows.end();
664         bool anyProcessed = false;
665         
666         for (;w_start != w_end; ++w_start) {
667                 GHOST_Event * g_event = new 
668                         GHOST_Event(
669                                 getMilliSeconds(),
670                                 GHOST_kEventWindowUpdate,
671                                 *w_start
672                         );                      
673
674                 (*w_start)->validate(); 
675                 
676                 if (g_event) {
677                         pushEvent(g_event);
678                         anyProcessed = true;
679                 }
680         }
681
682         m_dirty_windows.clear();
683         return anyProcessed;
684 }
685
686 #define GXMAP(k,x,y) case x: k = y; break; 
687
688         GHOST_TKey
689 GHOST_SystemX11::
690 convertXKey(
691         unsigned int key
692 ){
693         GHOST_TKey type;
694
695         if ((key >= XK_A) && (key <= XK_Z)) {
696                 type = GHOST_TKey( key - XK_A + int(GHOST_kKeyA));
697         } else if ((key >= XK_a) && (key <= XK_z)) {
698                 type = GHOST_TKey(key - XK_a + int(GHOST_kKeyA));
699         } else if ((key >= XK_0) && (key <= XK_9)) {
700                 type = GHOST_TKey(key - XK_0 + int(GHOST_kKey0));
701         } else if ((key >= XK_F1) && (key <= XK_F24)) {
702                 type = GHOST_TKey(key - XK_F1 + int(GHOST_kKeyF1));
703         } else {
704                 switch(key) {
705                         GXMAP(type,XK_BackSpace,        GHOST_kKeyBackSpace);
706                         GXMAP(type,XK_Tab,              GHOST_kKeyTab);
707                         GXMAP(type,XK_Return,           GHOST_kKeyEnter);
708                         GXMAP(type,XK_Escape,           GHOST_kKeyEsc);
709                         GXMAP(type,XK_space,            GHOST_kKeySpace);
710                         
711                         GXMAP(type,XK_Linefeed,         GHOST_kKeyLinefeed);
712                         GXMAP(type,XK_semicolon,        GHOST_kKeySemicolon);
713                         GXMAP(type,XK_period,           GHOST_kKeyPeriod);
714                         GXMAP(type,XK_comma,            GHOST_kKeyComma);
715                         GXMAP(type,XK_quoteright,       GHOST_kKeyQuote);
716                         GXMAP(type,XK_quoteleft,        GHOST_kKeyAccentGrave);
717                         GXMAP(type,XK_minus,            GHOST_kKeyMinus);
718                         GXMAP(type,XK_slash,            GHOST_kKeySlash);
719                         GXMAP(type,XK_backslash,        GHOST_kKeyBackslash);
720                         GXMAP(type,XK_equal,            GHOST_kKeyEqual);
721                         GXMAP(type,XK_bracketleft,      GHOST_kKeyLeftBracket);
722                         GXMAP(type,XK_bracketright,     GHOST_kKeyRightBracket);
723                         GXMAP(type,XK_Pause,            GHOST_kKeyPause);
724                         
725                         GXMAP(type,XK_Shift_L,          GHOST_kKeyLeftShift);
726                         GXMAP(type,XK_Shift_R,          GHOST_kKeyRightShift);
727                         GXMAP(type,XK_Control_L,        GHOST_kKeyLeftControl);
728                         GXMAP(type,XK_Control_R,        GHOST_kKeyRightControl);
729                         GXMAP(type,XK_Alt_L,            GHOST_kKeyLeftAlt);
730                         GXMAP(type,XK_Alt_R,            GHOST_kKeyRightAlt);
731
732                         GXMAP(type,XK_Insert,           GHOST_kKeyInsert);
733                         GXMAP(type,XK_Delete,           GHOST_kKeyDelete);
734                         GXMAP(type,XK_Home,                     GHOST_kKeyHome);
735                         GXMAP(type,XK_End,                      GHOST_kKeyEnd);
736                         GXMAP(type,XK_Page_Up,          GHOST_kKeyUpPage);
737                         GXMAP(type,XK_Page_Down,        GHOST_kKeyDownPage);
738
739                         GXMAP(type,XK_Left,                     GHOST_kKeyLeftArrow);
740                         GXMAP(type,XK_Right,            GHOST_kKeyRightArrow);
741                         GXMAP(type,XK_Up,                       GHOST_kKeyUpArrow);
742                         GXMAP(type,XK_Down,                     GHOST_kKeyDownArrow);
743
744                         GXMAP(type,XK_Caps_Lock,        GHOST_kKeyCapsLock);
745                         GXMAP(type,XK_Scroll_Lock,      GHOST_kKeyScrollLock);
746                         GXMAP(type,XK_Num_Lock,         GHOST_kKeyNumLock);
747                         
748                                 /* keypad events */
749                                 
750                         GXMAP(type,XK_KP_0,                     GHOST_kKeyNumpad0);
751                         GXMAP(type,XK_KP_1,                     GHOST_kKeyNumpad1);
752                         GXMAP(type,XK_KP_2,                     GHOST_kKeyNumpad2);
753                         GXMAP(type,XK_KP_3,                     GHOST_kKeyNumpad3);
754                         GXMAP(type,XK_KP_4,                     GHOST_kKeyNumpad4);
755                         GXMAP(type,XK_KP_5,                     GHOST_kKeyNumpad5);
756                         GXMAP(type,XK_KP_6,                     GHOST_kKeyNumpad6);
757                         GXMAP(type,XK_KP_7,                     GHOST_kKeyNumpad7);
758                         GXMAP(type,XK_KP_8,                     GHOST_kKeyNumpad8);
759                         GXMAP(type,XK_KP_9,                     GHOST_kKeyNumpad9);
760                         GXMAP(type,XK_KP_Decimal,       GHOST_kKeyNumpadPeriod);
761
762                         GXMAP(type,XK_KP_Insert,        GHOST_kKeyNumpad0);
763                         GXMAP(type,XK_KP_End,           GHOST_kKeyNumpad1);
764                         GXMAP(type,XK_KP_Down,          GHOST_kKeyNumpad2);
765                         GXMAP(type,XK_KP_Page_Down,     GHOST_kKeyNumpad3);
766                         GXMAP(type,XK_KP_Left,          GHOST_kKeyNumpad4);
767                         GXMAP(type,XK_KP_Begin,         GHOST_kKeyNumpad5);
768                         GXMAP(type,XK_KP_Right,         GHOST_kKeyNumpad6);
769                         GXMAP(type,XK_KP_Home,          GHOST_kKeyNumpad7);
770                         GXMAP(type,XK_KP_Up,            GHOST_kKeyNumpad8);
771                         GXMAP(type,XK_KP_Page_Up,       GHOST_kKeyNumpad9);
772                         GXMAP(type,XK_KP_Delete,        GHOST_kKeyNumpadPeriod);
773
774                         GXMAP(type,XK_KP_Enter,         GHOST_kKeyNumpadEnter);
775                         GXMAP(type,XK_KP_Add,           GHOST_kKeyNumpadPlus);
776                         GXMAP(type,XK_KP_Subtract,      GHOST_kKeyNumpadMinus);
777                         GXMAP(type,XK_KP_Multiply,      GHOST_kKeyNumpadAsterisk);
778                         GXMAP(type,XK_KP_Divide,        GHOST_kKeyNumpadSlash);
779
780                                 /* some extra sun cruft (NICE KEYBOARD!) */
781 #ifdef __sun__
782                         GXMAP(type,0xffde,                      GHOST_kKeyNumpad1);
783                         GXMAP(type,0xffe0,                      GHOST_kKeyNumpad3);
784                         GXMAP(type,0xffdc,                      GHOST_kKeyNumpad5);
785                         GXMAP(type,0xffd8,                      GHOST_kKeyNumpad7);
786                         GXMAP(type,0xffda,                      GHOST_kKeyNumpad9);
787
788                         GXMAP(type,0xffd6,                      GHOST_kKeyNumpadSlash);
789                         GXMAP(type,0xffd7,                      GHOST_kKeyNumpadAsterisk);
790 #endif
791
792                         default :
793                                 type = GHOST_kKeyUnknown;
794                                 break;
795                 }
796         }
797
798         return type;
799 }
800
801 #undef GXMAP
802
803