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