Made Modifers variable global as per glut classic.
[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 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include "../include/GL/freeglut.h"
33 #include "freeglut_internal.h"
34
35 #include <limits.h>
36 #if TARGET_HOST_UNIX_X11
37 #include <sys/types.h>
38 #include <sys/time.h>
39 #include <unistd.h>
40 #include <errno.h>
41 #include <sys/stat.h>
42 #elif TARGET_HOST_WIN32
43 #endif
44
45 #ifndef MAX
46 #define MAX(a,b) (((a)>(b)) ? (a) : (b))
47 #endif
48
49 #ifndef MIN
50 #define MIN(a,b) (((a)<(b)) ? (a) : (b))
51 #endif
52
53
54 /*
55  * TODO BEFORE THE STABLE RELEASE:
56  *
57  * There are some issues concerning window redrawing under X11, and maybe
58  * some events are not handled. The Win32 version lacks some more features,
59  * but seems acceptable for not demanding purposes.
60  *
61  * Need to investigate why the X11 version breaks out with an error when
62  * closing a window (using the window manager, not glutDestroyWindow)...
63  */
64
65 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
66
67 /*
68  * Calls a window's redraw method. This is used when
69  * a redraw is forced by the incoming window messages.
70  *
71  * XXX We can/should make a "unified" window handle type so that
72  * XXX the function headers don't need this silly #ifdef junk.
73  * XXX Make the type, say, {fgWindow}.  On UNIX_X11, this is
74  * XXX {Window}.  On WIN32, it is {HWND}.  Then do the #ifdef
75  * XXX junk *once* in "freeglut_internal.h".
76  */
77 static void fghRedrawWindowByHandle
78 #if TARGET_HOST_UNIX_X11
79     ( Window handle )
80 #elif TARGET_HOST_WIN32
81     ( HWND handle )
82 #endif
83 {
84     SFG_Window* window = fgWindowByHandle( handle );
85     freeglut_return_if_fail( window != NULL );
86
87     /*
88      * XXX Other than clearing the Redisplay flag or not,
89      * XXX we may as well rely on the INVOK_WCB() doing this
90      * XXX pointer-check.
91      * XXX
92      * XXX If we do not invoke the display because the pointer
93      * XXX is not defined (should never happen, really), then
94      * XXX we may enter an infinite busy-loop trying to update
95      * XXX the window.  Similarly, if we skip because the window
96      * XXX is not visible.  However, if the window becomes visible
97      * XXX at a later time, the window should get its callback
98      * XXX invoked.  I would recommend removing the first check,
99      * XXX and making the second check only affect whether the
100      * XXX callback is invoked---but always clear the flag, if
101      * XXX the {window} pointer is defined.
102      */
103     freeglut_return_if_fail( FETCH_WCB( *window, Display ) );
104     freeglut_return_if_fail( window->State.Visible );
105
106     window->State.Redisplay = GL_FALSE;
107     INVOKE_WCB( *window, Display, ( ) );
108 }
109
110 /*
111  * Handle a window configuration change. When no reshape
112  * callback is hooked, the viewport size is updated to
113  * match the new window size.
114  */
115 static void fghReshapeWindowByHandle
116 #if TARGET_HOST_UNIX_X11
117     ( Window handle, int width, int height )
118 #elif TARGET_HOST_WIN32
119     ( HWND handle, int width, int height )
120 #endif
121 {
122     SFG_Window *current_window = fgStructure.Window;
123
124     SFG_Window* window = fgWindowByHandle( handle );
125     freeglut_return_if_fail( window != NULL );
126
127     if( !( FETCH_WCB( *window, Reshape ) ) )
128     {
129         fgSetWindow( window );
130         glViewport( 0, 0, width, height );
131     }
132     else
133         INVOKE_WCB( *window, Reshape, ( width, height ) );
134
135     /*
136      * Force a window redraw.  In Windows at least this is only a partial
137      * solution:  if the window is increasing in size in either dimension,
138      * the already-drawn part does not get drawn again and things look funny.
139      * But without this we get this bad behaviour whenever we resize the
140      * window.
141      */
142     window->State.Redisplay = GL_TRUE;
143
144     if( window->IsMenu )
145         fgSetWindow( current_window );
146 }
147
148 /*
149  * A static helper function to execute display callback for a window
150  */
151 static void fghcbDisplayWindow( SFG_Window *window, SFG_Enumerator *enumerator )
152 {
153 #if TARGET_HOST_UNIX_X11
154     /*
155      * XXX Do we need/want to check the callback pointer here?
156      * XXX INVOKE_WCB() will check for us.  Arguably, the
157      * XXX Redisplay status flag should be cleared regardless
158      * XXX of any concern but that {window} is a valid pointer
159      * XXX (which this function is assuming anyway).
160      * XXX Especially since old GLUT wouldn't even enter its main
161      * XXX loop if you didn't have a display callback defined...
162      */
163     if( ( FETCH_WCB( *window, Display ) ) &&
164         window->State.Redisplay &&
165         window->State.Visible )
166     {
167         SFG_Window *current_window = fgStructure.Window;
168
169         window->State.Redisplay = GL_FALSE;
170         INVOKE_WCB( *window, Display, ( ) );
171         fgSetWindow( current_window );
172     }
173
174 #elif TARGET_HOST_WIN32
175
176     if( window->State.NeedToResize )
177     {
178         SFG_Window *current_window = fgStructure.Window;
179
180         fgSetWindow( window );
181
182         fghReshapeWindowByHandle( 
183             window->Window.Handle,
184             glutGet( GLUT_WINDOW_WIDTH ),
185             glutGet( GLUT_WINDOW_HEIGHT )
186         );
187
188         window->State.NeedToResize = GL_FALSE;
189         fgSetWindow ( current_window );
190     }
191
192     /*
193      * XXX See above comment about the Redisplay flag...
194      */
195     if( ( FETCH_WCB( *window, Display ) ) &&
196         window->State.Redisplay &&
197         window->State.Visible )
198     {
199         window->State.Redisplay = GL_FALSE;
200
201         RedrawWindow(
202             window->Window.Handle, NULL, NULL, 
203             RDW_NOERASE | RDW_INTERNALPAINT | RDW_INVALIDATE | RDW_UPDATENOW
204         );
205     }
206
207 #endif
208
209     fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
210 }
211
212 /*
213  * Make all windows perform a display call
214  */
215 static void fghDisplayAll( void )
216 {
217     SFG_Enumerator enumerator;
218
219     enumerator.found = GL_FALSE;
220     enumerator.data  =     NULL;
221
222     fgEnumWindows( fghcbDisplayWindow, &enumerator );
223 }
224
225 /*
226  * Window enumerator callback to check for the joystick polling code
227  */
228 static void fghcbCheckJoystickPolls( SFG_Window *window,
229                                      SFG_Enumerator *enumerator )
230 {
231     long int checkTime = fgElapsedTime( );
232
233     if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=
234         checkTime )
235     {
236         fgJoystickPollWindow( window );
237         window->State.JoystickLastPoll = checkTime;
238     }
239
240     fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
241 }
242
243 /*
244  * Check all windows for joystick polling
245  */
246 static void fghCheckJoystickPolls( void )
247 {
248     SFG_Enumerator enumerator;
249
250     enumerator.found = GL_FALSE;
251     enumerator.data  =     NULL;
252
253     fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
254 }
255
256 /*
257  * Check the global timers
258  */
259 static void fghCheckTimers( void )
260 {
261     long checkTime = fgElapsedTime( );
262     SFG_Timer *timer, *next;
263     SFG_List timedOut;
264
265     fgListInit(&timedOut);
266
267     for( timer = (SFG_Timer *)fgState.Timers.First;
268          timer;
269          timer = (SFG_Timer *)next )
270     {
271         next = (SFG_Timer *)timer->Node.Next;
272
273         if( timer->TriggerTime <= checkTime )
274         {
275             fgListRemove( &fgState.Timers, &timer->Node );
276             fgListAppend( &timedOut, &timer->Node );
277         }
278     }
279
280     /*
281      * Now feel free to execute all the hooked and timed out timer callbacks
282      * And delete the timed out timers...
283      */
284     while ( (timer = (SFG_Timer *)timedOut.First) )
285     {
286         if( timer->Callback != NULL )
287             timer->Callback( timer->ID );
288         fgListRemove( &timedOut, &timer->Node );
289         free( timer );
290     }
291 }
292
293
294 /*
295  * Elapsed Time
296  */
297 long fgElapsedTime( void )
298 {
299     if (fgState.Time.Set)
300     {
301 #if TARGET_HOST_UNIX_X11
302         struct timeval now;
303         long elapsed;
304     
305         gettimeofday( &now, NULL );
306     
307         elapsed  = ( now.tv_usec - fgState.Time.Value.tv_usec ) / 1000;
308         elapsed += ( now.tv_sec  - fgState.Time.Value.tv_sec  ) * 1000;
309     
310         return elapsed;
311 #elif TARGET_HOST_WIN32
312         return timeGetTime( ) - fgState.Time.Value;
313 #endif
314     }
315     else
316     {
317 #if TARGET_HOST_UNIX_X11
318         gettimeofday( &fgState.Time.Value, NULL );
319 #elif TARGET_HOST_WIN32
320         fgState.Time.Value = timeGetTime( );
321 #endif
322         fgState.Time.Set = GL_TRUE;
323     }
324 }
325
326 /*
327  * Error Messages.
328  */
329 void fgError( const char *fmt, ... )
330 {
331     va_list ap;
332
333     va_start( ap, fmt );
334
335     fprintf( stderr, "freeglut " );
336     if( fgState.ProgramName )
337         fprintf( stderr, "(%s): ", fgState.ProgramName );
338     vfprintf( stderr, fmt, ap );
339     fprintf( stderr, "\n" );
340
341     va_end( ap );
342
343     if ( fgState.Initalized )
344         fgDeinitialize( );
345
346     exit( 1 );
347 }
348
349 void fgWarning( const char *fmt, ... )
350 {
351     va_list ap;
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
364 /*
365  * Indicates whether Joystick events are being used by ANY window.
366  *
367  * The current mechanism is to walk all of the windows and ask if
368  * there is a joystick callback.  Certainly in some cases, maybe
369  * in all cases, the joystick is attached to the system and accessed
370  * from ONE point by GLUT/freeglut, so this is not the right way,
371  * in general, to do this.  However, the Joystick code is segregated
372  * in its own little world, so we can't access the information that
373  * we need in order to do that nicely.
374  *
375  * Some alternatives:
376  *  * Store Joystick data into freeglut global state.
377  *  * Provide NON-static functions or data from Joystick *.c file.
378  *
379  * Basically, the RIGHT way to do this requires knowing something
380  * about the Joystick.  Right now, the Joystick code is behind
381  * an opaque wall.
382  *
383  */
384 static void fgCheckJoystickCallback( SFG_Window* w, SFG_Enumerator* e )
385 {
386     if( FETCH_WCB( *w, Joystick ) )
387     {
388         e->found = GL_TRUE;
389         e->data = w;
390     }
391     fgEnumSubWindows( w, fgCheckJoystickCallback, e );
392 }
393 static int fgHaveJoystick( void )
394 {
395     SFG_Enumerator enumerator;
396     enumerator.found = GL_FALSE;
397     enumerator.data = NULL;
398     fgEnumWindows( fgCheckJoystickCallback, &enumerator );
399     return !!enumerator.data;
400 }
401 static void fgHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e )
402 {
403     if( w->State.Redisplay )
404     {
405         e->found = GL_TRUE;
406         e->data = w;
407     }
408     fgEnumSubWindows( w, fgHavePendingRedisplaysCallback, e );
409 }        
410 static int fgHavePendingRedisplays( void )
411 {
412     SFG_Enumerator enumerator;
413     enumerator.found = GL_FALSE;
414     enumerator.data = NULL;
415     fgEnumWindows( fgHavePendingRedisplaysCallback, &enumerator );
416     return !!enumerator.data;
417 }
418 /*
419  * Indicates whether there are any outstanding timers.
420  */
421 static int fgHaveTimers( void )
422 {
423     return !!fgState.Timers.First;
424 }
425 /*
426  * Returns the number of GLUT ticks (milliseconds) till the next timer event.
427  */
428 static long fgNextTimer( void )
429 {
430     long now = fgElapsedTime( );
431     long ret = INT_MAX;
432     SFG_Timer *timer;
433
434     for( timer = ( SFG_Timer * )fgState.Timers.First;
435          timer;
436          timer = ( SFG_Timer * )timer->Node.Next )
437         ret = MIN( ret, MAX( 0, timer->TriggerTime - now ) );
438
439     return ret;
440 }
441 /*
442  * Does the magic required to relinquish the CPU until something interesting
443  * happens.
444  */
445 static void fgSleepForEvents( void )
446 {
447 #if TARGET_HOST_UNIX_X11
448     fd_set fdset;
449     int err;
450     int socket;
451     struct timeval wait;
452     long msec;    
453     
454     if( fgState.IdleCallback || fgHavePendingRedisplays( ) )
455         return;
456     socket = ConnectionNumber( fgDisplay.Display );
457     FD_ZERO( &fdset );
458     FD_SET( socket, &fdset );
459     
460     msec = fgNextTimer( );
461     if( fgHaveJoystick( ) )
462         msec = MIN( msec, 10 );
463     
464     wait.tv_sec  =   msec / 1000;
465     wait.tv_usec = ( msec % 1000 ) * 1000;
466     err = select( socket+1, &fdset, NULL, NULL, &wait );
467
468     if( -1 == err )
469         fgWarning( "freeglut select() error: %d\n", errno );
470     
471 #elif TARGET_HOST_WIN32
472 #endif
473 }
474
475 #if TARGET_HOST_UNIX_X11
476 /*
477  * Returns GLUT modifier mask for an XEvent.
478  */
479 int fgGetXModifiers( XEvent *event )
480 {
481     int ret = 0;
482
483     if( event->xkey.state & ( ShiftMask | LockMask ) )
484         ret |= GLUT_ACTIVE_SHIFT;
485     if( event->xkey.state & ControlMask )
486         ret |= GLUT_ACTIVE_CTRL;
487     if( event->xkey.state & Mod1Mask )
488         ret |= GLUT_ACTIVE_ALT;
489     
490     return ret;
491 }
492 #endif
493
494
495 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
496
497 /*
498  * Executes a single iteration in the freeglut processing loop.
499  */
500 void FGAPIENTRY glutMainLoopEvent( void )
501 {
502 #if TARGET_HOST_UNIX_X11
503     SFG_Window* window;
504     XEvent event;
505
506     /*
507      * This code was repeated constantly, so here it goes into a definition:
508      */
509 #define GETWINDOW(a)                             \
510     window = fgWindowByHandle( event.a.window ); \
511     if( window == NULL )                         \
512         break;
513
514 #define GETMOUSE(a)                              \
515     window->State.MouseX = event.a.x;            \
516     window->State.MouseY = event.a.y;
517
518     freeglut_assert_ready;
519
520     while( XPending( fgDisplay.Display ) )
521     {
522         XNextEvent( fgDisplay.Display, &event );
523
524         switch( event.type )
525         {
526         case ClientMessage:
527             /*
528              * Destroy the window when the WM_DELETE_WINDOW message arrives
529              */
530             if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.DeleteWindow )
531             {
532                 GETWINDOW( xclient ); 
533
534                 fgCloseWindow ( window );
535                 fgAddToWindowDestroyList ( window, GL_FALSE );
536             }
537             break;
538
539             /*
540              * CreateNotify causes a configure-event so that sub-windows are
541              * handled compatibly with GLUT.  Otherwise, your sub-windows
542              * (in freeglut only) will not get an initial reshape event,
543              * which can break things.
544              *
545              * XXX NOTE that it is possible that you will more than one Reshape
546              * XXX event for your top-level window, but something like this
547              * XXX appears to be required for compatbility.
548              *
549              * GLUT presumably does this because it generally tries to treat
550              * sub-windows the same as windows.
551              */
552         case CreateNotify:
553         case ConfigureNotify:
554             fghReshapeWindowByHandle(
555                 event.xconfigure.window,
556                 event.xconfigure.width,
557                 event.xconfigure.height
558             );
559             break;
560
561         case DestroyNotify:
562             /*
563              * This is sent to confirm the XDestroyWindow call.
564              * XXX WHY is this commented out?  Should we re-enable it?
565              */
566             /* fgAddToWindowDestroyList ( window, GL_FALSE ); */
567             break;
568
569         case Expose:
570             /*
571              * We are too dumb to process partial exposes...
572              * XXX Well, we could do it.  However, it seems to only
573              * XXX be potentially useful for single-buffered (since
574              * XXX double-buffered does not respect viewport when we
575              * XXX do a buffer-swap).
576              */
577             if( event.xexpose.count == 0 )
578                 fghRedrawWindowByHandle( event.xexpose.window );
579             break;
580
581         case MapNotify:
582         case UnmapNotify:
583             /*
584              * If we never do anything with this, can we just not ask to
585              * get these messages?
586              */
587             break;
588
589         case MappingNotify:
590             /*
591              * Have the client's keyboard knowledge updated (xlib.ps,
592              * page 206, says that's a good thing to do)
593              */
594             XRefreshKeyboardMapping( (XMappingEvent *) &event );
595             break;
596
597         case VisibilityNotify:
598         {
599             GETWINDOW( xvisibility ); 
600             if( ! FETCH_WCB( *window, WindowStatus ) )
601                 break;
602             fgSetWindow( window );
603
604             /*
605              * Sending this event, the X server can notify us that the window
606              * has just acquired one of the three possible visibility states:
607              * VisibilityUnobscured, VisibilityPartiallyObscured or
608              * VisibilityFullyObscured
609              */
610             switch( event.xvisibility.state )
611             {
612             case VisibilityUnobscured:
613                 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_RETAINED ) );
614                 window->State.Visible = GL_TRUE;
615                 break;
616                 
617             case VisibilityPartiallyObscured:
618                 INVOKE_WCB( *window, WindowStatus,
619                             ( GLUT_PARTIALLY_RETAINED ) );
620                 window->State.Visible = GL_TRUE;
621                 break;
622                 
623             case VisibilityFullyObscured:
624                 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_COVERED ) );
625                 window->State.Visible = GL_FALSE;
626                 break;
627
628             default:
629                 fgWarning( "Uknown X visibility state: %d",
630                            event.xvisibility.state );
631                 break;
632             }
633         }
634         break;
635
636         case EnterNotify:
637         case LeaveNotify:
638             GETWINDOW( xcrossing );
639             GETMOUSE( xcrossing );
640             INVOKE_WCB( *window, Entry, ( ( EnterNotify == event.type ) ?
641                                           GLUT_ENTERED :
642                                           GLUT_LEFT ) );
643             break;
644
645         case MotionNotify:
646         {
647             GETWINDOW( xmotion );
648             GETMOUSE( xmotion );
649
650             if( window->ActiveMenu )
651             {
652                 if( window == window->ActiveMenu->ParentWindow )
653                 {
654                     window->ActiveMenu->Window->State.MouseX =
655                         event.xmotion.x_root - window->ActiveMenu->X;
656                     window->ActiveMenu->Window->State.MouseY =
657                         event.xmotion.y_root - window->ActiveMenu->Y;
658                 }
659                 window->ActiveMenu->Window->State.Redisplay = GL_TRUE;
660                 fgSetWindow( window->ActiveMenu->ParentWindow );
661
662                 break;
663             }
664
665             /*
666              * XXX For more than 5 buttons, just check {event.xmotion.state},
667              * XXX rather than a host of bit-masks?
668              */
669 #define BUTTON_MASK \
670   ( Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask )
671             if( event.xmotion.state & BUTTON_MASK )
672                 INVOKE_WCB( *window, Motion, ( event.xmotion.x,
673                                                event.xmotion.y ) );
674             else
675                 INVOKE_WCB( *window, Passive, ( event.xmotion.x,
676                                                 event.xmotion.y ) );
677         }
678         break;
679
680         case ButtonRelease:
681         case ButtonPress:
682         {
683             GLboolean pressed = GL_TRUE;
684             int button;
685
686             if( event.type == ButtonRelease )
687                 pressed = GL_FALSE;
688
689             /*
690              * A mouse button has been pressed or released. Traditionally,
691              * break if the window was found within the freeglut structures.
692              */
693             GETWINDOW( xbutton );
694             GETMOUSE( xbutton );
695           
696             /*
697              * An X button (at least in XFree86) is numbered from 1.
698              * A GLUT button is numbered from 0.
699              * Old GLUT passed through buttons other than just the first
700              * three, though it only gave symbolic names and official
701              * support to the first three.
702              */
703             button = event.xbutton.button - 1;
704
705             /*
706              * XXX This comment is replicated in the WIN32 section and
707              * XXX maybe also in the menu code.  Can we move the info
708              * XXX to one central place and *reference* it from here?
709              *
710              * Do not execute the application's mouse callback if a menu
711              * is hooked to this button.  In that case an appropriate
712              * private call should be generated.
713              * Near as I can tell, this is the menu behaviour:
714              *  - Down-click the menu button, menu not active:  activate
715              *    the menu with its upper left-hand corner at the mouse
716              *    location.
717              *  - Down-click any button outside the menu, menu active:
718              *    deactivate the menu
719              *  - Down-click any button inside the menu, menu active:
720              *    select the menu entry and deactivate the menu
721              *  - Up-click the menu button, menu not active:  nothing happens
722              *  - Up-click the menu button outside the menu, menu active:
723              *    nothing happens
724              *  - Up-click the menu button inside the menu, menu active:
725              *    select the menu entry and deactivate the menu
726              */
727             /* Window has an active menu, it absorbs any mouse click */
728             if( window->ActiveMenu )
729             {
730                 if( window == window->ActiveMenu->ParentWindow )
731                 {
732                     window->ActiveMenu->Window->State.MouseX =
733                         event.xbutton.x_root - window->ActiveMenu->X;
734                     window->ActiveMenu->Window->State.MouseY =
735                         event.xbutton.y_root - window->ActiveMenu->Y;
736                 }
737               
738                 /* In the menu, invoke the callback and deactivate the menu*/
739                 if( fgCheckActiveMenu( window->ActiveMenu->Window,
740                                        window->ActiveMenu ) )
741                 {
742                     /*
743                      * Save the current window and menu and set the current
744                      * window to the window whose menu this is
745                      */
746                     SFG_Window *save_window = fgStructure.Window;
747                     SFG_Menu *save_menu = fgStructure.Menu;
748                     SFG_Window *parent_window =
749                         window->ActiveMenu->ParentWindow;
750                     fgSetWindow( parent_window );
751                     fgStructure.Menu = window->ActiveMenu;
752
753                     /* Execute the menu callback */
754                     fgExecuteMenuCallback( window->ActiveMenu );
755                     fgDeactivateMenu( parent_window );
756
757                     /* Restore the current window and menu */
758                     fgSetWindow( save_window );
759                     fgStructure.Menu = save_menu;
760                 }
761                 else if( pressed )
762                     /*
763                      * Outside the menu, deactivate if it's a downclick
764                      * XXX This isn't enough.  A downclick outside of
765                      * XXX the interior of our freeglut windows should also
766                      * XXX deactivate the menu.  This is more complicated.
767                      */
768                     fgDeactivateMenu( window->ActiveMenu->ParentWindow );
769               
770                 window->State.Redisplay = GL_TRUE;
771                 break;
772             }
773
774             /*
775              * No active menu, let's check whether we need to activate one.
776              */
777             if( ( 0 <= button ) &&
778                 ( FREEGLUT_MAX_MENUS > button ) &&
779                 ( window->Menu[ button ] ) &&
780                 pressed )
781             {
782                 window->State.Redisplay = GL_TRUE;
783                 fgSetWindow( window );
784                 fgActivateMenu( window, button );
785                 break;
786             }
787
788             /*
789              * Check if there is a mouse or mouse wheel callback hooked to the
790              * window
791              */
792             if( ! FETCH_WCB( *window, Mouse ) &&
793                 ! FETCH_WCB( *window, MouseWheel ) )
794                 break;
795
796             /*
797              * XXX Why don't we use {window}?  Other code here does...
798              */
799             fgState.Modifiers = fgGetXModifiers( &event );
800
801             /*
802              * Finally execute the mouse or mouse wheel callback
803              *
804              * XXX Use a symbolic constant, *not* "4"!
805              */
806             if( ( button < 3 ) || ( ! FETCH_WCB( *window, MouseWheel ) ) )
807                 INVOKE_WCB( *window, Mouse, ( button,
808                                               pressed ? GLUT_DOWN : GLUT_UP,
809                                               event.xbutton.x,
810                                               event.xbutton.y )
811                 );
812             else
813             {
814                 /*
815                  * Map 4 and 5 to wheel zero; EVEN to +1, ODD to -1
816                  *  "  6 and 7 "    "   one; ...
817                  *
818                  * XXX This *should* be behind some variables/macros,
819                  * XXX since the order and numbering isn't certain
820                  * XXX See XFree86 configuration docs (even back in the
821                  * XXX 3.x days, and especially with 4.x).
822                  *
823                  * XXX Note that {button} has already been decremeted
824                  * XXX in mapping from X button numbering to GLUT.
825                  */
826                 int wheel_number = ( button - 3 ) / 2;
827                 int direction = -1;
828                 if( button % 2 )
829                     direction = 1;
830                 
831                 if( pressed )
832                     INVOKE_WCB( *window, MouseWheel, ( wheel_number,
833                                                        direction,
834                                                        event.xbutton.x,
835                                                        event.xbutton.y )
836                     );
837             }
838
839             /*
840              * Trash the modifiers state
841              */
842             fgState.Modifiers = 0xffffffff;
843         }
844         break;
845
846         case KeyRelease:
847         case KeyPress:
848         {
849             FGCBKeyboard keyboard_cb;
850             FGCBSpecial special_cb;
851
852             GETWINDOW( xkey );
853             GETMOUSE( xkey );
854
855             if( event.type == KeyPress )
856             {
857                 keyboard_cb = FETCH_WCB( *window, Keyboard );
858                 special_cb  = FETCH_WCB( *window, Special  );
859             }
860             else
861             {
862                 keyboard_cb = FETCH_WCB( *window, KeyboardUp );
863                 special_cb  = FETCH_WCB( *window, SpecialUp  );
864             }
865
866             /*
867              * Is there a keyboard/special callback hooked for this window?
868              */
869             if( keyboard_cb || special_cb )
870             {
871                 XComposeStatus composeStatus;
872                 char asciiCode[ 32 ];
873                 KeySym keySym;
874                 int len;
875
876                 /*
877                  * Check for the ASCII/KeySym codes associated with the event:
878                  */
879                 len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode),
880                                      &keySym, &composeStatus
881                 );
882
883                 /*
884                  * GLUT API tells us to have two separate callbacks...
885                  */
886                 if( len > 0 )
887                 {
888                     /*
889                      * ...one for the ASCII translateable keypresses...
890                      */
891                     if( keyboard_cb )
892                     {
893                         fgSetWindow( window );
894                         fgState.Modifiers = fgGetXModifiers( &event );
895                         keyboard_cb( asciiCode[ 0 ],
896                                      event.xkey.x, event.xkey.y
897                         );
898                         fgState.Modifiers = 0xffffffff;
899                     }
900                 }
901                 else
902                 {
903                     int special = -1;
904
905                     /*
906                      * ...and one for all the others, which need to be
907                      * translated to GLUT_KEY_Xs...
908                      */
909                     switch( keySym )
910                     {
911                     case XK_F1:     special = GLUT_KEY_F1;     break;
912                     case XK_F2:     special = GLUT_KEY_F2;     break;
913                     case XK_F3:     special = GLUT_KEY_F3;     break;
914                     case XK_F4:     special = GLUT_KEY_F4;     break;
915                     case XK_F5:     special = GLUT_KEY_F5;     break;
916                     case XK_F6:     special = GLUT_KEY_F6;     break;
917                     case XK_F7:     special = GLUT_KEY_F7;     break;
918                     case XK_F8:     special = GLUT_KEY_F8;     break;
919                     case XK_F9:     special = GLUT_KEY_F9;     break;
920                     case XK_F10:    special = GLUT_KEY_F10;    break;
921                     case XK_F11:    special = GLUT_KEY_F11;    break;
922                     case XK_F12:    special = GLUT_KEY_F12;    break;
923
924                     case XK_Left:   special = GLUT_KEY_LEFT;   break;
925                     case XK_Right:  special = GLUT_KEY_RIGHT;  break;
926                     case XK_Up:     special = GLUT_KEY_UP;     break;
927                     case XK_Down:   special = GLUT_KEY_DOWN;   break;
928
929                     case XK_KP_Prior:
930                     case XK_Prior:  special = GLUT_KEY_PAGE_UP; break;
931                     case XK_KP_Next:
932                     case XK_Next:   special = GLUT_KEY_PAGE_DOWN; break;
933                     case XK_KP_Home:
934                     case XK_Home:   special = GLUT_KEY_HOME;   break;
935                     case XK_KP_End:
936                     case XK_End:    special = GLUT_KEY_END;    break;
937                     case XK_KP_Insert:
938                     case XK_Insert: special = GLUT_KEY_INSERT; break;
939                     }
940
941                     /*
942                      * Execute the callback (if one has been specified),
943                      * given that the special code seems to be valid...
944                      */
945                     if( special_cb && ( special != -1 ) )
946                     {
947                         fgSetWindow( window );
948                         fgState.Modifiers = fgGetXModifiers( &event );
949                         special_cb( special, event.xkey.x, event.xkey.y );
950                         fgState.Modifiers = 0xffffffff;
951                     }
952                 }
953             }
954         }
955         break;
956
957         case ReparentNotify:
958             break; /* XXX Should disable this event */
959
960         default:
961             fgWarning( "Unknown X event type: %d", event.type );
962             break;
963         }
964     }
965
966 #elif TARGET_HOST_WIN32
967
968     MSG stMsg;
969
970     while( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
971     {
972         if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
973             fgState.ExecState = GLUT_EXEC_STATE_STOP;
974
975         TranslateMessage( &stMsg );
976         DispatchMessage( &stMsg );
977     }
978 #endif
979
980     fghCheckTimers( );
981     fghCheckJoystickPolls( );
982     fghDisplayAll( );
983
984     fgCloseWindows( );
985 }
986
987 /*
988  * Enters the freeglut processing loop.
989  * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
990  */
991 void FGAPIENTRY glutMainLoop( void )
992 {
993 #if TARGET_HOST_WIN32
994     SFG_Window *window = (SFG_Window *)fgStructure.Windows.First;
995 #endif
996
997     freeglut_assert_ready;
998
999 #if TARGET_HOST_WIN32
1000     /*
1001      * Processing before the main loop:  If there is a window which is open and
1002      * which has a visibility callback, call it.  I know this is an ugly hack,
1003      * but I'm not sure what else to do about it.  Ideally we should leave
1004      * something uninitialized in the create window code and initialize it in
1005      * the main loop, and have that initialization create a "WM_ACTIVATE"
1006      * message.  Then we would put the visibility callback code in the
1007      * "case WM_ACTIVATE" block below.         - John Fay -- 10/24/02
1008      */
1009     while( window )
1010     {
1011         if( FETCH_WCB( *window, Visibility ) )
1012         {
1013             SFG_Window *current_window = fgStructure.Window;
1014
1015             INVOKE_WCB( *window, Visibility, ( window->State.Visible ) );
1016             fgSetWindow( current_window );
1017         }
1018         
1019         window = (SFG_Window *)window->Node.Next;
1020     }
1021 #endif
1022
1023     fgState.ExecState = GLUT_EXEC_STATE_RUNNING;
1024     while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
1025     {
1026         glutMainLoopEvent( );
1027
1028         if( fgStructure.Windows.First == NULL )
1029             fgState.ExecState = GLUT_EXEC_STATE_STOP;
1030         else
1031         {
1032             if( fgState.IdleCallback )
1033                 fgState.IdleCallback( );
1034
1035             fgSleepForEvents( );
1036         }
1037     }
1038
1039     {
1040         fgExecutionState execState = fgState.ExecState;
1041         
1042         /*
1043          * When this loop terminates, destroy the display, state and structure
1044          * of a freeglut session, so that another glutInit() call can happen
1045          */
1046         fgDeinitialize( );
1047
1048         if( execState == GLUT_ACTION_EXIT )
1049             exit( 0 );
1050     }
1051 }
1052
1053 /*
1054  * Leaves the freeglut processing loop.
1055  */
1056 void FGAPIENTRY glutLeaveMainLoop( void )
1057 {
1058     fgState.ExecState = GLUT_EXEC_STATE_STOP;
1059 }
1060
1061
1062 #if TARGET_HOST_WIN32
1063 /*
1064  * Determine a GLUT modifer mask based on MS-WINDOWS system info.
1065  */
1066 int fgGetWin32Modifiers (void)
1067 {
1068     return
1069         ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
1070             ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1071         ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
1072             ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1073         ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
1074             ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1075 }
1076
1077 /*
1078  * The window procedure for handling Win32 events
1079  */
1080 LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
1081                                LPARAM lParam )
1082 {
1083     SFG_Window* window = fgWindowByHandle( hWnd );
1084     PAINTSTRUCT ps;
1085     LONG lRet = 1;
1086
1087     if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
1088       return DefWindowProc( hWnd, uMsg, wParam, lParam );
1089
1090     /* printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0,
1091              uMsg, wParam, lParam ); */
1092     switch( uMsg )
1093     {
1094     case WM_CREATE:
1095         /*
1096          * The window structure is passed as the creation structure paramter...
1097          */
1098         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
1099         assert( window != NULL );
1100
1101         window->Window.Handle = hWnd;
1102         window->Window.Device = GetDC( hWnd );
1103         if( window->IsMenu )
1104         {
1105             unsigned int current_DisplayMode = fgState.DisplayMode;
1106             fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH;
1107             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
1108             fgState.DisplayMode = current_DisplayMode;
1109
1110             if( fgStructure.MenuContext )
1111                 wglMakeCurrent( window->Window.Device,
1112                                 fgStructure.MenuContext->Context
1113                 );
1114             else
1115             {
1116                 fgStructure.MenuContext =
1117                     (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
1118                 fgStructure.MenuContext->Context =
1119                     wglCreateContext( window->Window.Device );
1120             }
1121
1122             /* window->Window.Context = wglGetCurrentContext( );   */
1123             window->Window.Context = wglCreateContext( window->Window.Device );
1124         }
1125         else
1126         {
1127             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
1128
1129             if( ! fgState.UseCurrentContext )
1130                 window->Window.Context =
1131                     wglCreateContext( window->Window.Device );
1132             else
1133             {
1134                 window->Window.Context = wglGetCurrentContext( );
1135                 if( ! window->Window.Context )
1136                     window->Window.Context =
1137                         wglCreateContext( window->Window.Device );
1138             }
1139         }
1140
1141         window->State.NeedToResize = GL_TRUE;
1142         ReleaseDC( window->Window.Handle, window->Window.Device );
1143         break;
1144
1145     case WM_SIZE:
1146         /*
1147          * We got resized... But check if the window has been already added...
1148          */
1149         fghReshapeWindowByHandle( hWnd, LOWORD(lParam), HIWORD(lParam) );
1150         break;
1151 #if 0
1152     case WM_SETFOCUS: 
1153         printf( "WM_SETFOCUS: %p\n", window );
1154         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1155         break;
1156
1157     case WM_ACTIVATE: 
1158         if( LOWORD( wParam ) != WA_INACTIVE )
1159         {
1160             /* glutSetCursor( fgStructure.Window->State.Cursor ); */
1161             printf("WM_ACTIVATE: glutSetCursor( %p, %d)\n", window,
1162                    window->State.Cursor );
1163             glutSetCursor( window->State.Cursor );
1164         }
1165
1166         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1167         break;
1168 #endif
1169
1170     case WM_SETCURSOR: 
1171         /*
1172          * Windows seems to need reminding to erase the cursor for NONE.
1173          */
1174
1175         /*
1176          * XXX Is this #if 0 section anything that we need to worry
1177          * XXX about?  Can we delete it?  If it will ever be used,
1178          * XXX why not re-use some common code with the glutSetCursor()
1179          * XXX function (or perhaps invoke glutSetCursor())?
1180          */
1181 #if 0
1182         if( ( LOWORD( lParam ) == HTCLIENT ) &&
1183             ( fgStructure.Window->State.Cursor == GLUT_CURSOR_NONE ) )
1184           SetCursor( NULL );
1185 #else
1186
1187         /* Set the cursor AND change it for this window class. */
1188 #define MAP_CURSOR(a,b)                     \
1189     case a:                                 \
1190         SetCursor( LoadCursor( NULL, b ) ); \
1191         break;
1192
1193         /* Nuke the cursor AND change it for this window class. */
1194 #define ZAP_CURSOR(a,b)     \
1195     case a:                 \
1196         SetCursor( NULL );  \
1197         break;
1198
1199         if( LOWORD( lParam ) == HTCLIENT )
1200             switch( window->State.Cursor )
1201             {
1202                 MAP_CURSOR( GLUT_CURSOR_RIGHT_ARROW, IDC_ARROW     );
1203                 MAP_CURSOR( GLUT_CURSOR_LEFT_ARROW,  IDC_ARROW     );
1204                 MAP_CURSOR( GLUT_CURSOR_INFO,        IDC_HELP      );
1205                 MAP_CURSOR( GLUT_CURSOR_DESTROY,     IDC_CROSS     );
1206                 MAP_CURSOR( GLUT_CURSOR_HELP,        IDC_HELP      );
1207                 MAP_CURSOR( GLUT_CURSOR_CYCLE,       IDC_SIZEALL   );
1208                 MAP_CURSOR( GLUT_CURSOR_SPRAY,       IDC_CROSS     );
1209                 MAP_CURSOR( GLUT_CURSOR_WAIT,        IDC_WAIT      );
1210                 MAP_CURSOR( GLUT_CURSOR_TEXT,        IDC_UPARROW   );
1211                 MAP_CURSOR( GLUT_CURSOR_CROSSHAIR,   IDC_CROSS     );
1212                 /* MAP_CURSOR( GLUT_CURSOR_NONE,        IDC_NO         ); */
1213                 ZAP_CURSOR( GLUT_CURSOR_NONE,        NULL          );
1214
1215             default:
1216                 MAP_CURSOR( GLUT_CURSOR_UP_DOWN,     IDC_ARROW     );
1217             }
1218 #endif
1219         else
1220             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1221         break;
1222
1223     case WM_SHOWWINDOW:
1224         window->State.Visible = GL_TRUE;
1225         window->State.Redisplay = GL_TRUE;
1226         break;
1227
1228     case WM_PAINT:
1229         /* Turn on the visibility in case it was turned off somehow */
1230         window->State.Visible = GL_TRUE;
1231         BeginPaint( hWnd, &ps );
1232         fghRedrawWindowByHandle( hWnd );
1233         EndPaint( hWnd, &ps );
1234         break;
1235
1236     case WM_CLOSE:
1237         /*
1238          * Make sure we don't close a window with current context active
1239          */
1240         if( fgStructure.Window == window )
1241         {
1242             int used = FALSE;
1243             SFG_Window *iter;
1244
1245             wglMakeCurrent( NULL, NULL );
1246             /*
1247              * Step through the list of windows.  If the rendering context
1248              * is not being used by another window, then we delete it.
1249              */
1250             for( iter = (SFG_Window *)fgStructure.Windows.First;
1251                  iter;
1252                  iter = (SFG_Window *)iter->Node.Next )
1253             {
1254                 if( ( iter->Window.Context == window->Window.Context ) &&
1255                     ( iter != window ) )
1256                     used = TRUE;
1257             }
1258
1259             if( used == FALSE )
1260                 wglDeleteContext( window->Window.Context );
1261         }
1262
1263         /*
1264          * Put on a linked list of windows to be removed after all the
1265          * callbacks have returned
1266          */
1267         fgAddToWindowDestroyList( window, GL_FALSE );
1268         DestroyWindow( hWnd );
1269         break;
1270
1271     case WM_DESTROY:
1272         /*
1273          * The window already got destroyed, so don't bother with it.
1274          */
1275         return 0;
1276
1277     case WM_MOUSEMOVE:
1278     {
1279         window->State.MouseX = LOWORD( lParam );
1280         window->State.MouseY = HIWORD( lParam );
1281         
1282         if ( window->ActiveMenu )
1283         {
1284             window->State.Redisplay = GL_TRUE;
1285             fgSetWindow ( window->ActiveMenu->ParentWindow );
1286             break;
1287         }
1288
1289         fgState.Modifiers = fgGetWin32Modifiers( );
1290
1291         if( ( wParam & MK_LBUTTON ) ||
1292             ( wParam & MK_MBUTTON ) ||
1293             ( wParam & MK_RBUTTON ) )
1294             INVOKE_WCB( *window, Motion, ( window->State.MouseX,
1295                                            window->State.MouseY ) );
1296         else
1297             INVOKE_WCB( *window, Passive, ( window->State.MouseX,
1298                                             window->State.MouseY ) );
1299
1300         fgState.Modifiers = 0xffffffff;
1301     }
1302     break;
1303
1304     case WM_LBUTTONDOWN:
1305     case WM_MBUTTONDOWN:
1306     case WM_RBUTTONDOWN:
1307     case WM_LBUTTONUP:
1308     case WM_MBUTTONUP:
1309     case WM_RBUTTONUP:
1310     {
1311         GLboolean pressed = GL_TRUE;
1312         int button;
1313
1314         window->State.MouseX = LOWORD( lParam );
1315         window->State.MouseY = HIWORD( lParam );
1316
1317         /*
1318          * XXX Either these multi-statement lines should be broken
1319          * XXX in the form:
1320          * XXX     pressed = GL_TRUE;
1321          * XXX     button = GLUT_LEFT_BUTTON;
1322          * XXX     break;
1323          * XXX ...or we should use a macro (much as I dislike freeglut's
1324          * XXX preponderance of using macros to "compress" code).
1325          */
1326         switch( uMsg )
1327         {
1328         case WM_LBUTTONDOWN:
1329             pressed = GL_TRUE;  button = GLUT_LEFT_BUTTON;   break;
1330         case WM_MBUTTONDOWN:
1331             pressed = GL_TRUE;  button = GLUT_MIDDLE_BUTTON; break;
1332         case WM_RBUTTONDOWN:
1333             pressed = GL_TRUE;  button = GLUT_RIGHT_BUTTON;  break;
1334         case WM_LBUTTONUP:
1335             pressed = GL_FALSE; button = GLUT_LEFT_BUTTON;   break;
1336         case WM_MBUTTONUP:
1337             pressed = GL_FALSE; button = GLUT_MIDDLE_BUTTON; break;
1338         case WM_RBUTTONUP:
1339             pressed = GL_FALSE; button = GLUT_RIGHT_BUTTON;  break;
1340         default:
1341             pressed = GL_FALSE; button = -1;                 break;
1342         }
1343
1344         if( GetSystemMetrics( SM_SWAPBUTTON ) )
1345             if( button == GLUT_LEFT_BUTTON )
1346                 button = GLUT_RIGHT_BUTTON;
1347             else if( button == GLUT_RIGHT_BUTTON )
1348                 button = GLUT_LEFT_BUTTON;
1349
1350         if( button == -1 )
1351             return DefWindowProc( hWnd, uMsg, lParam, wParam );
1352
1353         /*
1354          * XXX This comment is duplicated in two other spots.
1355          * XXX Can we centralize it?
1356          *
1357          * Do not execute the application's mouse callback if a
1358          * menu is hooked to this button.
1359          * In that case an appropriate private call should be generated.
1360          * Near as I can tell, this is the menu behaviour:
1361          *  - Down-click the menu button, menu not active:  activate
1362          *    the menu with its upper left-hand corner at the mouse location.
1363          *  - Down-click any button outside the menu, menu active:
1364          *    deactivate the menu
1365          *  - Down-click any button inside the menu, menu active:
1366          *    select the menu entry and deactivate the menu
1367          *  - Up-click the menu button, menu not active:  nothing happens
1368          *  - Up-click the menu button outside the menu, menu active:
1369          *    nothing happens
1370          *  - Up-click the menu button inside the menu, menu active:
1371          *    select the menu entry and deactivate the menu
1372          */
1373         /* Window has an active menu, it absorbs any mouse click */
1374         if( window->ActiveMenu )
1375         {
1376             /* Outside the menu, deactivate the menu if it's a downclick */
1377             if( ! fgCheckActiveMenu( window, window->ActiveMenu ) )
1378             {
1379                 if( pressed )
1380                     fgDeactivateMenu( window->ActiveMenu->ParentWindow );
1381             }
1382             else  /* In menu, invoke the callback and deactivate the menu*/
1383             {
1384                 /*
1385                  * Save the current window and menu and set the current
1386                  * window to the window whose menu this is
1387                  */
1388                 SFG_Window *save_window = fgStructure.Window;
1389                 SFG_Menu *save_menu = fgStructure.Menu;
1390                 SFG_Window *parent_window = window->ActiveMenu->ParentWindow;
1391                 fgSetWindow( parent_window );
1392                 fgStructure.Menu = window->ActiveMenu;
1393
1394                 /* Execute the menu callback */
1395                 fgExecuteMenuCallback( window->ActiveMenu );
1396                 fgDeactivateMenu( parent_window );
1397
1398                 /* Restore the current window and menu */
1399                 fgSetWindow( save_window );
1400                 fgStructure.Menu = save_menu;
1401             }
1402
1403             /*
1404              * Let's make the window redraw as a result of the mouse
1405              * click and menu activity.
1406              */
1407             if( ! window->IsMenu )
1408                 window->State.Redisplay = GL_TRUE;
1409
1410             break;
1411         }
1412
1413         if ( window->Menu[ button ] && pressed )
1414         {
1415             window->State.Redisplay = GL_TRUE;
1416             fgSetWindow( window );
1417             fgActivateMenu( window, button );
1418
1419             break;
1420         }
1421
1422         if( ! FETCH_WCB( *window, Mouse ) )
1423             break;
1424
1425         fgSetWindow( window );
1426         fgState.Modifiers = fgGetWin32Modifiers( );
1427
1428         INVOKE_WCB(
1429             *window, Mouse,
1430             ( button,
1431               pressed ? GLUT_DOWN : GLUT_UP,
1432               window->State.MouseX,
1433               window->State.MouseY
1434             )
1435         );
1436
1437         fgState.Modifiers = 0xffffffff;
1438     }
1439     break;
1440
1441     case 0x020a:
1442         /* Should be WM_MOUSEWHEEL but my compiler doesn't recognize it */
1443     {
1444         /*
1445          * XXX THIS IS SPECULATIVE -- John Fay, 10/2/03
1446          * XXX Should use WHEEL_DELTA instead of 120
1447          */
1448         int wheel_number = LOWORD( wParam );
1449         short ticks = ( short )HIWORD( wParam ) / 120;
1450         int direction = 1;
1451
1452         if( ticks < 0 )
1453         {
1454             direction = -1;
1455             ticks = -ticks;
1456         }
1457
1458         /*
1459          * The mouse cursor has moved. Remember the new mouse cursor's position
1460          */
1461         /*        window->State.MouseX = LOWORD( lParam ); */
1462         /* Need to adjust by window position, */
1463         /*        window->State.MouseY = HIWORD( lParam ); */
1464         /* change "lParam" to other parameter */
1465
1466         if( ! FETCH_WCB( *window, MouseWheel ) &&
1467             ! FETCH_WCB( *window, Mouse ) )
1468             break;
1469
1470         fgSetWindow( window );
1471         fgState.Modifiers = fgGetWin32Modifiers( );
1472
1473         while( ticks-- )
1474             if( FETCH_WCB( *window, MouseWheel ) )
1475                 INVOKE_WCB( *window, MouseWheel,
1476                             ( wheel_number,
1477                               direction,
1478                               window->State.MouseX,
1479                               window->State.MouseY
1480                             )
1481                 );
1482             else  /* No mouse wheel, call the mouse button callback twice */
1483             {
1484                 /*
1485                  * XXX The below assumes that you have no more than 3 mouse
1486                  * XXX buttons.  Sorry.
1487                  */
1488                 int button = wheel_number*2 + 4;
1489                 if( direction > 0 )
1490                     ++button;
1491                 INVOKE_WCB( *window, Mouse,
1492                             ( button, GLUT_DOWN,
1493                               window->State.MouseX, window->State.MouseY )
1494                 );
1495                 INVOKE_WCB( *window, Mouse,
1496                             ( button, GLUT_UP,
1497                               window->State.MouseX, window->State.MouseX )
1498                 );
1499             }
1500
1501         fgState.Modifiers = 0xffffffff;
1502     }
1503     break;
1504
1505     case WM_SYSKEYDOWN:
1506     case WM_KEYDOWN:
1507     {
1508         int keypress = -1;
1509         POINT mouse_pos;
1510
1511         if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
1512             break;
1513
1514         /*
1515          * Remember the current modifiers state. This is done here in order 
1516          * to make sure the VK_DELETE keyboard callback is executed properly.
1517          */
1518         fgState.Modifiers = fgGetWin32Modifiers( );
1519
1520         GetCursorPos( &mouse_pos );
1521         ScreenToClient( window->Window.Handle, &mouse_pos );
1522
1523         window->State.MouseX = mouse_pos.x;
1524         window->State.MouseY = mouse_pos.y;
1525
1526         /*
1527          * Convert the Win32 keystroke codes to GLUTtish way
1528          */
1529 #       define KEY(a,b) case a: keypress = b; break;
1530
1531         switch( wParam )
1532         {
1533             KEY( VK_F1,     GLUT_KEY_F1        );
1534             KEY( VK_F2,     GLUT_KEY_F2        );
1535             KEY( VK_F3,     GLUT_KEY_F3        );
1536             KEY( VK_F4,     GLUT_KEY_F4        );
1537             KEY( VK_F5,     GLUT_KEY_F5        );
1538             KEY( VK_F6,     GLUT_KEY_F6        );
1539             KEY( VK_F7,     GLUT_KEY_F7        );
1540             KEY( VK_F8,     GLUT_KEY_F8        );
1541             KEY( VK_F9,     GLUT_KEY_F9        );
1542             KEY( VK_F10,    GLUT_KEY_F10       );
1543             KEY( VK_F11,    GLUT_KEY_F11       );
1544             KEY( VK_F12,    GLUT_KEY_F12       );
1545             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
1546             KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
1547             KEY( VK_HOME,   GLUT_KEY_HOME      );
1548             KEY( VK_END,    GLUT_KEY_END       );
1549             KEY( VK_LEFT,   GLUT_KEY_LEFT      );
1550             KEY( VK_UP,     GLUT_KEY_UP        );
1551             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
1552             KEY( VK_DOWN,   GLUT_KEY_DOWN      );
1553             KEY( VK_INSERT, GLUT_KEY_INSERT    );
1554
1555         case VK_DELETE:
1556             /*
1557              * The delete key should be treated as an ASCII keypress:
1558              */
1559             INVOKE_WCB( *window, Keyboard,
1560                         ( 127, window->State.MouseX, window->State.MouseY )
1561             );
1562         }
1563
1564         if( keypress != -1 )
1565             INVOKE_WCB( *window, Special,
1566                         ( keypress,
1567                           window->State.MouseX, window->State.MouseY )
1568             );
1569
1570         fgState.Modifiers = 0xffffffff;
1571     }
1572     break;
1573
1574     case WM_SYSKEYUP:
1575     case WM_KEYUP:
1576     {
1577         int keypress = -1;
1578         POINT mouse_pos;
1579
1580         /*
1581          * Remember the current modifiers state. This is done here in order 
1582          * to make sure the VK_DELETE keyboard callback is executed properly.
1583          */
1584         fgState.Modifiers = fgGetWin32Modifiers( );
1585
1586         GetCursorPos( &mouse_pos );
1587         ScreenToClient( window->Window.Handle, &mouse_pos );
1588
1589         window->State.MouseX = mouse_pos.x;
1590         window->State.MouseY = mouse_pos.y;
1591
1592         /*
1593          * Convert the Win32 keystroke codes to GLUTtish way.
1594          * "KEY(a,b)" was defined under "WM_KEYDOWN"
1595          */
1596
1597         switch( wParam )
1598         {
1599             KEY( VK_F1,     GLUT_KEY_F1        );
1600             KEY( VK_F2,     GLUT_KEY_F2        );
1601             KEY( VK_F3,     GLUT_KEY_F3        );
1602             KEY( VK_F4,     GLUT_KEY_F4        );
1603             KEY( VK_F5,     GLUT_KEY_F5        );
1604             KEY( VK_F6,     GLUT_KEY_F6        );
1605             KEY( VK_F7,     GLUT_KEY_F7        );
1606             KEY( VK_F8,     GLUT_KEY_F8        );
1607             KEY( VK_F9,     GLUT_KEY_F9        );
1608             KEY( VK_F10,    GLUT_KEY_F10       );
1609             KEY( VK_F11,    GLUT_KEY_F11       );
1610             KEY( VK_F12,    GLUT_KEY_F12       );
1611             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
1612             KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
1613             KEY( VK_HOME,   GLUT_KEY_HOME      );
1614             KEY( VK_END,    GLUT_KEY_END       );
1615             KEY( VK_LEFT,   GLUT_KEY_LEFT      );
1616             KEY( VK_UP,     GLUT_KEY_UP        );
1617             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
1618             KEY( VK_DOWN,   GLUT_KEY_DOWN      );
1619             KEY( VK_INSERT, GLUT_KEY_INSERT    );
1620
1621           case VK_DELETE:
1622               /*
1623                * The delete key should be treated as an ASCII keypress:
1624                */
1625               INVOKE_WCB( *window, KeyboardUp,
1626                           ( 127, window->State.MouseX, window->State.MouseY )
1627               );
1628               break;
1629
1630         default:
1631         {
1632             BYTE state[ 256 ];
1633             WORD code[ 2 ];
1634             
1635             GetKeyboardState( state );
1636             
1637             if( ToAscii( wParam, 0, state, code, 0 ) == 1 )
1638                 wParam=code[ 0 ];
1639
1640             INVOKE_WCB( *window, KeyboardUp,
1641                         ( (char)wParam,
1642                           window->State.MouseX, window->State.MouseY )
1643             );
1644         }
1645         }
1646
1647         if( keypress != -1 )
1648             INVOKE_WCB( *window, SpecialUp,
1649                         ( keypress,
1650                           window->State.MouseX, window->State.MouseY )
1651             );
1652
1653         fgState.Modifiers = 0xffffffff;
1654     }
1655     break;
1656
1657     case WM_SYSCHAR:
1658     case WM_CHAR:
1659     {
1660         if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
1661             break;
1662
1663         /*
1664          * XXX INVOKE_WCB() takes care of the callback-pointer check.
1665          * XXX We could just uncoditionally find/trash the Modifiers
1666          * XXX and get rid of the "if( ... ) {" and "}".  Unconditinal
1667          * XXX code is simpler code.  (^&
1668          */
1669         if( FETCH_WCB( *window, Keyboard ) )
1670         {
1671             fgState.Modifiers = fgGetWin32Modifiers( );
1672             INVOKE_WCB( *window, Keyboard,
1673                         ( (char)wParam,
1674                           window->State.MouseX, window->State.MouseY )
1675             );
1676             fgState.Modifiers = 0xffffffff;
1677         }
1678     }
1679     break;
1680
1681     case WM_CAPTURECHANGED:
1682         /* User has finished resizing the window, force a redraw */
1683         INVOKE_WCB( *window, Display, ( ) );
1684
1685         /*lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ) ; */
1686         break;
1687
1688         /*
1689          * Other messages that I have seen and which are not handled already
1690          */
1691     case WM_SETTEXT:  /* 0x000c */
1692         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1693         /* Pass it on to "DefWindowProc" to set the window text */
1694         break;
1695
1696     case WM_GETTEXT:  /* 0x000d */
1697         /* Ideally we would copy the title of the window into "lParam" */
1698         /* strncpy ( (char *)lParam, "Window Title", wParam );
1699            lRet = ( wParam > 12 ) ? 12 : wParam;  */
1700         /* the number of characters copied */
1701         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1702         break;
1703
1704     case WM_GETTEXTLENGTH:  /* 0x000e */
1705         /* Ideally we would get the length of the title of the window */
1706         lRet = 12;
1707         /* the number of characters in "Window Title\0" (see above) */
1708         break;
1709
1710     case WM_ERASEBKGND:  /* 0x0014 */
1711         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1712         break;
1713
1714     case WM_SYNCPAINT:  /* 0x0088 */
1715         /* Another window has moved, need to update this one */
1716         window->State.Redisplay = GL_TRUE;
1717         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1718         /* Help screen says this message must be passed to "DefWindowProc" */
1719         break;
1720
1721     case WM_NCPAINT:  /* 0x0085 */
1722       /* Need to update the border of this window */
1723         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1724         /* Pass it on to "DefWindowProc" to repaint a standard border */
1725         break;
1726
1727     case WM_SYSCOMMAND :  /* 0x0112 */
1728         {
1729           /*
1730            * We have received a system command message.  Try to act on it.
1731            * The commands are passed in through the "lParam" parameter:
1732            * Clicking on a corner to resize the window gives a "F004" message
1733            * but this is not defined in my header file.
1734            */
1735             switch ( lParam )
1736             {
1737             case SC_SIZE:
1738                 break;
1739
1740             case SC_MOVE:
1741                 break;
1742
1743             case SC_MINIMIZE:
1744                 /* User has clicked on the "-" to minimize the window */
1745                 /* Turn off the visibility */
1746                 window->State.Visible = GL_FALSE;
1747                 break;
1748
1749             case SC_MAXIMIZE:
1750                 break;
1751
1752             case SC_NEXTWINDOW:
1753                 break;
1754
1755             case SC_PREVWINDOW:
1756                 break;
1757
1758             case SC_CLOSE:
1759                 /* Followed very closely by a WM_CLOSE message */
1760                 break;
1761
1762             case SC_VSCROLL:
1763                 break;
1764
1765             case SC_HSCROLL:
1766                 break;
1767
1768             case SC_MOUSEMENU:
1769                 break;
1770
1771             case SC_KEYMENU:
1772                 break;
1773
1774             case SC_ARRANGE:
1775                 break;
1776
1777             case SC_RESTORE:
1778                 break;
1779
1780             case SC_TASKLIST:
1781                 break;
1782
1783             case SC_SCREENSAVE:
1784                 break;
1785
1786             case SC_HOTKEY:
1787                 break;
1788             }
1789         }
1790
1791         /* We need to pass the message on to the operating system as well */
1792         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1793         break;
1794
1795     default:
1796         /*
1797          * Handle unhandled messages
1798          */
1799         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1800         break;
1801     }
1802
1803     return lRet;
1804 }
1805 #endif
1806
1807 /*** END OF FILE ***/