e31ee1a027e5ba23f0e031ffd2b2aec5181a6ff5
[freeglut] / src / freeglut_main.c
1 /*
2  * freeglut_main.c
3  *
4  * The windows message processing methods.
5  *
6  * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7  * Written by Pawel W. Olszta, <olszta@sourceforge.net>
8  * Creation date: Fri Dec 3 1999
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  */
27
28 #include <GL/freeglut.h>
29 #include "freeglut_internal.h"
30 #ifdef HAVE_ERRNO_H
31 #    include <errno.h>
32 #endif
33 #include <stdarg.h>
34 #ifdef  HAVE_VFPRINTF
35 #    define VFPRINTF(s,f,a) vfprintf((s),(f),(a))
36 #elif defined(HAVE__DOPRNT)
37 #    define VFPRINTF(s,f,a) _doprnt((f),(a),(s))
38 #else
39 #    define VFPRINTF(s,f,a)
40 #endif
41
42 #ifdef _WIN32_WCE
43
44 typedef struct GXDisplayProperties GXDisplayProperties;
45 typedef struct GXKeyList GXKeyList;
46 #include <gx.h>
47
48 typedef struct GXKeyList (*GXGETDEFAULTKEYS)(int);
49 typedef int (*GXOPENINPUT)();
50
51 GXGETDEFAULTKEYS GXGetDefaultKeys_ = NULL;
52 GXOPENINPUT GXOpenInput_ = NULL;
53
54 struct GXKeyList gxKeyList;
55
56 #endif /* _WIN32_WCE */
57
58 /*
59  * Try to get the maximum value allowed for ints, falling back to the minimum
60  * guaranteed by ISO C99 if there is no suitable header.
61  */
62 #ifdef HAVE_LIMITS_H
63 #    include <limits.h>
64 #endif
65 #ifndef INT_MAX
66 #    define INT_MAX 32767
67 #endif
68
69 #ifndef MIN
70 #    define MIN(a,b) (((a)<(b)) ? (a) : (b))
71 #endif
72
73
74 /*
75  * TODO BEFORE THE STABLE RELEASE:
76  *
77  * There are some issues concerning window redrawing under X11, and maybe
78  * some events are not handled. The Win32 version lacks some more features,
79  * but seems acceptable for not demanding purposes.
80  *
81  * Need to investigate why the X11 version breaks out with an error when
82  * closing a window (using the window manager, not glutDestroyWindow)...
83  */
84
85 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
86
87 /*
88  * Handle a window configuration change. When no reshape
89  * callback is hooked, the viewport size is updated to
90  * match the new window size.
91  */
92 static void fghReshapeWindow ( SFG_Window *window, int width, int height )
93 {
94     SFG_Window *current_window = fgStructure.CurrentWindow;
95
96     freeglut_return_if_fail( window != NULL );
97
98
99 #if TARGET_HOST_POSIX_X11
100
101     XResizeWindow( fgDisplay.Display, window->Window.Handle,
102                    width, height );
103     XFlush( fgDisplay.Display ); /* XXX Shouldn't need this */
104
105 #elif TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
106     {
107         RECT winRect;
108         int x, y, w, h;
109
110         /*
111          * For windowed mode, get the current position of the
112          * window and resize taking the size of the frame
113          * decorations into account.
114          */
115
116         /* "GetWindowRect" returns the pixel coordinates of the outside of the window */
117         GetWindowRect( window->Window.Handle, &winRect );
118         x = winRect.left;
119         y = winRect.top;
120         w = width;
121         h = height;
122
123         if ( window->Parent == NULL )
124         {
125            if ( ! window->IsMenu && (window != fgStructure.GameModeWindow) &&
126                !( fgState.DisplayMode & GLUT_BORDERLESS ))
127             {
128                 w += GetSystemMetrics( SM_CXSIZEFRAME ) * 2;
129                 h += GetSystemMetrics( SM_CYSIZEFRAME ) * 2 +
130                      GetSystemMetrics( SM_CYCAPTION );
131             }
132         }
133         else
134         {
135             RECT parentRect;
136             GetWindowRect( window->Parent->Window.Handle, &parentRect );
137             x -= parentRect.left + GetSystemMetrics( SM_CXSIZEFRAME ) * 2;
138             y -= parentRect.top  + GetSystemMetrics( SM_CYSIZEFRAME ) * 2 +
139                                    GetSystemMetrics( SM_CYCAPTION );
140         }
141
142         /*
143          * SWP_NOACTIVATE      Do not activate the window
144          * SWP_NOOWNERZORDER   Do not change position in z-order
145          * SWP_NOSENDCHANGING  Supress WM_WINDOWPOSCHANGING message
146          * SWP_NOZORDER        Retains the current Z order (ignore 2nd param)
147          */
148
149         SetWindowPos( window->Window.Handle,
150                       HWND_TOP,
151                       x, y, w, h,
152                       SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING |
153                       SWP_NOZORDER
154         );
155     }
156 #endif
157
158     /*
159      * XXX Should update {window->State.OldWidth, window->State.OldHeight}
160      * XXX to keep in lockstep with POSIX_X11 code.
161      */
162     if( FETCH_WCB( *window, Reshape ) )
163         INVOKE_WCB( *window, Reshape, ( width, height ) );
164     else
165     {
166         fgSetWindow( window );
167         glViewport( 0, 0, width, height );
168     }
169
170     /*
171      * Force a window redraw.  In Windows at least this is only a partial
172      * solution:  if the window is increasing in size in either dimension,
173      * the already-drawn part does not get drawn again and things look funny.
174      * But without this we get this bad behaviour whenever we resize the
175      * window.
176      */
177     window->State.Redisplay = GL_TRUE;
178
179     if( window->IsMenu )
180         fgSetWindow( current_window );
181 }
182
183 /*
184  * Calls a window's redraw method. This is used when
185  * a redraw is forced by the incoming window messages.
186  */
187 static void fghRedrawWindow ( SFG_Window *window )
188 {
189     SFG_Window *current_window = fgStructure.CurrentWindow;
190
191     freeglut_return_if_fail( window );
192     freeglut_return_if_fail( FETCH_WCB ( *window, Display ) );
193
194     window->State.Redisplay = GL_FALSE;
195
196     freeglut_return_if_fail( window->State.Visible );
197
198     fgSetWindow( window );
199
200     if( window->State.NeedToResize )
201     {
202         fghReshapeWindow(
203             window,
204             window->State.Width,
205             window->State.Height
206         );
207
208         window->State.NeedToResize = GL_FALSE;
209     }
210
211     INVOKE_WCB( *window, Display, ( ) );
212
213     fgSetWindow( current_window );
214 }
215
216 /*
217  * A static helper function to execute display callback for a window
218  */
219 static void fghcbDisplayWindow( SFG_Window *window,
220                                 SFG_Enumerator *enumerator )
221 {
222     if( window->State.Redisplay &&
223         window->State.Visible )
224     {
225         window->State.Redisplay = GL_FALSE;
226
227 #if TARGET_HOST_POSIX_X11
228         fghRedrawWindow ( window ) ;
229 #elif TARGET_HOST_MS_WINDOWS
230         RedrawWindow(
231             window->Window.Handle, NULL, NULL,
232             RDW_NOERASE | RDW_INTERNALPAINT | RDW_INVALIDATE | RDW_UPDATENOW
233         );
234 #endif
235     }
236
237     fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
238 }
239
240 /*
241  * Make all windows perform a display call
242  */
243 static void fghDisplayAll( void )
244 {
245     SFG_Enumerator enumerator;
246
247     enumerator.found = GL_FALSE;
248     enumerator.data  =  NULL;
249
250     fgEnumWindows( fghcbDisplayWindow, &enumerator );
251 }
252
253 /*
254  * Window enumerator callback to check for the joystick polling code
255  */
256 static void fghcbCheckJoystickPolls( SFG_Window *window,
257                                      SFG_Enumerator *enumerator )
258 {
259     long int checkTime = fgElapsedTime( );
260
261     if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=
262         checkTime )
263     {
264 #if !defined(_WIN32_WCE)
265         fgJoystickPollWindow( window );
266 #endif /* !defined(_WIN32_WCE) */
267         window->State.JoystickLastPoll = checkTime;
268     }
269
270     fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
271 }
272
273 /*
274  * Check all windows for joystick polling
275  */
276 static void fghCheckJoystickPolls( void )
277 {
278     SFG_Enumerator enumerator;
279
280     enumerator.found = GL_FALSE;
281     enumerator.data  =  NULL;
282
283     fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
284 }
285
286 /*
287  * Check the global timers
288  */
289 static void fghCheckTimers( void )
290 {
291     long checkTime = fgElapsedTime( );
292
293     while( fgState.Timers.First )
294     {
295         SFG_Timer *timer = fgState.Timers.First;
296
297         if( timer->TriggerTime > checkTime )
298             break;
299
300         fgListRemove( &fgState.Timers, &timer->Node );
301         fgListAppend( &fgState.FreeTimers, &timer->Node );
302
303         timer->Callback( timer->ID );
304     }
305 }
306
307  
308 /* Platform-dependent time in milliseconds, as an unsigned 32-bit integer.
309  * This value wraps every 49.7 days, but integer overflows cancel
310  * when subtracting an initial start time, unless the total time exceeds
311  * 32-bit, where the GLUT API return value is also overflowed.
312  */  
313 unsigned long fgSystemTime(void) {
314 #if TARGET_HOST_SOLARIS || HAVE_GETTIMEOFDAY
315     struct timeval now;
316     gettimeofday( &now, NULL );
317     return now.tv_usec/1000 + now.tv_sec*1000;
318 #elif TARGET_HOST_MS_WINDOWS
319 #    if defined(_WIN32_WCE)
320     return GetTickCount();
321 #    else
322     return timeGetTime();
323 #    endif
324 #endif
325 }
326   
327 /*
328  * Elapsed Time
329  */
330 long fgElapsedTime( void )
331 {
332     return (long) (fgSystemTime() - fgState.Time);
333 }
334
335 /*
336  * Error Messages.
337  */
338 void fgError( const char *fmt, ... )
339 {
340     va_list ap;
341
342     if (fgState.ErrorFunc) {
343
344         va_start( ap, fmt );
345
346         /* call user set error handler here */
347         fgState.ErrorFunc(fmt, ap);
348
349         va_end( ap );
350
351     } else {
352
353         va_start( ap, fmt );
354
355         fprintf( stderr, "freeglut ");
356         if( fgState.ProgramName )
357             fprintf( stderr, "(%s): ", fgState.ProgramName );
358         VFPRINTF( stderr, fmt, ap );
359         fprintf( stderr, "\n" );
360
361         va_end( ap );
362
363         if ( fgState.Initialised )
364             fgDeinitialize ();
365
366         exit( 1 );
367     }
368 }
369
370 void fgWarning( const char *fmt, ... )
371 {
372     va_list ap;
373
374     if (fgState.WarningFunc) {
375
376         va_start( ap, fmt );
377
378         /* call user set warning handler here */
379         fgState.WarningFunc(fmt, ap);
380
381         va_end( ap );
382
383     } else {
384
385         va_start( ap, fmt );
386
387         fprintf( stderr, "freeglut ");
388         if( fgState.ProgramName )
389             fprintf( stderr, "(%s): ", fgState.ProgramName );
390         VFPRINTF( stderr, fmt, ap );
391         fprintf( stderr, "\n" );
392
393         va_end( ap );
394     }
395 }
396
397
398 /*
399  * Indicates whether Joystick events are being used by ANY window.
400  *
401  * The current mechanism is to walk all of the windows and ask if
402  * there is a joystick callback.  We have a short-circuit early
403  * return if we find any joystick handler registered.
404  *
405  * The real way to do this is to make use of the glutTimer() API
406  * to more cleanly re-implement the joystick API.  Then, this code
407  * and all other "joystick timer" code can be yanked.
408  *
409  */
410 static void fghCheckJoystickCallback( SFG_Window* w, SFG_Enumerator* e)
411 {
412     if( FETCH_WCB( *w, Joystick ) )
413     {
414         e->found = GL_TRUE;
415         e->data = w;
416     }
417     fgEnumSubWindows( w, fghCheckJoystickCallback, e );
418 }
419 static int fghHaveJoystick( void )
420 {
421     SFG_Enumerator enumerator;
422
423     enumerator.found = GL_FALSE;
424     enumerator.data = NULL;
425     fgEnumWindows( fghCheckJoystickCallback, &enumerator );
426     return !!enumerator.data;
427 }
428 static void fghHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)
429 {
430     if( w->State.Redisplay && w->State.Visible )
431     {
432         e->found = GL_TRUE;
433         e->data = w;
434     }
435     fgEnumSubWindows( w, fghHavePendingRedisplaysCallback, e );
436 }
437 static int fghHavePendingRedisplays (void)
438 {
439     SFG_Enumerator enumerator;
440
441     enumerator.found = GL_FALSE;
442     enumerator.data = NULL;
443     fgEnumWindows( fghHavePendingRedisplaysCallback, &enumerator );
444     return !!enumerator.data;
445 }
446 /*
447  * Returns the number of GLUT ticks (milliseconds) till the next timer event.
448  */
449 static long fghNextTimer( void )
450 {
451     long ret = INT_MAX;
452     SFG_Timer *timer = fgState.Timers.First;
453
454     if( timer )
455         ret = timer->TriggerTime - fgElapsedTime();
456     if( ret < 0 )
457         ret = 0;
458
459     return ret;
460 }
461 /*
462  * Does the magic required to relinquish the CPU until something interesting
463  * happens.
464  */
465 static void fghSleepForEvents( void )
466 {
467     long msec;
468
469     if( fgState.IdleCallback || fghHavePendingRedisplays( ) )
470         return;
471
472     msec = fghNextTimer( );
473     /* XXX Use GLUT timers for joysticks... */
474     /* XXX Dumb; forces granularity to .01sec */
475     if( fghHaveJoystick( ) && ( msec > 10 ) )     
476         msec = 10;
477
478 #if TARGET_HOST_POSIX_X11
479     /*
480      * Possibly due to aggressive use of XFlush() and friends,
481      * it is possible to have our socket drained but still have
482      * unprocessed events.  (Or, this may just be normal with
483      * X, anyway?)  We do non-trivial processing of X events
484      * after the event-reading loop, in any case, so we
485      * need to allow that we may have an empty socket but non-
486      * empty event queue.
487      */
488     if( ! XPending( fgDisplay.Display ) )
489     {
490         fd_set fdset;
491         int err;
492         int socket;
493         struct timeval wait;
494
495         socket = ConnectionNumber( fgDisplay.Display );
496         FD_ZERO( &fdset );
497         FD_SET( socket, &fdset );
498         wait.tv_sec = msec / 1000;
499         wait.tv_usec = (msec % 1000) * 1000;
500         err = select( socket+1, &fdset, NULL, NULL, &wait );
501
502 #ifdef HAVE_ERRNO_H
503         if( ( -1 == err ) && ( errno != EINTR ) )
504             fgWarning ( "freeglut select() error: %d", errno );
505 #endif
506     }
507 #elif TARGET_HOST_MS_WINDOWS
508     MsgWaitForMultipleObjects( 0, NULL, FALSE, msec, QS_ALLINPUT );
509 #endif
510 }
511
512 #if TARGET_HOST_POSIX_X11
513 /*
514  * Returns GLUT modifier mask for the state field of an X11 event.
515  */
516 int fghGetXModifiers( int state )
517 {
518     int ret = 0;
519
520     if( state & ( ShiftMask | LockMask ) )
521         ret |= GLUT_ACTIVE_SHIFT;
522     if( state & ControlMask )
523         ret |= GLUT_ACTIVE_CTRL;
524     if( state & Mod1Mask )
525         ret |= GLUT_ACTIVE_ALT;
526
527     return ret;
528 }
529 #endif
530
531
532 #if TARGET_HOST_POSIX_X11 && _DEBUG
533
534 static const char* fghTypeToString( int type )
535 {
536     switch( type ) {
537     case KeyPress: return "KeyPress";
538     case KeyRelease: return "KeyRelease";
539     case ButtonPress: return "ButtonPress";
540     case ButtonRelease: return "ButtonRelease";
541     case MotionNotify: return "MotionNotify";
542     case EnterNotify: return "EnterNotify";
543     case LeaveNotify: return "LeaveNotify";
544     case FocusIn: return "FocusIn";
545     case FocusOut: return "FocusOut";
546     case KeymapNotify: return "KeymapNotify";
547     case Expose: return "Expose";
548     case GraphicsExpose: return "GraphicsExpose";
549     case NoExpose: return "NoExpose";
550     case VisibilityNotify: return "VisibilityNotify";
551     case CreateNotify: return "CreateNotify";
552     case DestroyNotify: return "DestroyNotify";
553     case UnmapNotify: return "UnmapNotify";
554     case MapNotify: return "MapNotify";
555     case MapRequest: return "MapRequest";
556     case ReparentNotify: return "ReparentNotify";
557     case ConfigureNotify: return "ConfigureNotify";
558     case ConfigureRequest: return "ConfigureRequest";
559     case GravityNotify: return "GravityNotify";
560     case ResizeRequest: return "ResizeRequest";
561     case CirculateNotify: return "CirculateNotify";
562     case CirculateRequest: return "CirculateRequest";
563     case PropertyNotify: return "PropertyNotify";
564     case SelectionClear: return "SelectionClear";
565     case SelectionRequest: return "SelectionRequest";
566     case SelectionNotify: return "SelectionNotify";
567     case ColormapNotify: return "ColormapNotify";
568     case ClientMessage: return "ClientMessage";
569     case MappingNotify: return "MappingNotify";
570     default: return "UNKNOWN";
571     }
572 }
573
574 static const char* fghBoolToString( Bool b )
575 {
576     return b == False ? "False" : "True";
577 }
578
579 static const char* fghNotifyHintToString( char is_hint )
580 {
581     switch( is_hint ) {
582     case NotifyNormal: return "NotifyNormal";
583     case NotifyHint: return "NotifyHint";
584     default: return "UNKNOWN";
585     }
586 }
587
588 static const char* fghNotifyModeToString( int mode )
589 {
590     switch( mode ) {
591     case NotifyNormal: return "NotifyNormal";
592     case NotifyGrab: return "NotifyGrab";
593     case NotifyUngrab: return "NotifyUngrab";
594     case NotifyWhileGrabbed: return "NotifyWhileGrabbed";
595     default: return "UNKNOWN";
596     }
597 }
598
599 static const char* fghNotifyDetailToString( int detail )
600 {
601     switch( detail ) {
602     case NotifyAncestor: return "NotifyAncestor";
603     case NotifyVirtual: return "NotifyVirtual";
604     case NotifyInferior: return "NotifyInferior";
605     case NotifyNonlinear: return "NotifyNonlinear";
606     case NotifyNonlinearVirtual: return "NotifyNonlinearVirtual";
607     case NotifyPointer: return "NotifyPointer";
608     case NotifyPointerRoot: return "NotifyPointerRoot";
609     case NotifyDetailNone: return "NotifyDetailNone";
610     default: return "UNKNOWN";
611     }
612 }
613
614 static const char* fghVisibilityToString( int state ) {
615     switch( state ) {
616     case VisibilityUnobscured: return "VisibilityUnobscured";
617     case VisibilityPartiallyObscured: return "VisibilityPartiallyObscured";
618     case VisibilityFullyObscured: return "VisibilityFullyObscured";
619     default: return "UNKNOWN";
620     }
621 }
622
623 static const char* fghConfigureDetailToString( int detail )
624 {
625     switch( detail ) {
626     case Above: return "Above";
627     case Below: return "Below";
628     case TopIf: return "TopIf";
629     case BottomIf: return "BottomIf";
630     case Opposite: return "Opposite";
631     default: return "UNKNOWN";
632     }
633 }
634
635 static const char* fghPlaceToString( int place )
636 {
637     switch( place ) {
638     case PlaceOnTop: return "PlaceOnTop";
639     case PlaceOnBottom: return "PlaceOnBottom";
640     default: return "UNKNOWN";
641     }
642 }
643
644 static const char* fghMappingRequestToString( int request )
645 {
646     switch( request ) {
647     case MappingModifier: return "MappingModifier";
648     case MappingKeyboard: return "MappingKeyboard";
649     case MappingPointer: return "MappingPointer";
650     default: return "UNKNOWN";
651     }
652 }
653
654 static const char* fghPropertyStateToString( int state )
655 {
656     switch( state ) {
657     case PropertyNewValue: return "PropertyNewValue";
658     case PropertyDelete: return "PropertyDelete";
659     default: return "UNKNOWN";
660     }
661 }
662
663 static const char* fghColormapStateToString( int state )
664 {
665     switch( state ) {
666     case ColormapUninstalled: return "ColormapUninstalled";
667     case ColormapInstalled: return "ColormapInstalled";
668     default: return "UNKNOWN";
669     }
670 }
671
672 static void fghPrintEvent( XEvent *event )
673 {
674     switch( event->type ) {
675
676     case KeyPress:
677     case KeyRelease: {
678         XKeyEvent *e = &event->xkey;
679         fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
680                    "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
681                    "keycode=%u, same_screen=%s", fghTypeToString( e->type ),
682                    e->window, e->root, e->subwindow, (unsigned long)e->time,
683                    e->x, e->y, e->x_root, e->y_root, e->state, e->keycode,
684                    fghBoolToString( e->same_screen ) );
685         break;
686     }
687
688     case ButtonPress:
689     case ButtonRelease: {
690         XButtonEvent *e = &event->xbutton;
691         fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
692                    "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
693                    "button=%u, same_screen=%d", fghTypeToString( e->type ),
694                    e->window, e->root, e->subwindow, (unsigned long)e->time,
695                    e->x, e->y, e->x_root, e->y_root, e->state, e->button,
696                    fghBoolToString( e->same_screen ) );
697         break;
698     }
699
700     case MotionNotify: {
701         XMotionEvent *e = &event->xmotion;
702         fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
703                    "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
704                    "is_hint=%s, same_screen=%d", fghTypeToString( e->type ),
705                    e->window, e->root, e->subwindow, (unsigned long)e->time,
706                    e->x, e->y, e->x_root, e->y_root, e->state,
707                    fghNotifyHintToString( e->is_hint ),
708                    fghBoolToString( e->same_screen ) );
709         break;
710     }
711
712     case EnterNotify:
713     case LeaveNotify: {
714         XCrossingEvent *e = &event->xcrossing;
715         fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
716                    "(x,y)=(%d,%d), mode=%s, detail=%s, same_screen=%d, "
717                    "focus=%d, state=0x%x", fghTypeToString( e->type ),
718                    e->window, e->root, e->subwindow, (unsigned long)e->time,
719                    e->x, e->y, fghNotifyModeToString( e->mode ),
720                    fghNotifyDetailToString( e->detail ), (int)e->same_screen,
721                    (int)e->focus, e->state );
722         break;
723     }
724
725     case FocusIn:
726     case FocusOut: {
727         XFocusChangeEvent *e = &event->xfocus;
728         fgWarning( "%s: window=0x%x, mode=%s, detail=%s",
729                    fghTypeToString( e->type ), e->window,
730                    fghNotifyModeToString( e->mode ),
731                    fghNotifyDetailToString( e->detail ) );
732         break;
733     }
734
735     case KeymapNotify: {
736         XKeymapEvent *e = &event->xkeymap;
737         char buf[32 * 2 + 1];
738         int i;
739         for ( i = 0; i < 32; i++ ) {
740             snprintf( &buf[ i * 2 ], sizeof( buf ) - i * 2,
741                       "%02x", e->key_vector[ i ] );
742         }
743         buf[ i ] = '\0';
744         fgWarning( "%s: window=0x%x, %s", fghTypeToString( e->type ), e->window,
745                    buf );
746         break;
747     }
748
749     case Expose: {
750         XExposeEvent *e = &event->xexpose;
751         fgWarning( "%s: window=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), "
752                    "count=%d", fghTypeToString( e->type ), e->window, e->x,
753                    e->y, e->width, e->height, e->count );
754         break;
755     }
756
757     case GraphicsExpose: {
758         XGraphicsExposeEvent *e = &event->xgraphicsexpose;
759         fgWarning( "%s: drawable=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), "
760                    "count=%d, (major_code,minor_code)=(%d,%d)",
761                    fghTypeToString( e->type ), e->drawable, e->x, e->y,
762                    e->width, e->height, e->count, e->major_code,
763                    e->minor_code );
764         break;
765     }
766
767     case NoExpose: {
768         XNoExposeEvent *e = &event->xnoexpose;
769         fgWarning( "%s: drawable=0x%x, (major_code,minor_code)=(%d,%d)",
770                    fghTypeToString( e->type ), e->drawable, e->major_code,
771                    e->minor_code );
772         break;
773     }
774
775     case VisibilityNotify: {
776         XVisibilityEvent *e = &event->xvisibility;
777         fgWarning( "%s: window=0x%x, state=%s", fghTypeToString( e->type ),
778                    e->window, fghVisibilityToString( e->state) );
779         break;
780     }
781
782     case CreateNotify: {
783         XCreateWindowEvent *e = &event->xcreatewindow;
784         fgWarning( "%s: (x,y)=(%d,%d), (width,height)=(%d,%d), border_width=%d, "
785                    "window=0x%x, override_redirect=%s",
786                    fghTypeToString( e->type ), e->x, e->y, e->width, e->height,
787                    e->border_width, e->window,
788                    fghBoolToString( e->override_redirect ) );
789         break;
790     }
791
792     case DestroyNotify: {
793         XDestroyWindowEvent *e = &event->xdestroywindow;
794         fgWarning( "%s: event=0x%x, window=0x%x",
795                    fghTypeToString( e->type ), e->event, e->window );
796         break;
797     }
798
799     case UnmapNotify: {
800         XUnmapEvent *e = &event->xunmap;
801         fgWarning( "%s: event=0x%x, window=0x%x, from_configure=%s",
802                    fghTypeToString( e->type ), e->event, e->window,
803                    fghBoolToString( e->from_configure ) );
804         break;
805     }
806
807     case MapNotify: {
808         XMapEvent *e = &event->xmap;
809         fgWarning( "%s: event=0x%x, window=0x%x, override_redirect=%s",
810                    fghTypeToString( e->type ), e->event, e->window,
811                    fghBoolToString( e->override_redirect ) );
812         break;
813     }
814
815     case MapRequest: {
816         XMapRequestEvent *e = &event->xmaprequest;
817         fgWarning( "%s: parent=0x%x, window=0x%x",
818                    fghTypeToString( event->type ), e->parent, e->window );
819         break;
820     }
821
822     case ReparentNotify: {
823         XReparentEvent *e = &event->xreparent;
824         fgWarning( "%s: event=0x%x, window=0x%x, parent=0x%x, (x,y)=(%d,%d), "
825                    "override_redirect=%s", fghTypeToString( e->type ),
826                    e->event, e->window, e->parent, e->x, e->y,
827                    fghBoolToString( e->override_redirect ) );
828         break;
829     }
830
831     case ConfigureNotify: {
832         XConfigureEvent *e = &event->xconfigure;
833         fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d), "
834                    "(width,height)=(%d,%d), border_width=%d, above=0x%x, "
835                    "override_redirect=%s", fghTypeToString( e->type ), e->event,
836                    e->window, e->x, e->y, e->width, e->height, e->border_width,
837                    e->above, fghBoolToString( e->override_redirect ) );
838         break;
839     }
840
841     case ConfigureRequest: {
842         XConfigureRequestEvent *e = &event->xconfigurerequest;
843         fgWarning( "%s: parent=0x%x, window=0x%x, (x,y)=(%d,%d), "
844                    "(width,height)=(%d,%d), border_width=%d, above=0x%x, "
845                    "detail=%s, value_mask=%lx", fghTypeToString( e->type ),
846                    e->parent, e->window, e->x, e->y, e->width, e->height,
847                    e->border_width, e->above,
848                    fghConfigureDetailToString( e->detail ), e->value_mask );
849         break;
850     }
851
852     case GravityNotify: {
853         XGravityEvent *e = &event->xgravity;
854         fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d)",
855                    fghTypeToString( e->type ), e->event, e->window, e->x, e->y );
856         break;
857     }
858
859     case ResizeRequest: {
860         XResizeRequestEvent *e = &event->xresizerequest;
861         fgWarning( "%s: window=0x%x, (width,height)=(%d,%d)",
862                    fghTypeToString( e->type ), e->window, e->width, e->height );
863         break;
864     }
865
866     case CirculateNotify: {
867         XCirculateEvent *e = &event->xcirculate;
868         fgWarning( "%s: event=0x%x, window=0x%x, place=%s",
869                    fghTypeToString( e->type ), e->event, e->window,
870                    fghPlaceToString( e->place ) );
871         break;
872     }
873
874     case CirculateRequest: {
875         XCirculateRequestEvent *e = &event->xcirculaterequest;
876         fgWarning( "%s: parent=0x%x, window=0x%x, place=%s",
877                    fghTypeToString( e->type ), e->parent, e->window,
878                    fghPlaceToString( e->place ) );
879         break;
880     }
881
882     case PropertyNotify: {
883         XPropertyEvent *e = &event->xproperty;
884         fgWarning( "%s: window=0x%x, atom=%lu, time=%lu, state=%s",
885                    fghTypeToString( e->type ), e->window,
886                    (unsigned long)e->atom, (unsigned long)e->time,
887                    fghPropertyStateToString( e->state ) );
888         break;
889     }
890
891     case SelectionClear: {
892         XSelectionClearEvent *e = &event->xselectionclear;
893         fgWarning( "%s: window=0x%x, selection=%lu, time=%lu",
894                    fghTypeToString( e->type ), e->window,
895                    (unsigned long)e->selection, (unsigned long)e->time );
896         break;
897     }
898
899     case SelectionRequest: {
900         XSelectionRequestEvent *e = &event->xselectionrequest;
901         fgWarning( "%s: owner=0x%x, requestor=0x%x, selection=0x%x, "
902                    "target=0x%x, property=%lu, time=%lu",
903                    fghTypeToString( e->type ), e->owner, e->requestor,
904                    (unsigned long)e->selection, (unsigned long)e->target,
905                    (unsigned long)e->property, (unsigned long)e->time );
906         break;
907     }
908
909     case SelectionNotify: {
910         XSelectionEvent *e = &event->xselection;
911         fgWarning( "%s: requestor=0x%x, selection=0x%x, target=0x%x, "
912                    "property=%lu, time=%lu", fghTypeToString( e->type ),
913                    e->requestor, (unsigned long)e->selection,
914                    (unsigned long)e->target, (unsigned long)e->property,
915                    (unsigned long)e->time );
916         break;
917     }
918
919     case ColormapNotify: {
920         XColormapEvent *e = &event->xcolormap;
921         fgWarning( "%s: window=0x%x, colormap=%lu, new=%s, state=%s",
922                    fghTypeToString( e->type ), e->window,
923                    (unsigned long)e->colormap, fghBoolToString( e->new ),
924                    fghColormapStateToString( e->state ) );
925         break;
926     }
927
928     case ClientMessage: {
929         XClientMessageEvent *e = &event->xclient;
930         char buf[ 61 ];
931         char* p = buf;
932         char* end = buf + sizeof( buf );
933         int i;
934         switch( e->format ) {
935         case 8:
936           for ( i = 0; i < 20; i++, p += 3 ) {
937                 snprintf( p, end - p, " %02x", e->data.b[ i ] );
938             }
939             break;
940         case 16:
941             for ( i = 0; i < 10; i++, p += 5 ) {
942                 snprintf( p, end - p, " %04x", e->data.s[ i ] );
943             }
944             break;
945         case 32:
946             for ( i = 0; i < 5; i++, p += 9 ) {
947                 snprintf( p, end - p, " %08lx", e->data.l[ i ] );
948             }
949             break;
950         }
951         *p = '\0';
952         fgWarning( "%s: window=0x%x, message_type=%lu, format=%d, data=(%s )",
953                    fghTypeToString( e->type ), e->window,
954                    (unsigned long)e->message_type, e->format, buf );
955         break;
956     }
957
958     case MappingNotify: {
959         XMappingEvent *e = &event->xmapping;
960         fgWarning( "%s: window=0x%x, request=%s, first_keycode=%d, count=%d",
961                    fghTypeToString( e->type ), e->window,
962                    fghMappingRequestToString( e->request ), e->first_keycode,
963                    e->count );
964         break;
965     }
966
967     default: {
968         fgWarning( "%s", fghTypeToString( event->type ) );
969         break;
970     }
971     }
972 }
973
974 #endif
975
976 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
977
978 /*
979  * Executes a single iteration in the freeglut processing loop.
980  */
981 void FGAPIENTRY glutMainLoopEvent( void )
982 {
983 #if TARGET_HOST_POSIX_X11
984     SFG_Window* window;
985     XEvent event;
986
987     /* This code was repeated constantly, so here it goes into a definition: */
988 #define GETWINDOW(a)                             \
989     window = fgWindowByHandle( event.a.window ); \
990     if( window == NULL )                         \
991         break;
992
993 #define GETMOUSE(a)                              \
994     window->State.MouseX = event.a.x;            \
995     window->State.MouseY = event.a.y;
996
997     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
998
999     while( XPending( fgDisplay.Display ) )
1000     {
1001         XNextEvent( fgDisplay.Display, &event );
1002 #if _DEBUG
1003         fghPrintEvent( &event );
1004 #endif
1005
1006         switch( event.type )
1007         {
1008         case ClientMessage:
1009             if(fgIsSpaceballXEvent(&event)) {
1010                 fgSpaceballHandleXEvent(&event);
1011                 break;
1012             }
1013             /* Destroy the window when the WM_DELETE_WINDOW message arrives */
1014             if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.DeleteWindow )
1015             {
1016                 GETWINDOW( xclient );
1017
1018                 fgDestroyWindow ( window );
1019
1020                 if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
1021                 {
1022                     fgDeinitialize( );
1023                     exit( 0 );
1024                 }
1025                 else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
1026                     fgState.ExecState = GLUT_EXEC_STATE_STOP;
1027
1028                 return;
1029             }
1030             break;
1031
1032             /*
1033              * CreateNotify causes a configure-event so that sub-windows are
1034              * handled compatibly with GLUT.  Otherwise, your sub-windows
1035              * (in freeglut only) will not get an initial reshape event,
1036              * which can break things.
1037              *
1038              * GLUT presumably does this because it generally tries to treat
1039              * sub-windows the same as windows.
1040              */
1041         case CreateNotify:
1042         case ConfigureNotify:
1043             {
1044                 int width, height;
1045                 if( event.type == CreateNotify ) {
1046                     GETWINDOW( xcreatewindow );
1047                     width = event.xcreatewindow.width;
1048                     height = event.xcreatewindow.height;
1049                 } else {
1050                     GETWINDOW( xconfigure );
1051                     width = event.xconfigure.width;
1052                     height = event.xconfigure.height;
1053                 }
1054
1055                 if( ( width != window->State.OldWidth ) ||
1056                     ( height != window->State.OldHeight ) )
1057                 {
1058                     SFG_Window *current_window = fgStructure.CurrentWindow;
1059
1060                     window->State.OldWidth = width;
1061                     window->State.OldHeight = height;
1062                     if( FETCH_WCB( *window, Reshape ) )
1063                         INVOKE_WCB( *window, Reshape, ( width, height ) );
1064                     else
1065                     {
1066                         fgSetWindow( window );
1067                         glViewport( 0, 0, width, height );
1068                     }
1069                     glutPostRedisplay( );
1070                     if( window->IsMenu )
1071                         fgSetWindow( current_window );
1072                 }
1073             }
1074             break;
1075
1076         case DestroyNotify:
1077             /*
1078              * This is sent to confirm the XDestroyWindow call.
1079              *
1080              * XXX WHY is this commented out?  Should we re-enable it?
1081              */
1082             /* fgAddToWindowDestroyList ( window ); */
1083             break;
1084
1085         case Expose:
1086             /*
1087              * We are too dumb to process partial exposes...
1088              *
1089              * XXX Well, we could do it.  However, it seems to only
1090              * XXX be potentially useful for single-buffered (since
1091              * XXX double-buffered does not respect viewport when we
1092              * XXX do a buffer-swap).
1093              *
1094              */
1095             if( event.xexpose.count == 0 )
1096             {
1097                 GETWINDOW( xexpose );
1098                 window->State.Redisplay = GL_TRUE;
1099             }
1100             break;
1101
1102         case MapNotify:
1103             break;
1104
1105         case UnmapNotify:
1106             /* We get this when iconifying a window. */ 
1107             GETWINDOW( xunmap );
1108             INVOKE_WCB( *window, WindowStatus, ( GLUT_HIDDEN ) );
1109             window->State.Visible = GL_FALSE;
1110             break;
1111
1112         case MappingNotify:
1113             /*
1114              * Have the client's keyboard knowledge updated (xlib.ps,
1115              * page 206, says that's a good thing to do)
1116              */
1117             XRefreshKeyboardMapping( (XMappingEvent *) &event );
1118             break;
1119
1120         case VisibilityNotify:
1121         {
1122             /*
1123              * Sending this event, the X server can notify us that the window
1124              * has just acquired one of the three possible visibility states:
1125              * VisibilityUnobscured, VisibilityPartiallyObscured or
1126              * VisibilityFullyObscured. Note that we DO NOT receive a
1127              * VisibilityNotify event when iconifying a window, we only get an
1128              * UnmapNotify then.
1129              */
1130             GETWINDOW( xvisibility );
1131             switch( event.xvisibility.state )
1132             {
1133             case VisibilityUnobscured:
1134                 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_RETAINED ) );
1135                 window->State.Visible = GL_TRUE;
1136                 break;
1137
1138             case VisibilityPartiallyObscured:
1139                 INVOKE_WCB( *window, WindowStatus,
1140                             ( GLUT_PARTIALLY_RETAINED ) );
1141                 window->State.Visible = GL_TRUE;
1142                 break;
1143
1144             case VisibilityFullyObscured:
1145                 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_COVERED ) );
1146                 window->State.Visible = GL_FALSE;
1147                 break;
1148
1149             default:
1150                 fgWarning( "Unknown X visibility state: %d",
1151                            event.xvisibility.state );
1152                 break;
1153             }
1154         }
1155         break;
1156
1157         case EnterNotify:
1158         case LeaveNotify:
1159             GETWINDOW( xcrossing );
1160             GETMOUSE( xcrossing );
1161             if( ( event.type == LeaveNotify ) && window->IsMenu &&
1162                 window->ActiveMenu && window->ActiveMenu->IsActive )
1163                 fgUpdateMenuHighlight( window->ActiveMenu );
1164
1165             INVOKE_WCB( *window, Entry, ( ( EnterNotify == event.type ) ?
1166                                           GLUT_ENTERED :
1167                                           GLUT_LEFT ) );
1168             break;
1169
1170         case MotionNotify:
1171         {
1172             GETWINDOW( xmotion );
1173             GETMOUSE( xmotion );
1174
1175             if( window->ActiveMenu )
1176             {
1177                 if( window == window->ActiveMenu->ParentWindow )
1178                 {
1179                     window->ActiveMenu->Window->State.MouseX =
1180                         event.xmotion.x_root - window->ActiveMenu->X;
1181                     window->ActiveMenu->Window->State.MouseY =
1182                         event.xmotion.y_root - window->ActiveMenu->Y;
1183                 }
1184
1185                 fgUpdateMenuHighlight( window->ActiveMenu );
1186
1187                 break;
1188             }
1189
1190             /*
1191              * XXX For more than 5 buttons, just check {event.xmotion.state},
1192              * XXX rather than a host of bit-masks?  Or maybe we need to
1193              * XXX track ButtonPress/ButtonRelease events in our own
1194              * XXX bit-mask?
1195              */
1196             fgState.Modifiers = fghGetXModifiers( event.xmotion.state );
1197             if ( event.xmotion.state & ( Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask ) ) {
1198                 INVOKE_WCB( *window, Motion, ( event.xmotion.x,
1199                                                event.xmotion.y ) );
1200             } else {
1201                 INVOKE_WCB( *window, Passive, ( event.xmotion.x,
1202                                                 event.xmotion.y ) );
1203             }
1204             fgState.Modifiers = INVALID_MODIFIERS;
1205         }
1206         break;
1207
1208         case ButtonRelease:
1209         case ButtonPress:
1210         {
1211             GLboolean pressed = GL_TRUE;
1212             int button;
1213
1214             if( event.type == ButtonRelease )
1215                 pressed = GL_FALSE ;
1216
1217             /*
1218              * A mouse button has been pressed or released. Traditionally,
1219              * break if the window was found within the freeglut structures.
1220              */
1221             GETWINDOW( xbutton );
1222             GETMOUSE( xbutton );
1223
1224             /*
1225              * An X button (at least in XFree86) is numbered from 1.
1226              * A GLUT button is numbered from 0.
1227              * Old GLUT passed through buttons other than just the first
1228              * three, though it only gave symbolic names and official
1229              * support to the first three.
1230              */
1231             button = event.xbutton.button - 1;
1232
1233             /*
1234              * Do not execute the application's mouse callback if a menu
1235              * is hooked to this button.  In that case an appropriate
1236              * private call should be generated.
1237              */
1238             if( fgCheckActiveMenu( window, button, pressed,
1239                                    event.xbutton.x_root, event.xbutton.y_root ) )
1240                 break;
1241
1242             /*
1243              * Check if there is a mouse or mouse wheel callback hooked to the
1244              * window
1245              */
1246             if( ! FETCH_WCB( *window, Mouse ) &&
1247                 ! FETCH_WCB( *window, MouseWheel ) )
1248                 break;
1249
1250             fgState.Modifiers = fghGetXModifiers( event.xbutton.state );
1251
1252             /* Finally execute the mouse or mouse wheel callback */
1253             if( ( button < glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS ) ) || ( ! FETCH_WCB( *window, MouseWheel ) ) )
1254                 INVOKE_WCB( *window, Mouse, ( button,
1255                                               pressed ? GLUT_DOWN : GLUT_UP,
1256                                               event.xbutton.x,
1257                                               event.xbutton.y )
1258                 );
1259             else
1260             {
1261                 /*
1262                  * Map 4 and 5 to wheel zero; EVEN to +1, ODD to -1
1263                  *  "  6 and 7 "    "   one; ...
1264                  *
1265                  * XXX This *should* be behind some variables/macros,
1266                  * XXX since the order and numbering isn't certain
1267                  * XXX See XFree86 configuration docs (even back in the
1268                  * XXX 3.x days, and especially with 4.x).
1269                  *
1270                  * XXX Note that {button} has already been decremeted
1271                  * XXX in mapping from X button numbering to GLUT.
1272                  */
1273                 int wheel_number = (button - glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS )) / 2;
1274                 int direction = -1;
1275                 if( button % 2 )
1276                     direction = 1;
1277
1278                 if( pressed )
1279                     INVOKE_WCB( *window, MouseWheel, ( wheel_number,
1280                                                        direction,
1281                                                        event.xbutton.x,
1282                                                        event.xbutton.y )
1283                     );
1284             }
1285             fgState.Modifiers = INVALID_MODIFIERS;
1286         }
1287         break;
1288
1289         case KeyRelease:
1290         case KeyPress:
1291         {
1292             FGCBKeyboard keyboard_cb;
1293             FGCBSpecial special_cb;
1294
1295             GETWINDOW( xkey );
1296             GETMOUSE( xkey );
1297
1298             /* Detect auto repeated keys, if configured globally or per-window */
1299
1300             if ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE )
1301             {
1302                 if (event.type==KeyRelease)
1303                 {
1304                     /*
1305                      * Look at X11 keystate to detect repeat mode.
1306                      * While X11 says the key is actually held down, we'll ignore KeyRelease/KeyPress pairs.
1307                      */
1308
1309                     char keys[32];
1310                     XQueryKeymap( fgDisplay.Display, keys ); /* Look at X11 keystate to detect repeat mode */
1311
1312                     if ( event.xkey.keycode<256 )            /* XQueryKeymap is limited to 256 keycodes    */
1313                     {
1314                         if ( keys[event.xkey.keycode>>3] & (1<<(event.xkey.keycode%8)) )
1315                             window->State.KeyRepeating = GL_TRUE;
1316                         else
1317                             window->State.KeyRepeating = GL_FALSE;
1318                     }
1319                 }
1320             }
1321             else
1322                 window->State.KeyRepeating = GL_FALSE;
1323
1324             /* Cease processing this event if it is auto repeated */
1325
1326             if (window->State.KeyRepeating)
1327             {
1328                 if (event.type == KeyPress) window->State.KeyRepeating = GL_FALSE;
1329                 break;
1330             }
1331
1332             if( event.type == KeyPress )
1333             {
1334                 keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, Keyboard ));
1335                 special_cb  = (FGCBSpecial) ( FETCH_WCB( *window, Special  ));
1336             }
1337             else
1338             {
1339                 keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, KeyboardUp ));
1340                 special_cb  = (FGCBSpecial) ( FETCH_WCB( *window, SpecialUp  ));
1341             }
1342
1343             /* Is there a keyboard/special callback hooked for this window? */
1344             if( keyboard_cb || special_cb )
1345             {
1346                 XComposeStatus composeStatus;
1347                 char asciiCode[ 32 ];
1348                 KeySym keySym;
1349                 int len;
1350
1351                 /* Check for the ASCII/KeySym codes associated with the event: */
1352                 len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode),
1353                                      &keySym, &composeStatus
1354                 );
1355
1356                 /* GLUT API tells us to have two separate callbacks... */
1357                 if( len > 0 )
1358                 {
1359                     /* ...one for the ASCII translateable keypresses... */
1360                     if( keyboard_cb )
1361                     {
1362                         fgSetWindow( window );
1363                         fgState.Modifiers = fghGetXModifiers( event.xkey.state );
1364                         keyboard_cb( asciiCode[ 0 ],
1365                                      event.xkey.x, event.xkey.y
1366                         );
1367                         fgState.Modifiers = INVALID_MODIFIERS;
1368                     }
1369                 }
1370                 else
1371                 {
1372                     int special = -1;
1373
1374                     /*
1375                      * ...and one for all the others, which need to be
1376                      * translated to GLUT_KEY_Xs...
1377                      */
1378                     switch( keySym )
1379                     {
1380                     case XK_F1:     special = GLUT_KEY_F1;     break;
1381                     case XK_F2:     special = GLUT_KEY_F2;     break;
1382                     case XK_F3:     special = GLUT_KEY_F3;     break;
1383                     case XK_F4:     special = GLUT_KEY_F4;     break;
1384                     case XK_F5:     special = GLUT_KEY_F5;     break;
1385                     case XK_F6:     special = GLUT_KEY_F6;     break;
1386                     case XK_F7:     special = GLUT_KEY_F7;     break;
1387                     case XK_F8:     special = GLUT_KEY_F8;     break;
1388                     case XK_F9:     special = GLUT_KEY_F9;     break;
1389                     case XK_F10:    special = GLUT_KEY_F10;    break;
1390                     case XK_F11:    special = GLUT_KEY_F11;    break;
1391                     case XK_F12:    special = GLUT_KEY_F12;    break;
1392
1393                     case XK_KP_Left:
1394                     case XK_Left:   special = GLUT_KEY_LEFT;   break;
1395                     case XK_KP_Right:
1396                     case XK_Right:  special = GLUT_KEY_RIGHT;  break;
1397                     case XK_KP_Up:
1398                     case XK_Up:     special = GLUT_KEY_UP;     break;
1399                     case XK_KP_Down:
1400                     case XK_Down:   special = GLUT_KEY_DOWN;   break;
1401
1402                     case XK_KP_Prior:
1403                     case XK_Prior:  special = GLUT_KEY_PAGE_UP; break;
1404                     case XK_KP_Next:
1405                     case XK_Next:   special = GLUT_KEY_PAGE_DOWN; break;
1406                     case XK_KP_Home:
1407                     case XK_Home:   special = GLUT_KEY_HOME;   break;
1408                     case XK_KP_End:
1409                     case XK_End:    special = GLUT_KEY_END;    break;
1410                     case XK_KP_Insert:
1411                     case XK_Insert: special = GLUT_KEY_INSERT; break;
1412
1413                     case XK_Num_Lock :  special = GLUT_KEY_NUM_LOCK;  break;
1414                     case XK_KP_Begin :  special = GLUT_KEY_BEGIN;     break;
1415                     case XK_KP_Delete:  special = GLUT_KEY_DELETE;    break;
1416
1417                     case XK_Shift_L:   special = GLUT_KEY_SHIFT_L;    break;
1418                     case XK_Shift_R:   special = GLUT_KEY_SHIFT_R;    break;
1419                     case XK_Control_L: special = GLUT_KEY_CTRL_L;     break;
1420                     case XK_Control_R: special = GLUT_KEY_CTRL_R;     break;
1421                     case XK_Alt_L:     special = GLUT_KEY_ALT_L;      break;
1422                     case XK_Alt_R:     special = GLUT_KEY_ALT_R;      break;
1423                     }
1424
1425                     /*
1426                      * Execute the callback (if one has been specified),
1427                      * given that the special code seems to be valid...
1428                      */
1429                     if( special_cb && (special != -1) )
1430                     {
1431                         fgSetWindow( window );
1432                         fgState.Modifiers = fghGetXModifiers( event.xkey.state );
1433                         special_cb( special, event.xkey.x, event.xkey.y );
1434                         fgState.Modifiers = INVALID_MODIFIERS;
1435                     }
1436                 }
1437             }
1438         }
1439         break;
1440
1441         case ReparentNotify:
1442             break; /* XXX Should disable this event */
1443
1444         /* Not handled */
1445         case GravityNotify:
1446             break;
1447
1448         default:
1449             /* enter handling of Extension Events here */
1450             #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
1451                 fgHandleExtensionEvents( &event );
1452             #endif
1453             break;
1454         }
1455     }
1456
1457 #elif TARGET_HOST_MS_WINDOWS
1458
1459     MSG stMsg;
1460
1461     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
1462
1463     while( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
1464     {
1465         if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
1466         {
1467             if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
1468             {
1469                 fgDeinitialize( );
1470                 exit( 0 );
1471             }
1472             else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
1473                 fgState.ExecState = GLUT_EXEC_STATE_STOP;
1474
1475             return;
1476         }
1477
1478         TranslateMessage( &stMsg );
1479         DispatchMessage( &stMsg );
1480     }
1481 #endif
1482
1483     if( fgState.Timers.First )
1484         fghCheckTimers( );
1485     fghCheckJoystickPolls( );
1486     fghDisplayAll( );
1487
1488     fgCloseWindows( );
1489 }
1490
1491 /*
1492  * Enters the freeglut processing loop.
1493  * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
1494  */
1495 void FGAPIENTRY glutMainLoop( void )
1496 {
1497     int action;
1498
1499 #if TARGET_HOST_MS_WINDOWS
1500     SFG_Window *window = (SFG_Window *)fgStructure.Windows.First ;
1501 #endif
1502
1503     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );
1504
1505 #if TARGET_HOST_MS_WINDOWS
1506     /*
1507      * Processing before the main loop:  If there is a window which is open and
1508      * which has a visibility callback, call it.  I know this is an ugly hack,
1509      * but I'm not sure what else to do about it.  Ideally we should leave
1510      * something uninitialized in the create window code and initialize it in
1511      * the main loop, and have that initialization create a "WM_ACTIVATE"
1512      * message.  Then we would put the visibility callback code in the
1513      * "case WM_ACTIVATE" block below.         - John Fay -- 10/24/02
1514      */
1515     while( window )
1516     {
1517         if ( FETCH_WCB( *window, Visibility ) )
1518         {
1519             SFG_Window *current_window = fgStructure.CurrentWindow ;
1520
1521             INVOKE_WCB( *window, Visibility, ( window->State.Visible ) );
1522             fgSetWindow( current_window );
1523         }
1524
1525         window = (SFG_Window *)window->Node.Next ;
1526     }
1527 #endif
1528
1529     fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
1530     while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
1531     {
1532         SFG_Window *window;
1533
1534         glutMainLoopEvent( );
1535         /*
1536          * Step through the list of windows, seeing if there are any
1537          * that are not menus
1538          */
1539         for( window = ( SFG_Window * )fgStructure.Windows.First;
1540              window;
1541              window = ( SFG_Window * )window->Node.Next )
1542             if ( ! ( window->IsMenu ) )
1543                 break;
1544
1545         if( ! window )
1546             fgState.ExecState = GLUT_EXEC_STATE_STOP;
1547         else
1548         {
1549             if( fgState.IdleCallback )
1550             {
1551                 if( fgStructure.CurrentWindow &&
1552                     fgStructure.CurrentWindow->IsMenu )
1553                     /* fail safe */
1554                     fgSetWindow( window );
1555                 fgState.IdleCallback( );
1556             }
1557
1558             fghSleepForEvents( );
1559         }
1560     }
1561
1562     /*
1563      * When this loop terminates, destroy the display, state and structure
1564      * of a freeglut session, so that another glutInit() call can happen
1565      *
1566      * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it.
1567      */
1568     action = fgState.ActionOnWindowClose;
1569     fgDeinitialize( );
1570     if( action == GLUT_ACTION_EXIT )
1571         exit( 0 );
1572 }
1573
1574 /*
1575  * Leaves the freeglut processing loop.
1576  */
1577 void FGAPIENTRY glutLeaveMainLoop( void )
1578 {
1579     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" );
1580     fgState.ExecState = GLUT_EXEC_STATE_STOP ;
1581 }
1582
1583
1584 #if TARGET_HOST_MS_WINDOWS
1585 /*
1586  * Determine a GLUT modifer mask based on MS-WINDOWS system info.
1587  */
1588 static int fghGetWin32Modifiers (void)
1589 {
1590     return
1591         ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
1592             ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1593         ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
1594             ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1595         ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
1596             ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1597 }
1598
1599 /*
1600  * The window procedure for handling Win32 events
1601  */
1602 LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
1603                                LPARAM lParam )
1604 {
1605     static unsigned char lControl = 0, rControl = 0, lShift = 0,
1606                          rShift = 0, lAlt = 0, rAlt = 0;
1607
1608     SFG_Window* window;
1609     PAINTSTRUCT ps;
1610     LRESULT lRet = 1;
1611
1612     FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Event Handler" ) ;
1613
1614     window = fgWindowByHandle( hWnd );
1615
1616     if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
1617       return DefWindowProc( hWnd, uMsg, wParam, lParam );
1618
1619     /* printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0,
1620              uMsg, wParam, lParam ); */
1621
1622     if ( window )
1623     {
1624       /* Checking for CTRL, ALT, and SHIFT key positions:  Key Down! */
1625       if ( !lControl && GetAsyncKeyState ( VK_LCONTROL ) )
1626       {
1627           INVOKE_WCB    ( *window, Special,
1628                         ( GLUT_KEY_CTRL_L, window->State.MouseX, window->State.MouseY )
1629                       );
1630
1631           lControl = 1;
1632       }
1633
1634       if ( !rControl && GetAsyncKeyState ( VK_RCONTROL ) )
1635       {
1636           INVOKE_WCB ( *window, Special,
1637                        ( GLUT_KEY_CTRL_R, window->State.MouseX, window->State.MouseY )
1638                      );
1639
1640           rControl = 1;
1641       }
1642
1643       if ( !lShift && GetAsyncKeyState ( VK_LSHIFT ) )
1644       {
1645           INVOKE_WCB ( *window, Special,
1646                        ( GLUT_KEY_SHIFT_L, window->State.MouseX, window->State.MouseY )
1647                      );
1648
1649           lShift = 1;
1650       }
1651
1652       if ( !rShift && GetAsyncKeyState ( VK_RSHIFT ) )
1653       {
1654           INVOKE_WCB ( *window, Special,
1655                        ( GLUT_KEY_SHIFT_R, window->State.MouseX, window->State.MouseY )
1656                      );
1657
1658           rShift = 1;
1659       }
1660
1661       if ( !lAlt && GetAsyncKeyState ( VK_LMENU ) )
1662       {
1663           INVOKE_WCB ( *window, Special,
1664                        ( GLUT_KEY_ALT_L, window->State.MouseX, window->State.MouseY )
1665                      );
1666
1667           lAlt = 1;
1668       }
1669
1670       if ( !rAlt && GetAsyncKeyState ( VK_RMENU ) )
1671       {
1672           INVOKE_WCB ( *window, Special,
1673                        ( GLUT_KEY_ALT_R, window->State.MouseX, window->State.MouseY )
1674                      );
1675
1676           rAlt = 1;
1677       }
1678
1679       /* Checking for CTRL, ALT, and SHIFT key positions:  Key Up! */
1680       if ( lControl && !GetAsyncKeyState ( VK_LCONTROL ) )
1681       {
1682           INVOKE_WCB ( *window, SpecialUp,
1683                        ( GLUT_KEY_CTRL_L, window->State.MouseX, window->State.MouseY )
1684                      );
1685
1686           lControl = 0;
1687       }
1688
1689       if ( rControl && !GetAsyncKeyState ( VK_RCONTROL ) )
1690       {
1691           INVOKE_WCB ( *window, SpecialUp,
1692                        ( GLUT_KEY_CTRL_R, window->State.MouseX, window->State.MouseY )
1693                      );
1694
1695           rControl = 0;
1696       }
1697
1698       if ( lShift && !GetAsyncKeyState ( VK_LSHIFT ) )
1699       {
1700           INVOKE_WCB ( *window, SpecialUp,
1701                        ( GLUT_KEY_SHIFT_L, window->State.MouseX, window->State.MouseY )
1702                      );
1703
1704           lShift = 0;
1705       }
1706
1707       if ( rShift && !GetAsyncKeyState ( VK_RSHIFT ) )
1708       {
1709           INVOKE_WCB ( *window, SpecialUp,
1710                        ( GLUT_KEY_SHIFT_R, window->State.MouseX, window->State.MouseY )
1711                      );
1712
1713           rShift = 0;
1714       }
1715
1716       if ( lAlt && !GetAsyncKeyState ( VK_LMENU ) )
1717       {
1718           INVOKE_WCB ( *window, SpecialUp,
1719                        ( GLUT_KEY_ALT_L, window->State.MouseX, window->State.MouseY )
1720                      );
1721
1722           lAlt = 0;
1723       }
1724
1725       if ( rAlt && !GetAsyncKeyState ( VK_RMENU ) )
1726       {
1727           INVOKE_WCB ( *window, SpecialUp,
1728                        ( GLUT_KEY_ALT_R, window->State.MouseX, window->State.MouseY )
1729                      );
1730
1731           rAlt = 0;
1732       }
1733     }
1734
1735     switch( uMsg )
1736     {
1737     case WM_CREATE:
1738         /* The window structure is passed as the creation structure paramter... */
1739         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
1740         FREEGLUT_INTERNAL_ERROR_EXIT ( ( window != NULL ), "Cannot create window",
1741                                        "fgWindowProc" );
1742
1743         window->Window.Handle = hWnd;
1744         window->Window.Device = GetDC( hWnd );
1745         if( window->IsMenu )
1746         {
1747             unsigned int current_DisplayMode = fgState.DisplayMode;
1748             fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH;
1749 #if !defined(_WIN32_WCE)
1750             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
1751 #endif
1752             fgState.DisplayMode = current_DisplayMode;
1753
1754             if( fgStructure.MenuContext )
1755                 wglMakeCurrent( window->Window.Device,
1756                                 fgStructure.MenuContext->MContext
1757                 );
1758             else
1759             {
1760                 fgStructure.MenuContext =
1761                     (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
1762                 fgStructure.MenuContext->MContext =
1763                     wglCreateContext( window->Window.Device );
1764             }
1765
1766             /* window->Window.Context = wglGetCurrentContext ();   */
1767             window->Window.Context = wglCreateContext( window->Window.Device );
1768         }
1769         else
1770         {
1771 #if !defined(_WIN32_WCE)
1772             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
1773 #endif
1774
1775             if( ! fgState.UseCurrentContext )
1776                 window->Window.Context =
1777                     wglCreateContext( window->Window.Device );
1778             else
1779             {
1780                 window->Window.Context = wglGetCurrentContext( );
1781                 if( ! window->Window.Context )
1782                     window->Window.Context =
1783                         wglCreateContext( window->Window.Device );
1784             }
1785
1786 #if !defined(_WIN32_WCE)
1787             fgNewWGLCreateContext( window );
1788 #endif
1789         }
1790
1791         window->State.NeedToResize = GL_TRUE;
1792         if( ( window->State.Width < 0 ) || ( window->State.Height < 0 ) )
1793         {
1794             SFG_Window *current_window = fgStructure.CurrentWindow;
1795
1796             fgSetWindow( window );
1797             window->State.Width = glutGet( GLUT_WINDOW_WIDTH );
1798             window->State.Height = glutGet( GLUT_WINDOW_HEIGHT );
1799             fgSetWindow( current_window );
1800         }
1801
1802         ReleaseDC( window->Window.Handle, window->Window.Device );
1803
1804 #if defined(_WIN32_WCE)
1805         /* Take over button handling */
1806         {
1807             HINSTANCE dxDllLib=LoadLibrary(_T("gx.dll"));
1808             if (dxDllLib)
1809             {
1810                 GXGetDefaultKeys_=(GXGETDEFAULTKEYS)GetProcAddress(dxDllLib, _T("?GXGetDefaultKeys@@YA?AUGXKeyList@@H@Z"));
1811                 GXOpenInput_=(GXOPENINPUT)GetProcAddress(dxDllLib, _T("?GXOpenInput@@YAHXZ"));
1812             }
1813
1814             if(GXOpenInput_)
1815                 (*GXOpenInput_)();
1816             if(GXGetDefaultKeys_)
1817                 gxKeyList = (*GXGetDefaultKeys_)(GX_LANDSCAPEKEYS);
1818         }
1819
1820 #endif /* defined(_WIN32_WCE) */
1821         break;
1822
1823     case WM_SIZE:
1824         /*
1825          * If the window is visible, then it is the user manually resizing it.
1826          * If it is not, then it is the system sending us a dummy resize with
1827          * zero dimensions on a "glutIconifyWindow" call.
1828          */
1829         if( window->State.Visible )
1830         {
1831             window->State.NeedToResize = GL_TRUE;
1832 #if defined(_WIN32_WCE)
1833             window->State.Width  = HIWORD(lParam);
1834             window->State.Height = LOWORD(lParam);
1835 #else
1836             window->State.Width  = LOWORD(lParam);
1837             window->State.Height = HIWORD(lParam);
1838 #endif /* defined(_WIN32_WCE) */
1839         }
1840
1841         break;
1842
1843     case WM_SETFOCUS:
1844 /*        printf("WM_SETFOCUS: %p\n", window ); */
1845         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1846         INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
1847         break;
1848
1849     case WM_KILLFOCUS:
1850 /*        printf("WM_KILLFOCUS: %p\n", window ); */
1851         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1852         INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
1853
1854         if( window->IsMenu &&
1855             window->ActiveMenu && window->ActiveMenu->IsActive )
1856             fgUpdateMenuHighlight( window->ActiveMenu );
1857
1858         break;
1859
1860 #if 0
1861     case WM_ACTIVATE:
1862         if (LOWORD(wParam) != WA_INACTIVE)
1863         {
1864 /*            printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
1865                    window->State.Cursor ); */
1866             fgSetCursor( window, window->State.Cursor );
1867         }
1868
1869         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1870         break;
1871 #endif
1872
1873     case WM_SETCURSOR:
1874 /*      printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
1875         if( LOWORD( lParam ) == HTCLIENT )
1876             fgSetCursor ( window, window->State.Cursor ) ;
1877         else
1878             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1879         break;
1880
1881     case WM_SHOWWINDOW:
1882         window->State.Visible = GL_TRUE;
1883         window->State.Redisplay = GL_TRUE;
1884         break;
1885
1886     case WM_PAINT:
1887         /* Turn on the visibility in case it was turned off somehow */
1888         window->State.Visible = GL_TRUE;
1889         BeginPaint( hWnd, &ps );
1890         fghRedrawWindow( window );
1891         EndPaint( hWnd, &ps );
1892         break;
1893
1894     case WM_CLOSE:
1895         fgDestroyWindow ( window );
1896         if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
1897             PostQuitMessage(0);
1898         break;
1899
1900     case WM_DESTROY:
1901         /*
1902          * The window already got destroyed, so don't bother with it.
1903          */
1904         return 0;
1905
1906     case WM_MOUSEMOVE:
1907     {
1908 #if defined(_WIN32_WCE)
1909         window->State.MouseX = 320-HIWORD( lParam );
1910         window->State.MouseY = LOWORD( lParam );
1911 #else
1912         window->State.MouseX = LOWORD( lParam );
1913         window->State.MouseY = HIWORD( lParam );
1914 #endif /* defined(_WIN32_WCE) */
1915         /* Restrict to [-32768, 32767] to match X11 behaviour       */
1916         /* See comment in "freeglut_developer" mailing list 10/4/04 */
1917         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
1918         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
1919
1920         if ( window->ActiveMenu )
1921         {
1922             fgUpdateMenuHighlight( window->ActiveMenu );
1923             break;
1924         }
1925         SetFocus(window->Window.Handle);
1926
1927         fgState.Modifiers = fghGetWin32Modifiers( );
1928
1929         if( ( wParam & MK_LBUTTON ) ||
1930             ( wParam & MK_MBUTTON ) ||
1931             ( wParam & MK_RBUTTON ) )
1932             INVOKE_WCB( *window, Motion, ( window->State.MouseX,
1933                                            window->State.MouseY ) );
1934         else
1935             INVOKE_WCB( *window, Passive, ( window->State.MouseX,
1936                                             window->State.MouseY ) );
1937
1938         fgState.Modifiers = INVALID_MODIFIERS;
1939     }
1940     break;
1941
1942     case WM_LBUTTONDOWN:
1943     case WM_MBUTTONDOWN:
1944     case WM_RBUTTONDOWN:
1945     case WM_LBUTTONUP:
1946     case WM_MBUTTONUP:
1947     case WM_RBUTTONUP:
1948     {
1949         GLboolean pressed = GL_TRUE;
1950         int button;
1951
1952 #if defined(_WIN32_WCE)
1953         window->State.MouseX = 320-HIWORD( lParam );
1954         window->State.MouseY = LOWORD( lParam );
1955 #else
1956         window->State.MouseX = LOWORD( lParam );
1957         window->State.MouseY = HIWORD( lParam );
1958 #endif /* defined(_WIN32_WCE) */
1959
1960         /* Restrict to [-32768, 32767] to match X11 behaviour       */
1961         /* See comment in "freeglut_developer" mailing list 10/4/04 */
1962         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
1963         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
1964
1965         switch( uMsg )
1966         {
1967         case WM_LBUTTONDOWN:
1968             pressed = GL_TRUE;
1969             button = GLUT_LEFT_BUTTON;
1970             break;
1971         case WM_MBUTTONDOWN:
1972             pressed = GL_TRUE;
1973             button = GLUT_MIDDLE_BUTTON;
1974             break;
1975         case WM_RBUTTONDOWN:
1976             pressed = GL_TRUE;
1977             button = GLUT_RIGHT_BUTTON;
1978             break;
1979         case WM_LBUTTONUP:
1980             pressed = GL_FALSE;
1981             button = GLUT_LEFT_BUTTON;
1982             break;
1983         case WM_MBUTTONUP:
1984             pressed = GL_FALSE;
1985             button = GLUT_MIDDLE_BUTTON;
1986             break;
1987         case WM_RBUTTONUP:
1988             pressed = GL_FALSE;
1989             button = GLUT_RIGHT_BUTTON;
1990             break;
1991         default:
1992             pressed = GL_FALSE;
1993             button = -1;
1994             break;
1995         }
1996
1997 #if !defined(_WIN32_WCE)
1998         if( GetSystemMetrics( SM_SWAPBUTTON ) )
1999         {
2000             if( button == GLUT_LEFT_BUTTON )
2001                 button = GLUT_RIGHT_BUTTON;
2002             else
2003                 if( button == GLUT_RIGHT_BUTTON )
2004                     button = GLUT_LEFT_BUTTON;
2005         }
2006 #endif /* !defined(_WIN32_WCE) */
2007
2008         if( button == -1 )
2009             return DefWindowProc( hWnd, uMsg, lParam, wParam );
2010
2011         /*
2012          * Do not execute the application's mouse callback if a menu
2013          * is hooked to this button.  In that case an appropriate
2014          * private call should be generated.
2015          */
2016         if( fgCheckActiveMenu( window, button, pressed,
2017                                window->State.MouseX, window->State.MouseY ) )
2018             break;
2019
2020         /* Set capture so that the window captures all the mouse messages */
2021         /*
2022          * XXX - Multiple button support:  Under X11, the mouse is not released
2023          * XXX - from the window until all buttons have been released, even if the
2024          * XXX - user presses a button in another window.  This will take more
2025          * XXX - code changes than I am up to at the moment (10/5/04).  The present
2026          * XXX - is a 90 percent solution.
2027          */
2028         if ( pressed == GL_TRUE )
2029           SetCapture ( window->Window.Handle ) ;
2030         else
2031           ReleaseCapture () ;
2032
2033         if( ! FETCH_WCB( *window, Mouse ) )
2034             break;
2035
2036         fgSetWindow( window );
2037         fgState.Modifiers = fghGetWin32Modifiers( );
2038
2039         INVOKE_WCB(
2040             *window, Mouse,
2041             ( button,
2042               pressed ? GLUT_DOWN : GLUT_UP,
2043               window->State.MouseX,
2044               window->State.MouseY
2045             )
2046         );
2047
2048         fgState.Modifiers = INVALID_MODIFIERS;
2049     }
2050     break;
2051
2052     case 0x020a:
2053         /* Should be WM_MOUSEWHEEL but my compiler doesn't recognize it */
2054     {
2055         /*
2056          * XXX THIS IS SPECULATIVE -- John Fay, 10/2/03
2057          * XXX Should use WHEEL_DELTA instead of 120
2058          */
2059         int wheel_number = LOWORD( wParam );
2060         short ticks = ( short )HIWORD( wParam ) / 120;
2061         int direction = 1;
2062
2063         if( ticks < 0 )
2064         {
2065             direction = -1;
2066             ticks = -ticks;
2067         }
2068
2069         /*
2070          * The mouse cursor has moved. Remember the new mouse cursor's position
2071          */
2072         /*        window->State.MouseX = LOWORD( lParam ); */
2073         /* Need to adjust by window position, */
2074         /*        window->State.MouseY = HIWORD( lParam ); */
2075         /* change "lParam" to other parameter */
2076
2077         if( ! FETCH_WCB( *window, MouseWheel ) &&
2078             ! FETCH_WCB( *window, Mouse ) )
2079             break;
2080
2081         fgSetWindow( window );
2082         fgState.Modifiers = fghGetWin32Modifiers( );
2083
2084         while( ticks-- )
2085             if( FETCH_WCB( *window, MouseWheel ) )
2086                 INVOKE_WCB( *window, MouseWheel,
2087                             ( wheel_number,
2088                               direction,
2089                               window->State.MouseX,
2090                               window->State.MouseY
2091                             )
2092                 );
2093             else  /* No mouse wheel, call the mouse button callback twice */
2094             {
2095                 /*
2096                  * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
2097                  *  "    "   one                     +1 to 5, -1 to 6, ...
2098                  *
2099                  * XXX The below assumes that you have no more than 3 mouse
2100                  * XXX buttons.  Sorry.
2101                  */
2102                 int button = wheel_number * 2 + 3;
2103                 if( direction < 0 )
2104                     ++button;
2105                 INVOKE_WCB( *window, Mouse,
2106                             ( button, GLUT_DOWN,
2107                               window->State.MouseX, window->State.MouseY )
2108                 );
2109                 INVOKE_WCB( *window, Mouse,
2110                             ( button, GLUT_UP,
2111                               window->State.MouseX, window->State.MouseY )
2112                 );
2113             }
2114
2115         fgState.Modifiers = INVALID_MODIFIERS;
2116     }
2117     break ;
2118
2119     case WM_SYSKEYDOWN:
2120     case WM_KEYDOWN:
2121     {
2122         int keypress = -1;
2123         POINT mouse_pos ;
2124
2125         if( ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) && (HIWORD(lParam) & KF_REPEAT) )
2126             break;
2127
2128         /*
2129          * Remember the current modifiers state. This is done here in order
2130          * to make sure the VK_DELETE keyboard callback is executed properly.
2131          */
2132         fgState.Modifiers = fghGetWin32Modifiers( );
2133
2134         GetCursorPos( &mouse_pos );
2135         ScreenToClient( window->Window.Handle, &mouse_pos );
2136
2137         window->State.MouseX = mouse_pos.x;
2138         window->State.MouseY = mouse_pos.y;
2139
2140         /* Convert the Win32 keystroke codes to GLUTtish way */
2141 #       define KEY(a,b) case a: keypress = b; break;
2142
2143         switch( wParam )
2144         {
2145             KEY( VK_F1,     GLUT_KEY_F1        );
2146             KEY( VK_F2,     GLUT_KEY_F2        );
2147             KEY( VK_F3,     GLUT_KEY_F3        );
2148             KEY( VK_F4,     GLUT_KEY_F4        );
2149             KEY( VK_F5,     GLUT_KEY_F5        );
2150             KEY( VK_F6,     GLUT_KEY_F6        );
2151             KEY( VK_F7,     GLUT_KEY_F7        );
2152             KEY( VK_F8,     GLUT_KEY_F8        );
2153             KEY( VK_F9,     GLUT_KEY_F9        );
2154             KEY( VK_F10,    GLUT_KEY_F10       );
2155             KEY( VK_F11,    GLUT_KEY_F11       );
2156             KEY( VK_F12,    GLUT_KEY_F12       );
2157             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
2158             KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
2159             KEY( VK_HOME,   GLUT_KEY_HOME      );
2160             KEY( VK_END,    GLUT_KEY_END       );
2161             KEY( VK_LEFT,   GLUT_KEY_LEFT      );
2162             KEY( VK_UP,     GLUT_KEY_UP        );
2163             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
2164             KEY( VK_DOWN,   GLUT_KEY_DOWN      );
2165             KEY( VK_INSERT, GLUT_KEY_INSERT    );
2166             KEY( VK_LCONTROL, GLUT_KEY_CTRL_L  );
2167             KEY( VK_RCONTROL, GLUT_KEY_CTRL_R  );
2168             KEY( VK_LSHIFT, GLUT_KEY_SHIFT_L   );
2169             KEY( VK_RSHIFT, GLUT_KEY_SHIFT_R   );
2170             KEY( VK_LMENU,  GLUT_KEY_ALT_L     );
2171             KEY( VK_RMENU,  GLUT_KEY_ALT_R     );
2172
2173         case VK_DELETE:
2174             /* The delete key should be treated as an ASCII keypress: */
2175             INVOKE_WCB( *window, Keyboard,
2176                         ( 127, window->State.MouseX, window->State.MouseY )
2177             );
2178         }
2179
2180 #if defined(_WIN32_WCE)
2181         if(!(lParam & 0x40000000)) /* Prevent auto-repeat */
2182         {
2183             if(wParam==(unsigned)gxKeyList.vkRight)
2184                 keypress = GLUT_KEY_RIGHT;
2185             else if(wParam==(unsigned)gxKeyList.vkLeft)
2186                 keypress = GLUT_KEY_LEFT;
2187             else if(wParam==(unsigned)gxKeyList.vkUp)
2188                 keypress = GLUT_KEY_UP;
2189             else if(wParam==(unsigned)gxKeyList.vkDown)
2190                 keypress = GLUT_KEY_DOWN;
2191             else if(wParam==(unsigned)gxKeyList.vkA)
2192                 keypress = GLUT_KEY_F1;
2193             else if(wParam==(unsigned)gxKeyList.vkB)
2194                 keypress = GLUT_KEY_F2;
2195             else if(wParam==(unsigned)gxKeyList.vkC)
2196                 keypress = GLUT_KEY_F3;
2197             else if(wParam==(unsigned)gxKeyList.vkStart)
2198                 keypress = GLUT_KEY_F4;
2199         }
2200 #endif
2201
2202         if( keypress != -1 )
2203             INVOKE_WCB( *window, Special,
2204                         ( keypress,
2205                           window->State.MouseX, window->State.MouseY )
2206             );
2207
2208         fgState.Modifiers = INVALID_MODIFIERS;
2209     }
2210     break;
2211
2212     case WM_SYSKEYUP:
2213     case WM_KEYUP:
2214     {
2215         int keypress = -1;
2216         POINT mouse_pos;
2217
2218         /*
2219          * Remember the current modifiers state. This is done here in order
2220          * to make sure the VK_DELETE keyboard callback is executed properly.
2221          */
2222         fgState.Modifiers = fghGetWin32Modifiers( );
2223
2224         GetCursorPos( &mouse_pos );
2225         ScreenToClient( window->Window.Handle, &mouse_pos );
2226
2227         window->State.MouseX = mouse_pos.x;
2228         window->State.MouseY = mouse_pos.y;
2229
2230         /*
2231          * Convert the Win32 keystroke codes to GLUTtish way.
2232          * "KEY(a,b)" was defined under "WM_KEYDOWN"
2233          */
2234
2235         switch( wParam )
2236         {
2237             KEY( VK_F1,     GLUT_KEY_F1        );
2238             KEY( VK_F2,     GLUT_KEY_F2        );
2239             KEY( VK_F3,     GLUT_KEY_F3        );
2240             KEY( VK_F4,     GLUT_KEY_F4        );
2241             KEY( VK_F5,     GLUT_KEY_F5        );
2242             KEY( VK_F6,     GLUT_KEY_F6        );
2243             KEY( VK_F7,     GLUT_KEY_F7        );
2244             KEY( VK_F8,     GLUT_KEY_F8        );
2245             KEY( VK_F9,     GLUT_KEY_F9        );
2246             KEY( VK_F10,    GLUT_KEY_F10       );
2247             KEY( VK_F11,    GLUT_KEY_F11       );
2248             KEY( VK_F12,    GLUT_KEY_F12       );
2249             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
2250             KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
2251             KEY( VK_HOME,   GLUT_KEY_HOME      );
2252             KEY( VK_END,    GLUT_KEY_END       );
2253             KEY( VK_LEFT,   GLUT_KEY_LEFT      );
2254             KEY( VK_UP,     GLUT_KEY_UP        );
2255             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
2256             KEY( VK_DOWN,   GLUT_KEY_DOWN      );
2257             KEY( VK_INSERT, GLUT_KEY_INSERT    );
2258             KEY( VK_LCONTROL, GLUT_KEY_CTRL_L  );
2259             KEY( VK_RCONTROL, GLUT_KEY_CTRL_R  );
2260             KEY( VK_LSHIFT, GLUT_KEY_SHIFT_L   );
2261             KEY( VK_RSHIFT, GLUT_KEY_SHIFT_R   );
2262             KEY( VK_LMENU,  GLUT_KEY_ALT_L     );
2263             KEY( VK_RMENU,  GLUT_KEY_ALT_R     );
2264
2265           case VK_DELETE:
2266               /* The delete key should be treated as an ASCII keypress: */
2267               INVOKE_WCB( *window, KeyboardUp,
2268                           ( 127, window->State.MouseX, window->State.MouseY )
2269               );
2270               break;
2271
2272         default:
2273         {
2274 #if !defined(_WIN32_WCE)
2275             BYTE state[ 256 ];
2276             WORD code[ 2 ];
2277
2278             GetKeyboardState( state );
2279
2280             if( ToAscii( (UINT)wParam, 0, state, code, 0 ) == 1 )
2281                 wParam=code[ 0 ];
2282
2283             INVOKE_WCB( *window, KeyboardUp,
2284                         ( (char)wParam,
2285                           window->State.MouseX, window->State.MouseY )
2286             );
2287 #endif /* !defined(_WIN32_WCE) */
2288         }
2289         }
2290
2291         if( keypress != -1 )
2292             INVOKE_WCB( *window, SpecialUp,
2293                         ( keypress,
2294                           window->State.MouseX, window->State.MouseY )
2295             );
2296
2297         fgState.Modifiers = INVALID_MODIFIERS;
2298     }
2299     break;
2300
2301     case WM_SYSCHAR:
2302     case WM_CHAR:
2303     {
2304       if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
2305             break;
2306
2307         fgState.Modifiers = fghGetWin32Modifiers( );
2308         INVOKE_WCB( *window, Keyboard,
2309                     ( (char)wParam,
2310                       window->State.MouseX, window->State.MouseY )
2311         );
2312         fgState.Modifiers = INVALID_MODIFIERS;
2313     }
2314     break;
2315
2316     case WM_CAPTURECHANGED:
2317         /* User has finished resizing the window, force a redraw */
2318         INVOKE_WCB( *window, Display, ( ) );
2319
2320         /*lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); */
2321         break;
2322
2323         /* Other messages that I have seen and which are not handled already */
2324     case WM_SETTEXT:  /* 0x000c */
2325         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2326         /* Pass it on to "DefWindowProc" to set the window text */
2327         break;
2328
2329     case WM_GETTEXT:  /* 0x000d */
2330         /* Ideally we would copy the title of the window into "lParam" */
2331         /* strncpy ( (char *)lParam, "Window Title", wParam );
2332            lRet = ( wParam > 12 ) ? 12 : wParam;  */
2333         /* the number of characters copied */
2334         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2335         break;
2336
2337     case WM_GETTEXTLENGTH:  /* 0x000e */
2338         /* Ideally we would get the length of the title of the window */
2339         lRet = 12;
2340         /* the number of characters in "Window Title\0" (see above) */
2341         break;
2342
2343     case WM_ERASEBKGND:  /* 0x0014 */
2344         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2345         break;
2346
2347 #if !defined(_WIN32_WCE)
2348     case WM_SYNCPAINT:  /* 0x0088 */
2349         /* Another window has moved, need to update this one */
2350         window->State.Redisplay = GL_TRUE;
2351         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2352         /* Help screen says this message must be passed to "DefWindowProc" */
2353         break;
2354
2355     case WM_NCPAINT:  /* 0x0085 */
2356       /* Need to update the border of this window */
2357         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2358         /* Pass it on to "DefWindowProc" to repaint a standard border */
2359         break;
2360
2361     case WM_SYSCOMMAND :  /* 0x0112 */
2362         {
2363           /*
2364            * We have received a system command message.  Try to act on it.
2365            * The commands are passed in through the "wParam" parameter:
2366            * The least significant digit seems to be which edge of the window
2367            * is being used for a resize event:
2368            *     4  3  5
2369            *     1     2
2370            *     7  6  8
2371            * Congratulations and thanks to Richard Rauch for figuring this out..
2372            */
2373             switch ( wParam & 0xfff0 )
2374             {
2375             case SC_SIZE       :
2376                 break ;
2377
2378             case SC_MOVE       :
2379                 break ;
2380
2381             case SC_MINIMIZE   :
2382                 /* User has clicked on the "-" to minimize the window */
2383                 /* Turn off the visibility */
2384                 window->State.Visible = GL_FALSE ;
2385
2386                 break ;
2387
2388             case SC_MAXIMIZE   :
2389                 break ;
2390
2391             case SC_NEXTWINDOW :
2392                 break ;
2393
2394             case SC_PREVWINDOW :
2395                 break ;
2396
2397             case SC_CLOSE      :
2398                 /* Followed very closely by a WM_CLOSE message */
2399                 break ;
2400
2401             case SC_VSCROLL    :
2402                 break ;
2403
2404             case SC_HSCROLL    :
2405                 break ;
2406
2407             case SC_MOUSEMENU  :
2408                 break ;
2409
2410             case SC_KEYMENU    :
2411                 break ;
2412
2413             case SC_ARRANGE    :
2414                 break ;
2415
2416             case SC_RESTORE    :
2417                 break ;
2418
2419             case SC_TASKLIST   :
2420                 break ;
2421
2422             case SC_SCREENSAVE :
2423                 break ;
2424
2425             case SC_HOTKEY     :
2426                 break ;
2427
2428 #if(WINVER >= 0x0400)
2429             case SC_DEFAULT    :
2430                 break ;
2431
2432             case SC_MONITORPOWER    :
2433                 break ;
2434
2435             case SC_CONTEXTHELP    :
2436                 break ;
2437 #endif /* WINVER >= 0x0400 */
2438
2439             default:
2440 #if _DEBUG
2441                 fgWarning( "Unknown wParam type 0x%x", wParam );
2442 #endif
2443                 break;
2444             }
2445         }
2446 #endif /* !defined(_WIN32_WCE) */
2447
2448         /* We need to pass the message on to the operating system as well */
2449         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2450         break;
2451
2452 #ifdef WM_TOUCH
2453         /* handle multi-touch messages */
2454         case WM_TOUCH:
2455         {
2456                 unsigned int numInputs = (unsigned int)wParam;
2457                 unsigned int i = 0;
2458                 TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
2459                 if (GetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
2460                         /* Handle each contact point */
2461                         for (i = 0; i < numInputs; ++i ) {
2462
2463                                 POINT tp;
2464                                 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
2465                                 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
2466                                 ScreenToClient( hWnd, &tp );
2467
2468                                 ti[i].dwID = ti[i].dwID * 2;
2469
2470                                 if (ti[i].dwFlags & TOUCHEVENTF_DOWN) {
2471                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_ENTERED ) );
2472                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) );
2473                                 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
2474                                         INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) );
2475                                 } else if (ti[i].dwFlags & TOUCHEVENTF_UP)   { 
2476                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) );
2477                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_LEFT ) );
2478                                 }
2479                         }
2480                 }
2481                 CloseTouchInputHandle((HTOUCHINPUT)lParam);
2482                 free( (void*)ti );
2483                 lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
2484                 break;
2485         }
2486 #endif
2487     default:
2488         /* Handle unhandled messages */
2489         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2490         break;
2491     }
2492
2493     return lRet;
2494 }
2495 #endif
2496
2497 /*** END OF FILE ***/