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