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