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