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