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