Propogated a pointer-check from menu-attach to menu-detach. (Apparently,
[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 #elif TARGET_HOST_WIN32
999
1000   MSG stMsg;
1001
1002   /*
1003    * The windows processing is considerably smaller
1004    */
1005   while( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
1006   {
1007     /*
1008      * Grab the message now, checking for WM_QUIT
1009      */
1010     if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
1011       fgState.ExecState = GLUT_EXEC_STATE_STOP ;
1012
1013     /*
1014      * Translate virtual-key messages and send them to the window...
1015      */
1016     TranslateMessage( &stMsg );
1017     DispatchMessage( &stMsg );
1018   }
1019
1020 #endif
1021
1022     fghCheckTimers ();
1023     fghCheckJoystickPolls ();
1024     fghDisplayAll ();
1025     fgCloseWindows () ;
1026 }
1027
1028 /*
1029  * Enters the freeglut processing loop. Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
1030  */
1031 void FGAPIENTRY glutMainLoop( void )
1032 {
1033 #if TARGET_HOST_WIN32
1034   SFG_Window *window = (SFG_Window *)fgStructure.Windows.First ;
1035 #endif
1036
1037   freeglut_assert_ready;
1038
1039 #if TARGET_HOST_WIN32
1040   /*
1041    * Processing before the main loop:  If there is a window which is open and
1042    * which has a visibility callback, call it.  I know this is an ugly hack,
1043    * but I'm not sure what else to do about it.  Ideally we should leave
1044    * something uninitialized in the create window code and initialize it in
1045    * the main loop, and have that initialization create a "WM_ACTIVATE"
1046    * message.  Then we would put the visibility callback code in the
1047    * "case WM_ACTIVATE" block below.         - John Fay -- 10/24/02
1048    */
1049   while ( window != NULL )
1050   {
1051     if ( window->Callbacks.Visibility != NULL )
1052     {
1053       SFG_Window *current_window = fgStructure.Window ;
1054       fgSetWindow( window );
1055       window->Callbacks.Visibility ( window->State.Visible ) ;
1056       fgSetWindow( current_window );
1057     }
1058
1059     window = (SFG_Window *)window->Node.Next ;
1060   }
1061 #endif
1062
1063   fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
1064
1065   while ( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
1066   {
1067     glutMainLoopEvent () ;
1068     if ( fgStructure.Windows.First == NULL )
1069       fgState.ExecState = GLUT_EXEC_STATE_STOP ;
1070     else
1071     {
1072       if ( fgState.IdleCallback )
1073         fgState.IdleCallback ();
1074       fgSleepForEvents();
1075     }
1076   }
1077
1078   {
1079     fgExecutionState execState = fgState.ActionOnWindowClose;
1080
1081     /*
1082      * When this loop terminates, destroy the display, state and structure
1083      * of a freeglut session, so that another glutInit() call can happen
1084      */
1085     fgDeinitialize();
1086
1087     if ( execState == GLUT_ACTION_EXIT )
1088       exit ( 0 ) ;
1089   }
1090 }
1091
1092 /*
1093  * Leaves the freeglut processing loop.
1094  */
1095 void FGAPIENTRY glutLeaveMainLoop( void )
1096 {
1097   fgState.ExecState = GLUT_EXEC_STATE_STOP ;
1098 }
1099
1100 /*
1101  * The window procedure for handling Win32 events
1102  */
1103 #if TARGET_HOST_WIN32
1104 LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
1105 {
1106     SFG_Window* window = fgWindowByHandle( hWnd );
1107     PAINTSTRUCT ps;
1108     LONG lRet = 1;
1109
1110     if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
1111       return( DefWindowProc( hWnd, uMsg, wParam, lParam ) );
1112
1113 /*    printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0, uMsg, wParam, lParam ) ; */
1114     /*
1115      * Check what type of message are we receiving
1116      */
1117     switch( uMsg )
1118     {
1119     case WM_CREATE:
1120         /*
1121          * The window structure is passed as the creation structure paramter...
1122          */
1123         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
1124         assert( window != NULL );
1125
1126         /*
1127          * We can safely store the window's handle now:
1128          */
1129         window->Window.Handle = hWnd;
1130
1131         /*
1132          * Get the window's device context
1133          */
1134         window->Window.Device = GetDC( hWnd );
1135
1136         /*
1137          * Create or get the OpenGL rendering context now
1138          */
1139         if ( fgState.BuildingAMenu )
1140         {
1141           /*
1142            * Setup the pixel format of our window
1143            */
1144           unsigned int current_DisplayMode = fgState.DisplayMode ;
1145           fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH ;
1146           fgSetupPixelFormat( window, FALSE, PFD_MAIN_PLANE );
1147           fgState.DisplayMode = current_DisplayMode ;
1148
1149           /*
1150            * If there isn't already an OpenGL rendering context for menu windows, make one
1151            */
1152           if ( !fgStructure.MenuContext )
1153           {
1154             fgStructure.MenuContext = (SFG_MenuContext *)malloc ( sizeof(SFG_MenuContext) ) ;
1155             fgStructure.MenuContext->Context = wglCreateContext( window->Window.Device );
1156           }
1157           else
1158             wglMakeCurrent ( window->Window.Device, fgStructure.MenuContext->Context ) ;
1159
1160 /*          window->Window.Context = wglGetCurrentContext () ;   */
1161           window->Window.Context = wglCreateContext( window->Window.Device );
1162         }
1163         else
1164         {
1165           /*
1166            * Setup the pixel format of our window
1167            */
1168           fgSetupPixelFormat( window, FALSE, PFD_MAIN_PLANE );
1169
1170           if ( fgState.UseCurrentContext == TRUE )
1171           {
1172             window->Window.Context = wglGetCurrentContext();
1173             if ( ! window->Window.Context )
1174               window->Window.Context = wglCreateContext( window->Window.Device );
1175           }
1176           else
1177             window->Window.Context = wglCreateContext( window->Window.Device );
1178         }
1179
1180         /*
1181          * Still, we'll be needing to explicitly resize the window
1182          */
1183         window->State.NeedToResize = TRUE;
1184
1185         /*
1186          * Finally, have the window's device context released
1187          */
1188         ReleaseDC( window->Window.Handle, window->Window.Device );
1189         break;
1190
1191     case WM_SIZE:
1192         /*
1193          * We got resized... But check if the window has been already added...
1194          */
1195         fghReshapeWindowByHandle( hWnd, LOWORD(lParam), HIWORD(lParam) );
1196         break;
1197 #if 0
1198     case WM_SETFOCUS: 
1199         printf("WM_SETFOCUS: %p\n", window );
1200         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1201         break;
1202
1203     case WM_ACTIVATE: 
1204         if (LOWORD(wParam) != WA_INACTIVE)
1205         {
1206           /* glutSetCursor( fgStructure.Window->State.Cursor ); */
1207                 printf("WM_ACTIVATE: glutSetCursor( %p, %d)\n", window, window->State.Cursor );
1208
1209           glutSetCursor( window->State.Cursor );
1210         }
1211
1212         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1213         break;
1214 #endif
1215
1216     case WM_SETCURSOR: 
1217         /*
1218          * Windows seems to need reminding to erase the cursor for NONE.
1219          */
1220 #if 0
1221         if ((LOWORD(lParam) == HTCLIENT) &&
1222             (fgStructure.Window->State.Cursor == GLUT_CURSOR_NONE))
1223           SetCursor( NULL );
1224 #else
1225         /* Set the cursor AND change it for this window class. */
1226 #       define MAP_CURSOR(a,b) case a: SetCursor( LoadCursor( NULL, b ) ); \
1227         break;
1228         /* Nuke the cursor AND change it for this window class. */
1229 #       define ZAP_CURSOR(a,b) case a: SetCursor( NULL ); \
1230         break;
1231
1232         if (LOWORD(lParam) == HTCLIENT)
1233           switch( window->State.Cursor )
1234           {
1235                 MAP_CURSOR( GLUT_CURSOR_RIGHT_ARROW, IDC_ARROW     );
1236                 MAP_CURSOR( GLUT_CURSOR_LEFT_ARROW,  IDC_ARROW     );
1237                 MAP_CURSOR( GLUT_CURSOR_INFO,        IDC_HELP      );
1238                 MAP_CURSOR( GLUT_CURSOR_DESTROY,     IDC_CROSS     );
1239                 MAP_CURSOR( GLUT_CURSOR_HELP,        IDC_HELP      );
1240                 MAP_CURSOR( GLUT_CURSOR_CYCLE,       IDC_SIZEALL   );
1241                 MAP_CURSOR( GLUT_CURSOR_SPRAY,       IDC_CROSS     );
1242                 MAP_CURSOR( GLUT_CURSOR_WAIT,            IDC_WAIT      );
1243                 MAP_CURSOR( GLUT_CURSOR_TEXT,        IDC_UPARROW   );
1244                 MAP_CURSOR( GLUT_CURSOR_CROSSHAIR,   IDC_CROSS     );
1245                 /* MAP_CURSOR( GLUT_CURSOR_NONE,        IDC_NO             ); */
1246                 ZAP_CURSOR( GLUT_CURSOR_NONE,        NULL          );
1247
1248                 default:
1249                 MAP_CURSOR( GLUT_CURSOR_UP_DOWN,     IDC_ARROW     );
1250           }
1251 #endif
1252         else
1253           lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1254         break;
1255
1256     case WM_SHOWWINDOW:
1257         /*
1258          * We are now Visible!
1259          */
1260         window->State.Visible = TRUE;
1261         window->State.Redisplay = TRUE;
1262         break;
1263
1264     case WM_PAINT:
1265         /*
1266          * Start the painting job
1267          */
1268
1269         BeginPaint( hWnd, &ps );
1270
1271         /*
1272          * Call the engine's main frame drawing method
1273          */
1274         fghRedrawWindowByHandle( hWnd );
1275
1276         /*
1277          * End the painting job, release the device context
1278          */
1279         EndPaint( hWnd, &ps );
1280         break;
1281
1282     case WM_CLOSE:
1283         /*
1284          * Make sure we don't close a window with current context active
1285          */
1286         if( fgStructure.Window == window )
1287         {
1288           int used = FALSE ;
1289           SFG_Window *iter ;
1290
1291             wglMakeCurrent( NULL, NULL );
1292             /* Step through the list of windows.  If the rendering context is notbeing used
1293              * by another window, then we delete it.
1294              */
1295             for ( iter = (SFG_Window *)fgStructure.Windows.First; iter; iter = (SFG_Window *)iter->Node.Next )
1296             {
1297               if ( ( iter->Window.Context == window->Window.Context ) && ( iter != window ) )
1298                 used = TRUE ;
1299             }
1300
1301             if ( used == FALSE ) wglDeleteContext( window->Window.Context );
1302         }
1303
1304         /*
1305          * Put on a linked list of windows to be removed after all the callbacks have returned
1306          */
1307         fgAddToWindowDestroyList ( window, FALSE ) ;
1308
1309         /*
1310          * Proceed with the window destruction
1311          */
1312         DestroyWindow( hWnd );
1313         break;
1314
1315     case WM_DESTROY:
1316         /*
1317          * The window already got destroyed, so don't bother with it.
1318          */
1319         return( 0 );
1320
1321     case WM_MOUSEMOVE:
1322     {
1323         /*
1324          * The mouse cursor has moved. Remember the new mouse cursor's position
1325          */
1326         window->State.MouseX = LOWORD( lParam );
1327         window->State.MouseY = HIWORD( lParam );
1328
1329         /*
1330          * Fallback if there's an active menu hooked to this window
1331          */
1332         if ( window->ActiveMenu != NULL )
1333         {
1334             /*
1335              * Let's make the window redraw as a result of the mouse motion.
1336              */
1337             window->State.Redisplay = TRUE ;
1338
1339             /*
1340              * Since the window is a menu, make the parent window current
1341              */
1342             fgSetWindow ( window->ActiveMenu->ParentWindow ) ;
1343
1344             break;
1345         }
1346
1347         /*
1348          * Remember the current modifiers state.
1349          */
1350         window->State.Modifiers = 
1351             ( ( (GetKeyState( VK_LSHIFT   ) < 0 ) || ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1352             ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1353             ( ( (GetKeyState( VK_LMENU    ) < 0 ) || ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1354
1355         /*
1356          * Check if any of the mouse buttons is pressed...
1357          */
1358         if( (wParam & MK_LBUTTON) || (wParam & MK_MBUTTON) || (wParam & MK_RBUTTON) )
1359         {
1360             /*
1361              * Yeah, indeed. We need to use the motion callback then:
1362              */
1363             if( window->Callbacks.Motion != NULL )
1364             {
1365                 /*
1366                  * Make sure the current window is set...
1367                  */
1368                 fgSetWindow( window );
1369
1370                 /*
1371                  * Execute the active mouse motion callback now
1372                  */
1373                 window->Callbacks.Motion( window->State.MouseX, window->State.MouseY );
1374             }
1375         }
1376         else
1377         {
1378             /*
1379              * All mouse buttons are up, execute the passive mouse motion callback
1380              */
1381             if( window->Callbacks.Passive != NULL )
1382             {
1383                 /*
1384                  * Make sure the current window is set
1385                  */
1386                 fgSetWindow( window );
1387
1388                 /*
1389                  * Execute the passive mouse motion callback
1390                  */
1391                 window->Callbacks.Passive( window->State.MouseX, window->State.MouseY );
1392             }
1393         }
1394
1395         /*
1396          * Thrash the current modifiers state now
1397          */
1398         window->State.Modifiers = 0xffffffff;
1399     }
1400     break;
1401
1402     case WM_LBUTTONDOWN:
1403     case WM_MBUTTONDOWN:
1404     case WM_RBUTTONDOWN:
1405     case WM_LBUTTONUP:
1406     case WM_MBUTTONUP:
1407     case WM_RBUTTONUP:
1408     {
1409         GLboolean pressed = TRUE;
1410         int button;
1411
1412         /*
1413          * The mouse cursor has moved. Remember the new mouse cursor's position
1414          */
1415         window->State.MouseX = LOWORD( lParam );
1416         window->State.MouseY = HIWORD( lParam );
1417
1418         /*
1419          * We're curious about the GLUT API button name...
1420          */
1421         switch( uMsg )
1422         {
1423         case WM_LBUTTONDOWN: pressed = TRUE;  button = GLUT_LEFT_BUTTON;   break;
1424         case WM_MBUTTONDOWN: pressed = TRUE;  button = GLUT_MIDDLE_BUTTON; break;
1425         case WM_RBUTTONDOWN: pressed = TRUE;  button = GLUT_RIGHT_BUTTON;  break;
1426         case WM_LBUTTONUP:   pressed = FALSE; button = GLUT_LEFT_BUTTON;   break;
1427         case WM_MBUTTONUP:   pressed = FALSE; button = GLUT_MIDDLE_BUTTON; break;
1428         case WM_RBUTTONUP:   pressed = FALSE; button = GLUT_RIGHT_BUTTON;  break;
1429         default:             pressed = FALSE; button = -1;                 break;
1430         }
1431
1432         /*
1433          * The left and right mouse buttons might have been swapped...
1434          */
1435         if( GetSystemMetrics( SM_SWAPBUTTON ) )
1436             if( button == GLUT_LEFT_BUTTON ) button = GLUT_RIGHT_BUTTON;
1437             else if( button == GLUT_RIGHT_BUTTON ) button = GLUT_LEFT_BUTTON;
1438
1439         /*
1440          * Hey, what's up with you?
1441          */
1442         if( button == -1 )
1443             return( DefWindowProc( hWnd, uMsg, lParam, wParam ) );
1444
1445         /*
1446          * Do not execute the application's mouse callback if a menu is hooked to this button.
1447          * In that case an appropriate private call should be generated.
1448          * Near as I can tell, this is the menu behaviour:
1449          *  - Down-click the menu button, menu not active:  activate the menu with its upper left-hand corner at the mouse location.
1450          *  - Down-click any button outside the menu, menu active:  deactivate the menu
1451          *  - Down-click any button inside the menu, menu active:  select the menu entry and deactivate the menu
1452          *  - Up-click the menu button, menu not active:  nothing happens
1453          *  - Up-click the menu button outside the menu, menu active:  nothing happens
1454          *  - Up-click the menu button inside the menu, menu active:  select the menu entry and deactivate the menu
1455          */
1456         if ( window->ActiveMenu != NULL )  /* Window has an active menu, it absorbs any mouse click */
1457         {
1458           if ( fgCheckActiveMenu ( window, window->ActiveMenu ) == TRUE )  /* Inside the menu, invoke the callback and deactivate the menu*/
1459           {
1460             /* Save the current window and menu and set the current window to the window whose menu this is */
1461             SFG_Window *save_window = fgStructure.Window ;
1462             SFG_Menu *save_menu = fgStructure.Menu ;
1463             SFG_Window *parent_window = window->ActiveMenu->ParentWindow ;
1464             fgSetWindow ( parent_window ) ;
1465             fgStructure.Menu = window->ActiveMenu ;
1466
1467             /* Execute the menu callback */
1468             fgExecuteMenuCallback ( window->ActiveMenu ) ;
1469             fgDeactivateMenu ( parent_window ) ;
1470
1471             /* Restore the current window and menu */
1472             fgSetWindow ( save_window ) ;
1473             fgStructure.Menu = save_menu ;
1474           }
1475           else  /* Outside the menu, deactivate the menu if it's a downclick */
1476           {
1477             if ( pressed == TRUE ) fgDeactivateMenu ( window->ActiveMenu->ParentWindow ) ;
1478           }
1479
1480           /*
1481            * Let's make the window redraw as a result of the mouse click and menu activity.
1482            */
1483           if ( ! window->IsMenu ) window->State.Redisplay = TRUE ;
1484
1485           break ;
1486         }
1487
1488         /*
1489          * No active menu, let's check whether we need to activate one.
1490          */
1491         if ( ( window->Menu[ button ] != NULL ) && ( pressed == TRUE ) )
1492         {
1493             /*
1494              * Let's make the window redraw as a result of the mouse click.
1495              */
1496             window->State.Redisplay = TRUE ;
1497
1498             /*
1499              * Set the current window
1500              */
1501             fgSetWindow( window );
1502
1503             /*
1504              * Activate the appropriate menu structure...
1505              */
1506             fgActivateMenu( window, button );
1507
1508             break;
1509         }
1510
1511         /*
1512          * Check if there is a mouse callback hooked to the window
1513          */
1514         if( window->Callbacks.Mouse == NULL )
1515             break;
1516
1517         /*
1518          * Set the current window
1519          */
1520         fgSetWindow ( window );
1521
1522         /*
1523          * Remember the current modifiers state.
1524          */
1525         fgStructure.Window->State.Modifiers = 
1526             ( ( (GetKeyState( VK_LSHIFT   ) < 0 ) || ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1527             ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1528             ( ( (GetKeyState( VK_LMENU    ) < 0 ) || ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1529
1530         /*
1531          * Finally execute the mouse callback
1532          */
1533         window->Callbacks.Mouse(
1534             button,
1535             pressed == TRUE ? GLUT_DOWN : GLUT_UP,
1536             window->State.MouseX,
1537             window->State.MouseY
1538         );
1539
1540         /*
1541          * Trash the modifiers state
1542          */
1543         fgStructure.Window->State.Modifiers = 0xffffffff;
1544     }
1545     break;
1546
1547     case WM_SYSKEYDOWN:
1548     case WM_KEYDOWN:
1549     {
1550         int keypress = -1;
1551         POINT mouse_pos ;
1552
1553         /*
1554          * Ignore the automatic key repetition if needed:
1555          */
1556         if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
1557             break;
1558
1559         /*
1560          * Remember the current modifiers state. This is done here in order 
1561          * to make sure the VK_DELETE keyboard callback is executed properly.
1562          */
1563         window->State.Modifiers = 
1564             ( ( (GetKeyState( VK_LSHIFT   ) < 0 ) || ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1565             ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1566             ( ( (GetKeyState( VK_LMENU    ) < 0 ) || ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1567
1568         /*
1569          * Set the mouse position
1570          */
1571         GetCursorPos ( &mouse_pos ) ;
1572         ScreenToClient ( window->Window.Handle, &mouse_pos ) ;
1573
1574         window->State.MouseX = mouse_pos.x ;
1575         window->State.MouseY = mouse_pos.y ;
1576
1577         /*
1578          * Convert the Win32 keystroke codes to GLUTtish way
1579          */
1580 #       define KEY(a,b) case a: keypress = b; break;
1581
1582         switch( wParam )
1583         {
1584             /*
1585              * Most of the special characters can be handled automagically...
1586              */
1587             KEY( VK_F1,     GLUT_KEY_F1        ); KEY( VK_F2,     GLUT_KEY_F2        );
1588             KEY( VK_F3,     GLUT_KEY_F3        ); KEY( VK_F4,     GLUT_KEY_F4        );
1589             KEY( VK_F5,     GLUT_KEY_F5        ); KEY( VK_F6,     GLUT_KEY_F6        );
1590             KEY( VK_F7,     GLUT_KEY_F7        ); KEY( VK_F8,     GLUT_KEY_F8        );
1591             KEY( VK_F9,     GLUT_KEY_F9        ); KEY( VK_F10,    GLUT_KEY_F10       );
1592             KEY( VK_F11,    GLUT_KEY_F11       ); KEY( VK_F12,    GLUT_KEY_F12       );
1593             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   ); KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
1594             KEY( VK_HOME,   GLUT_KEY_HOME      ); KEY( VK_END,    GLUT_KEY_END       );
1595             KEY( VK_LEFT,   GLUT_KEY_LEFT      ); KEY( VK_UP,     GLUT_KEY_UP        );
1596             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     ); KEY( VK_DOWN,   GLUT_KEY_DOWN      );
1597             KEY( VK_INSERT, GLUT_KEY_INSERT    );
1598
1599             /*
1600              * ...yet there is a small exception we need to have handled...
1601              */
1602             case VK_DELETE:
1603                 /*
1604                  * The delete key should be treated as an ASCII keypress:
1605                  */
1606                 if( window->Callbacks.Keyboard != NULL )
1607                 {
1608                     fgSetWindow( window );
1609                     window->Callbacks.Keyboard( 127, window->State.MouseX, window->State.MouseY );
1610                 }
1611         }
1612
1613         /*
1614          * Execute the special callback, if present, given the conversion was a success:
1615          */
1616         if( (keypress != -1) && (window->Callbacks.Special != NULL) )
1617         {
1618             /*
1619              * Set the current window
1620              */
1621             fgSetWindow( window );
1622
1623             /*
1624              * Have the special callback executed:
1625              */
1626             window->Callbacks.Special( keypress, window->State.MouseX, window->State.MouseY );
1627         }
1628
1629         /*
1630          * Thrash the modifiers register now
1631          */
1632         window->State.Modifiers = 0xffffffff;
1633     }
1634     break;
1635
1636     case WM_SYSKEYUP:
1637     case WM_KEYUP:
1638     {
1639         int keypress = -1;
1640         POINT mouse_pos ;
1641
1642         /*
1643          * Remember the current modifiers state. This is done here in order 
1644          * to make sure the VK_DELETE keyboard callback is executed properly.
1645          */
1646         window->State.Modifiers = 
1647             ( ( (GetKeyState( VK_LSHIFT   ) < 0 ) || ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1648             ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1649             ( ( (GetKeyState( VK_LMENU    ) < 0 ) || ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1650
1651         /*
1652          * Set the mouse position
1653          */
1654         GetCursorPos ( &mouse_pos ) ;
1655         ScreenToClient ( window->Window.Handle, &mouse_pos ) ;
1656
1657         window->State.MouseX = mouse_pos.x ;
1658         window->State.MouseY = mouse_pos.y ;
1659
1660         /*
1661          * Convert the Win32 keystroke codes to GLUTtish way.  "KEY(a,b)" was defined under "WM_KEYDOWN"
1662          */
1663
1664         switch( wParam )
1665         {
1666           /*
1667            * Most of the special characters can be handled automagically...
1668            */
1669           KEY( VK_F1,     GLUT_KEY_F1        ); KEY( VK_F2,     GLUT_KEY_F2        );
1670           KEY( VK_F3,     GLUT_KEY_F3        ); KEY( VK_F4,     GLUT_KEY_F4        );
1671           KEY( VK_F5,     GLUT_KEY_F5        ); KEY( VK_F6,     GLUT_KEY_F6        );
1672           KEY( VK_F7,     GLUT_KEY_F7        ); KEY( VK_F8,     GLUT_KEY_F8        );
1673           KEY( VK_F9,     GLUT_KEY_F9        ); KEY( VK_F10,    GLUT_KEY_F10       );
1674           KEY( VK_F11,    GLUT_KEY_F11       ); KEY( VK_F12,    GLUT_KEY_F12       );
1675           KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   ); KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
1676           KEY( VK_HOME,   GLUT_KEY_HOME      ); KEY( VK_END,    GLUT_KEY_END       );
1677           KEY( VK_LEFT,   GLUT_KEY_LEFT      ); KEY( VK_UP,     GLUT_KEY_UP        );
1678           KEY( VK_RIGHT,  GLUT_KEY_RIGHT     ); KEY( VK_DOWN,   GLUT_KEY_DOWN      );
1679           KEY( VK_INSERT, GLUT_KEY_INSERT    );
1680
1681           /*
1682            * ...yet there is a small exception we need to have handled...
1683            */
1684           case VK_DELETE:
1685             /*
1686              * The delete key should be treated as an ASCII keypress:
1687              */
1688             if( window->Callbacks.KeyboardUp != NULL )
1689             {
1690                 fgSetWindow ( window ) ;
1691                 window->Callbacks.KeyboardUp( 127, window->State.MouseX, window->State.MouseY );
1692             }
1693
1694             break ;
1695           default:
1696             {
1697               /*
1698                * Call the KeyboardUp callback for a regular character if there is one.
1699                */
1700               BYTE state[ 256 ];
1701               WORD code[ 2 ];
1702
1703               GetKeyboardState(state);
1704
1705               if ( ToAscii( wParam, 0, state, code, 0 ) == 1 )
1706                 wParam=code[ 0 ];
1707
1708               if( window->Callbacks.KeyboardUp != NULL )
1709               {
1710                 /*
1711                  * Set the current window
1712                  */
1713                 fgSetWindow( window );
1714
1715                 window->Callbacks.KeyboardUp( (char)wParam, window->State.MouseX, window->State.MouseY );
1716               }
1717             }
1718         }
1719
1720         /*
1721          * Execute the special callback, if present, given the conversion was a success:
1722          */
1723         if( (keypress != -1) && (window->Callbacks.SpecialUp != NULL) )
1724         {
1725             /*
1726              * Set the current window
1727              */
1728             fgSetWindow( window );
1729
1730             /*
1731              * Have the special callback executed:
1732              */
1733             window->Callbacks.SpecialUp( keypress, window->State.MouseX, window->State.MouseY );
1734         }
1735
1736         /*
1737          * Thrash the modifiers register now
1738          */
1739         window->State.Modifiers = 0xffffffff;
1740     }
1741     break;
1742
1743     case WM_SYSCHAR:
1744     case WM_CHAR:
1745     {
1746         /*
1747          * Ignore the automatic key repetition if needed:
1748          */
1749         if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
1750             break;
1751
1752         /*
1753          * Clear to go with the keyboard callback, if registered:
1754          */
1755         if( window->Callbacks.Keyboard != NULL )
1756         {
1757             /*
1758              * Set the current window
1759              */
1760             fgSetWindow( window );
1761
1762             /*
1763              * Remember the current modifiers state
1764              */
1765             window->State.Modifiers = 
1766                 ( ( (GetKeyState( VK_LSHIFT   ) < 0 ) || ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1767                 ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1768                 ( ( (GetKeyState( VK_LMENU    ) < 0 ) || ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1769
1770             /*
1771              * Have the special callback executed:
1772              */
1773             window->Callbacks.Keyboard( (char)wParam, window->State.MouseX, window->State.MouseY );
1774
1775             /*
1776              * Thrash the modifiers register now
1777              */
1778             window->State.Modifiers = 0xffffffff;
1779         }
1780     }
1781     break;
1782
1783     case WM_CAPTURECHANGED :  /* User has finished resizing the window, force a redraw */
1784       if ( window->Callbacks.Display )
1785       {
1786         /*
1787          * Set the current window
1788          */
1789         fgSetWindow( window );
1790
1791         window->Callbacks.Display () ;
1792       }
1793
1794 /*      lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ) ; */
1795       break ;
1796
1797       /*
1798        * Other messages that I have seen and which are not handled already
1799        */
1800     case WM_SETTEXT :  /* 0x000c */
1801       lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );  /* Pass it on to "DefWindowProc" to set the window text */
1802       break ;
1803
1804     case WM_GETTEXT :  /* 0x000d */
1805       /* Ideally we would copy the title of the window into "lParam" */
1806 /*      strncpy ( (char *)lParam, "Window Title", wParam ) ;
1807       lRet = ( wParam > 12 ) ? 12 : wParam ;  */ /* the number of characters copied */
1808       lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1809       break ;
1810
1811     case WM_GETTEXTLENGTH :  /* 0x000e */
1812       /* Ideally we would get the length of the title of the window */
1813       lRet = 12 ;  /* the number of characters in "Window Title\0" (see above) */
1814       break ;
1815
1816     case WM_ERASEBKGND :  /* 0x0014 */
1817       lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1818       break ;
1819
1820     case WM_SYNCPAINT :  /* 0x0088 */
1821       /* Another window has moved, need to update this one */
1822       window->State.Redisplay = TRUE ;
1823       lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );  /* Help screen says this message must be passed to "DefWindowProc" */
1824       break ;
1825
1826     case WM_NCPAINT :  /* 0x0085 */
1827       /* Need to update the border of this window */
1828       lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );  /* Pass it on to "DefWindowProc" to repaint a standard border */
1829       break ;
1830
1831     default:
1832         /*
1833          * Handle unhandled messages
1834          */
1835         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1836         break;
1837     }
1838
1839     return( lRet );
1840 }
1841 #endif
1842
1843 /*** END OF FILE ***/