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