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