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