Forgot to set {direction} explicitly to +/- 1 for freeglut mouse-wheel
[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
815          * window
816          */
817         if ( ( window->Callbacks.Mouse == NULL ) &&
818              ( window->Callbacks.MouseWheel == NULL ) )
819           break;
820
821         fgSetWindow ( window );
822
823         modifiers = 0;
824         if (event.xbutton.state & (ShiftMask|LockMask))
825           modifiers |= GLUT_ACTIVE_SHIFT;
826         if (event.xbutton.state & ControlMask)
827           modifiers |= GLUT_ACTIVE_CTRL;
828         if (event.xbutton.state & Mod1Mask)
829           modifiers |= GLUT_ACTIVE_ALT;
830         fgStructure.Window->State.Modifiers = modifiers;
831
832         /*
833          * Finally execute the mouse or mouse wheel callback
834          *
835          * XXX Use a symbolic constant, *not* "4"!
836          */
837         if ( button < 4 )
838         {
839           fgStructure.Window->Callbacks.Mouse(
840               button,
841               event.type == ButtonPress ? GLUT_DOWN : GLUT_UP,
842               event.xbutton.x,
843               event.xbutton.y
844           );
845         }
846         else
847         {
848           if ( window->Callbacks.MouseWheel )
849           {
850               /*
851                * Map 4 and 5 to wheel zero; EVEN to +1, ODD to -1
852                *  "  6 and 7 "    "   one; ...
853                *
854                * XXX This *should* be behind some variables/macros,
855                * XXX since the order and numbering isn't certain
856                * XXX See XFree86 configuration docs (even back in the
857                * XXX 3.x days, and especially with 4.x).
858                */
859             int wheel_number = (button - 4) / 2;
860             int direction = (button & 1)*2 - 1;
861
862             if( ButtonPress )
863                 fgStructure.Window->Callbacks.MouseWheel(
864                     wheel_number,
865                     direction,
866                     event.xbutton.x,
867                     event.xbutton.y
868                 );
869           }
870           else
871           {
872             fgStructure.Window->Callbacks.Mouse(
873                 button,
874                 event.type == ButtonPress ? GLUT_DOWN : GLUT_UP,
875                 event.xbutton.x,
876                 event.xbutton.y
877                 );
878           }
879         }
880
881         /*
882          * Trash the modifiers state
883          */
884         fgStructure.Window->State.Modifiers = 0xffffffff;
885       }
886       break;
887
888     case KeyRelease:
889     case KeyPress:
890       {
891         FGCBkeyboard keyboard_cb;
892         FGCBspecial special_cb;
893
894         /*
895          * A key has been pressed, find the window that had the focus:
896          */
897         GETWINDOW( xkey );
898         GETMOUSE( xkey );
899
900         if( event.type == KeyPress )
901         {
902           keyboard_cb = window->Callbacks.Keyboard;
903           special_cb = window->Callbacks.Special;
904         }
905         else
906         {
907           keyboard_cb = window->Callbacks.KeyboardUp;
908           special_cb = window->Callbacks.SpecialUp;
909         }
910
911         /*
912          * Is there a keyboard/special callback hooked for this window?
913          */
914         if( (keyboard_cb != NULL) || (special_cb != NULL) )
915         {
916           XComposeStatus composeStatus;
917           char asciiCode[ 32 ];
918           KeySym keySym;
919           int len;
920
921           /*
922            * Check for the ASCII/KeySym codes associated with the event:
923            */
924           len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode), &keySym, &composeStatus );
925
926           /*
927            * GLUT API tells us to have two separate callbacks...
928            */
929           if( len > 0 )
930           {
931             /*
932              * ...one for the ASCII translateable keypresses...
933              */
934             if( keyboard_cb != NULL )
935             {
936               /*
937                * Set the current window
938                */
939               fgSetWindow( window );
940
941               /*
942                * Remember the current modifiers state
943                */
944               modifiers = 0;
945               if (event.xkey.state & (ShiftMask|LockMask))
946                   modifiers |= GLUT_ACTIVE_SHIFT;
947               if (event.xkey.state & ControlMask)
948                   modifiers |= GLUT_ACTIVE_CTRL;
949               if (event.xkey.state & Mod1Mask)
950                   modifiers |= GLUT_ACTIVE_ALT;
951               window->State.Modifiers = modifiers;
952
953               /*
954                * Execute the callback
955                */
956               keyboard_cb( asciiCode[ 0 ], event.xkey.x, event.xkey.y );
957
958               /*
959                * Trash the modifiers state
960                */
961               window->State.Modifiers = 0xffffffff;
962             }
963           }
964           else
965           {
966             int special = -1;
967
968             /*
969              * ...and one for all the others, which need to be translated to GLUT_KEY_Xs...
970              */
971             switch( keySym )
972             {
973             /*
974              * First the function keys come:
975              */
976             case XK_F1:     special = GLUT_KEY_F1;     break;
977             case XK_F2:     special = GLUT_KEY_F2;     break;
978             case XK_F3:     special = GLUT_KEY_F3;     break;
979             case XK_F4:     special = GLUT_KEY_F4;     break;
980             case XK_F5:     special = GLUT_KEY_F5;     break;
981             case XK_F6:     special = GLUT_KEY_F6;     break;
982             case XK_F7:     special = GLUT_KEY_F7;     break;
983             case XK_F8:     special = GLUT_KEY_F8;     break;
984             case XK_F9:     special = GLUT_KEY_F9;     break;
985             case XK_F10:    special = GLUT_KEY_F10;    break;
986             case XK_F11:    special = GLUT_KEY_F11;    break;
987             case XK_F12:    special = GLUT_KEY_F12;    break;
988
989             /*
990              * Then the arrows and stuff:
991              */
992             case XK_Left:   special = GLUT_KEY_LEFT;   break;
993             case XK_Right:  special = GLUT_KEY_RIGHT;  break;
994             case XK_Up:     special = GLUT_KEY_UP;     break;
995             case XK_Down:   special = GLUT_KEY_DOWN;   break;
996
997             case XK_KP_Prior:
998             case XK_Prior:  special = GLUT_KEY_PAGE_UP; break;
999             case XK_KP_Next:
1000             case XK_Next:   special = GLUT_KEY_PAGE_DOWN; break;
1001             case XK_KP_Home:
1002             case XK_Home:   special = GLUT_KEY_HOME;   break;
1003             case XK_KP_End:
1004             case XK_End:    special = GLUT_KEY_END;    break;
1005             case XK_KP_Insert:
1006             case XK_Insert: special = GLUT_KEY_INSERT; break;
1007             }
1008
1009             /*
1010              * Execute the callback (if one has been specified),
1011              * given that the special code seems to be valid...
1012              */
1013             if( (special_cb != NULL) && (special != -1) )
1014             {
1015               fgSetWindow( window );
1016
1017               /*
1018                * Remember the current modifiers state
1019                */
1020               modifiers = 0;
1021               if (event.xkey.state & (ShiftMask|LockMask))
1022                 modifiers |= GLUT_ACTIVE_SHIFT;
1023               if (event.xkey.state & ControlMask)
1024                 modifiers |= GLUT_ACTIVE_CTRL;
1025               if (event.xkey.state & Mod1Mask)
1026                 modifiers |= GLUT_ACTIVE_ALT;
1027               window->State.Modifiers = modifiers;
1028
1029               special_cb( special, event.xkey.x, event.xkey.y );
1030
1031               /*
1032                * Trash the modifiers state
1033                */
1034               window->State.Modifiers = 0xffffffff;
1035             }
1036           }
1037         }
1038       }
1039       break;
1040     }
1041   }
1042
1043 #elif TARGET_HOST_WIN32
1044
1045   MSG stMsg;
1046
1047   while ( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
1048   {
1049     if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
1050       fgState.ExecState = GLUT_EXEC_STATE_STOP ;
1051
1052     TranslateMessage( &stMsg );
1053     DispatchMessage( &stMsg );
1054   }
1055 #endif
1056
1057   fghCheckTimers();
1058   fghCheckJoystickPolls();
1059   fghDisplayAll();
1060
1061   fgCloseWindows () ;
1062 }
1063
1064 /*
1065  * Enters the freeglut processing loop. Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
1066  */
1067 void FGAPIENTRY glutMainLoop( void )
1068 {
1069 #if TARGET_HOST_WIN32
1070   SFG_Window *window = (SFG_Window *)fgStructure.Windows.First ;
1071 #endif
1072
1073   freeglut_assert_ready;
1074
1075 #if TARGET_HOST_WIN32
1076   /*
1077    * Processing before the main loop:  If there is a window which is open and
1078    * which has a visibility callback, call it.  I know this is an ugly hack,
1079    * but I'm not sure what else to do about it.  Ideally we should leave
1080    * something uninitialized in the create window code and initialize it in
1081    * the main loop, and have that initialization create a "WM_ACTIVATE"
1082    * message.  Then we would put the visibility callback code in the
1083    * "case WM_ACTIVATE" block below.         - John Fay -- 10/24/02
1084    */
1085   while ( window != NULL )
1086   {
1087     if ( window->Callbacks.Visibility != NULL )
1088     {
1089       SFG_Window *current_window = fgStructure.Window ;
1090
1091       fgSetWindow( window );
1092       window->Callbacks.Visibility ( window->State.Visible ) ;
1093       fgSetWindow( current_window );
1094     }
1095
1096     window = (SFG_Window *)window->Node.Next ;
1097   }
1098 #endif
1099
1100   fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
1101   while ( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
1102   {
1103     glutMainLoopEvent () ;
1104
1105     if ( fgStructure.Windows.First == NULL )
1106       fgState.ExecState = GLUT_EXEC_STATE_STOP ;
1107     else
1108     {
1109       if ( fgState.IdleCallback )
1110         fgState.IdleCallback ();
1111
1112       fgSleepForEvents();
1113     }
1114   }
1115
1116   {
1117     fgExecutionState execState = fgState.ActionOnWindowClose;
1118
1119     /*
1120      * When this loop terminates, destroy the display, state and structure
1121      * of a freeglut session, so that another glutInit() call can happen
1122      */
1123     fgDeinitialize();
1124
1125     if ( execState == GLUT_ACTION_EXIT )
1126       exit ( 0 ) ;
1127   }
1128 }
1129
1130 /*
1131  * Leaves the freeglut processing loop.
1132  */
1133 void FGAPIENTRY glutLeaveMainLoop( void )
1134 {
1135   fgState.ExecState = GLUT_EXEC_STATE_STOP ;
1136 }
1137
1138 /*
1139  * The window procedure for handling Win32 events
1140  */
1141 #if TARGET_HOST_WIN32
1142 LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
1143 {
1144     SFG_Window* window = fgWindowByHandle( hWnd );
1145     PAINTSTRUCT ps;
1146     LONG lRet = 1;
1147
1148     if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
1149       return( DefWindowProc( hWnd, uMsg, wParam, lParam ) );
1150
1151 /*    printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0, uMsg, wParam, lParam ) ; */
1152     switch( uMsg )
1153     {
1154     case WM_CREATE:
1155         /*
1156          * The window structure is passed as the creation structure paramter...
1157          */
1158         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
1159         assert( window != NULL );
1160
1161         window->Window.Handle = hWnd;
1162         window->Window.Device = GetDC( hWnd );
1163         if ( fgState.BuildingAMenu )
1164         {
1165           unsigned int current_DisplayMode = fgState.DisplayMode ;
1166           fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH ;
1167           fgSetupPixelFormat( window, FALSE, PFD_MAIN_PLANE );
1168           fgState.DisplayMode = current_DisplayMode ;
1169
1170           if ( !fgStructure.MenuContext )
1171           {
1172             fgStructure.MenuContext = (SFG_MenuContext *)malloc ( sizeof(SFG_MenuContext) ) ;
1173             fgStructure.MenuContext->Context = wglCreateContext( window->Window.Device );
1174           }
1175           else
1176             wglMakeCurrent ( window->Window.Device, fgStructure.MenuContext->Context ) ;
1177
1178 /*          window->Window.Context = wglGetCurrentContext () ;   */
1179           window->Window.Context = wglCreateContext( window->Window.Device );
1180         }
1181         else
1182         {
1183           fgSetupPixelFormat( window, FALSE, PFD_MAIN_PLANE );
1184
1185           if ( fgState.UseCurrentContext == TRUE )
1186           {
1187             window->Window.Context = wglGetCurrentContext();
1188             if ( ! window->Window.Context )
1189               window->Window.Context = wglCreateContext( window->Window.Device );
1190           }
1191           else
1192             window->Window.Context = wglCreateContext( window->Window.Device );
1193         }
1194
1195         window->State.NeedToResize = TRUE;
1196         ReleaseDC( window->Window.Handle, window->Window.Device );
1197         break;
1198
1199     case WM_SIZE:
1200         /*
1201          * We got resized... But check if the window has been already added...
1202          */
1203         fghReshapeWindowByHandle( hWnd, LOWORD(lParam), HIWORD(lParam) );
1204         break;
1205 #if 0
1206     case WM_SETFOCUS: 
1207         printf("WM_SETFOCUS: %p\n", window );
1208         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1209         break;
1210
1211     case WM_ACTIVATE: 
1212         if (LOWORD(wParam) != WA_INACTIVE)
1213         {
1214           /* glutSetCursor( fgStructure.Window->State.Cursor ); */
1215                 printf("WM_ACTIVATE: glutSetCursor( %p, %d)\n", window, window->State.Cursor );
1216
1217           glutSetCursor( window->State.Cursor );
1218         }
1219
1220         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1221         break;
1222 #endif
1223
1224     case WM_SETCURSOR: 
1225         /*
1226          * Windows seems to need reminding to erase the cursor for NONE.
1227          */
1228 #if 0
1229         if ((LOWORD(lParam) == HTCLIENT) &&
1230             (fgStructure.Window->State.Cursor == GLUT_CURSOR_NONE))
1231           SetCursor( NULL );
1232 #else
1233         /* Set the cursor AND change it for this window class. */
1234 #       define MAP_CURSOR(a,b) case a: SetCursor( LoadCursor( NULL, b ) ); \
1235         break;
1236         /* Nuke the cursor AND change it for this window class. */
1237 #       define ZAP_CURSOR(a,b) case a: SetCursor( NULL ); \
1238         break;
1239
1240         if (LOWORD(lParam) == HTCLIENT)
1241           switch( window->State.Cursor )
1242           {
1243                 MAP_CURSOR( GLUT_CURSOR_RIGHT_ARROW, IDC_ARROW     );
1244                 MAP_CURSOR( GLUT_CURSOR_LEFT_ARROW,  IDC_ARROW     );
1245                 MAP_CURSOR( GLUT_CURSOR_INFO,        IDC_HELP      );
1246                 MAP_CURSOR( GLUT_CURSOR_DESTROY,     IDC_CROSS     );
1247                 MAP_CURSOR( GLUT_CURSOR_HELP,        IDC_HELP      );
1248                 MAP_CURSOR( GLUT_CURSOR_CYCLE,       IDC_SIZEALL   );
1249                 MAP_CURSOR( GLUT_CURSOR_SPRAY,       IDC_CROSS     );
1250                 MAP_CURSOR( GLUT_CURSOR_WAIT,            IDC_WAIT      );
1251                 MAP_CURSOR( GLUT_CURSOR_TEXT,        IDC_UPARROW   );
1252                 MAP_CURSOR( GLUT_CURSOR_CROSSHAIR,   IDC_CROSS     );
1253                 /* MAP_CURSOR( GLUT_CURSOR_NONE,        IDC_NO             ); */
1254                 ZAP_CURSOR( GLUT_CURSOR_NONE,        NULL          );
1255
1256                 default:
1257                 MAP_CURSOR( GLUT_CURSOR_UP_DOWN,     IDC_ARROW     );
1258           }
1259 #endif
1260         else
1261           lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1262         break;
1263
1264     case WM_SHOWWINDOW:
1265         window->State.Visible = TRUE;
1266         window->State.Redisplay = TRUE;
1267         break;
1268
1269     case WM_PAINT:
1270         BeginPaint( hWnd, &ps );
1271         fghRedrawWindowByHandle( hWnd );
1272         EndPaint( hWnd, &ps );
1273         break;
1274
1275     case WM_CLOSE:
1276         /*
1277          * Make sure we don't close a window with current context active
1278          */
1279         if( fgStructure.Window == window )
1280         {
1281           int used = FALSE ;
1282           SFG_Window *iter ;
1283
1284             wglMakeCurrent( NULL, NULL );
1285             /* Step through the list of windows.  If the rendering context is not being used
1286              * by another window, then we delete it.
1287              */
1288             for ( iter = (SFG_Window *)fgStructure.Windows.First; iter; iter = (SFG_Window *)iter->Node.Next )
1289             {
1290               if ( ( iter->Window.Context == window->Window.Context ) && ( iter != window ) )
1291                 used = TRUE ;
1292             }
1293
1294             if ( used == FALSE ) wglDeleteContext( window->Window.Context );
1295         }
1296
1297         /*
1298          * Put on a linked list of windows to be removed after all the callbacks have returned
1299          */
1300         fgAddToWindowDestroyList ( window, FALSE ) ;
1301         DestroyWindow( hWnd );
1302         break;
1303
1304     case WM_DESTROY:
1305         /*
1306          * The window already got destroyed, so don't bother with it.
1307          */
1308         return( 0 );
1309
1310     case WM_MOUSEMOVE:
1311     {
1312         window->State.MouseX = LOWORD( lParam );
1313         window->State.MouseY = HIWORD( lParam );
1314
1315         if ( window->ActiveMenu != NULL )
1316         {
1317             window->State.Redisplay = TRUE ;
1318
1319             /*
1320              * Since the window is a menu, make the parent window current
1321              */
1322             fgSetWindow ( window->ActiveMenu->ParentWindow ) ;
1323
1324             break;
1325         }
1326
1327         window->State.Modifiers = 
1328             ( ( (GetKeyState( VK_LSHIFT   ) < 0 ) || ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1329             ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1330             ( ( (GetKeyState( VK_LMENU    ) < 0 ) || ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1331
1332         if( (wParam & MK_LBUTTON) || (wParam & MK_MBUTTON) || (wParam & MK_RBUTTON) )
1333         {
1334             if( window->Callbacks.Motion != NULL )
1335             {
1336                 fgSetWindow( window );
1337                 window->Callbacks.Motion( window->State.MouseX, window->State.MouseY );
1338             }
1339         }
1340         else
1341         {
1342             if( window->Callbacks.Passive != NULL )
1343             {
1344                 fgSetWindow( window );
1345                 window->Callbacks.Passive( window->State.MouseX, window->State.MouseY );
1346             }
1347         }
1348
1349         window->State.Modifiers = 0xffffffff;
1350     }
1351     break;
1352
1353     case WM_LBUTTONDOWN:
1354     case WM_MBUTTONDOWN:
1355     case WM_RBUTTONDOWN:
1356     case WM_LBUTTONUP:
1357     case WM_MBUTTONUP:
1358     case WM_RBUTTONUP:
1359     {
1360         GLboolean pressed = TRUE;
1361         int button;
1362
1363         window->State.MouseX = LOWORD( lParam );
1364         window->State.MouseY = HIWORD( lParam );
1365
1366         switch( uMsg )
1367         {
1368         case WM_LBUTTONDOWN: pressed = TRUE;  button = GLUT_LEFT_BUTTON;   break;
1369         case WM_MBUTTONDOWN: pressed = TRUE;  button = GLUT_MIDDLE_BUTTON; break;
1370         case WM_RBUTTONDOWN: pressed = TRUE;  button = GLUT_RIGHT_BUTTON;  break;
1371         case WM_LBUTTONUP:   pressed = FALSE; button = GLUT_LEFT_BUTTON;   break;
1372         case WM_MBUTTONUP:   pressed = FALSE; button = GLUT_MIDDLE_BUTTON; break;
1373         case WM_RBUTTONUP:   pressed = FALSE; button = GLUT_RIGHT_BUTTON;  break;
1374         default:             pressed = FALSE; button = -1;                 break;
1375         }
1376
1377         if( GetSystemMetrics( SM_SWAPBUTTON ) )
1378             if( button == GLUT_LEFT_BUTTON ) button = GLUT_RIGHT_BUTTON;
1379             else if( button == GLUT_RIGHT_BUTTON ) button = GLUT_LEFT_BUTTON;
1380
1381         if( button == -1 )
1382             return( DefWindowProc( hWnd, uMsg, lParam, wParam ) );
1383
1384         /*
1385          * Do not execute the application's mouse callback if a menu is hooked to this button.
1386          * In that case an appropriate private call should be generated.
1387          * Near as I can tell, this is the menu behaviour:
1388          *  - Down-click the menu button, menu not active:  activate the menu with its upper left-hand corner at the mouse location.
1389          *  - Down-click any button outside the menu, menu active:  deactivate the menu
1390          *  - Down-click any button inside the menu, menu active:  select the menu entry and deactivate the menu
1391          *  - Up-click the menu button, menu not active:  nothing happens
1392          *  - Up-click the menu button outside the menu, menu active:  nothing happens
1393          *  - Up-click the menu button inside the menu, menu active:  select the menu entry and deactivate the menu
1394          */
1395         if ( window->ActiveMenu != NULL )  /* Window has an active menu, it absorbs any mouse click */
1396         {
1397           if ( fgCheckActiveMenu ( window, window->ActiveMenu ) == TRUE )  /* Inside the menu, invoke the callback and deactivate the menu*/
1398           {
1399             /* Save the current window and menu and set the current window to the window whose menu this is */
1400             SFG_Window *save_window = fgStructure.Window ;
1401             SFG_Menu *save_menu = fgStructure.Menu ;
1402             SFG_Window *parent_window = window->ActiveMenu->ParentWindow ;
1403             fgSetWindow ( parent_window ) ;
1404             fgStructure.Menu = window->ActiveMenu ;
1405
1406             /* Execute the menu callback */
1407             fgExecuteMenuCallback ( window->ActiveMenu ) ;
1408             fgDeactivateMenu ( parent_window ) ;
1409
1410             /* Restore the current window and menu */
1411             fgSetWindow ( save_window ) ;
1412             fgStructure.Menu = save_menu ;
1413           }
1414           else  /* Outside the menu, deactivate the menu if it's a downclick */
1415           {
1416             if ( pressed == TRUE ) fgDeactivateMenu ( window->ActiveMenu->ParentWindow ) ;
1417           }
1418
1419           /*
1420            * Let's make the window redraw as a result of the mouse click and menu activity.
1421            */
1422           if ( ! window->IsMenu ) window->State.Redisplay = TRUE ;
1423
1424           break ;
1425         }
1426
1427         if ( ( window->Menu[ button ] != NULL ) && ( pressed == TRUE ) )
1428         {
1429             window->State.Redisplay = TRUE ;
1430             fgSetWindow( window );
1431             fgActivateMenu( window, button );
1432
1433             break;
1434         }
1435
1436         if( window->Callbacks.Mouse == NULL )
1437             break;
1438
1439         fgSetWindow ( window );
1440
1441         fgStructure.Window->State.Modifiers = 
1442             ( ( (GetKeyState( VK_LSHIFT   ) < 0 ) || ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1443             ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1444             ( ( (GetKeyState( VK_LMENU    ) < 0 ) || ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1445
1446         window->Callbacks.Mouse(
1447             button,
1448             pressed == TRUE ? GLUT_DOWN : GLUT_UP,
1449             window->State.MouseX,
1450             window->State.MouseY
1451         );
1452
1453         fgStructure.Window->State.Modifiers = 0xffffffff;
1454     }
1455     break;
1456
1457     case 0x020a:
1458         /* Should be WM_MOUSEWHEEL but my compiler doesn't recognize it */
1459       {
1460         int wheel_number = LOWORD ( lParam ) ;
1461         /* THIS IS SPECULATIVE -- John Fay, 10/2/03 */
1462         int ticks = HIWORD ( lParam ) / 120 ;
1463         /* Should be WHEEL_DELTA instead of 120 */
1464         int direction = 1;
1465
1466         if( ticks < 0 )
1467         {
1468             direction = -1;
1469             ticks = -ticks;
1470         }
1471
1472         /*
1473          * The mouse cursor has moved. Remember the new mouse cursor's position
1474          */
1475         /*        window->State.MouseX = LOWORD( lParam ); */
1476         /* Need to adjust by window position, */
1477         /*        window->State.MouseY = HIWORD( lParam ); */
1478         /* change "lParam" to other parameter */
1479
1480         if ( ( window->Callbacks.MouseWheel == NULL ) &&
1481              ( window->Callbacks.Mouse == NULL ) )
1482             break;
1483
1484         fgSetWindow ( window );
1485         fgStructure.Window->State.Modifiers = 
1486             ( ( (GetKeyState( VK_LSHIFT   ) < 0 ) ||
1487                 ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1488             ( ( (GetKeyState( VK_LCONTROL ) < 0 ) ||
1489                 ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1490             ( ( (GetKeyState( VK_LMENU    ) < 0 ) ||
1491                 ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1492
1493         while( ticks-- )
1494             if ( window->Callbacks.MouseWheel )
1495                 window->Callbacks.MouseWheel(
1496                     wheel_number,
1497                     direction,
1498                     window->State.MouseX,
1499                     window->State.MouseY
1500                 ) ;
1501             else  /* No mouse wheel, call the mouse button callback twice */
1502             {
1503                 /*
1504                  * XXX The below assumes that you have no more than 3 mouse
1505                  * XXX buttons.  Sorry.
1506                  */
1507                 int button = wheel_number * 2 + 4;
1508                 button += (1 + direction)/2;
1509                 window->Callbacks.Mouse ( button, GLUT_DOWN,
1510                                           window->State.MouseX,
1511                                           window->State.MouseY ) ;
1512                 window->Callbacks.Mouse ( button, GLUT_UP,
1513                                           window->State.MouseX,
1514                                           window->State.MouseY ) ;
1515             }
1516
1517         fgStructure.Window->State.Modifiers = 0xffffffff;
1518       }
1519       break ;
1520
1521     case WM_SYSKEYDOWN:
1522     case WM_KEYDOWN:
1523     {
1524         int keypress = -1;
1525         POINT mouse_pos ;
1526
1527         if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
1528             break;
1529
1530         /*
1531          * Remember the current modifiers state. This is done here in order 
1532          * to make sure the VK_DELETE keyboard callback is executed properly.
1533          */
1534         window->State.Modifiers = 
1535             ( ( (GetKeyState( VK_LSHIFT   ) < 0 ) || ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1536             ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1537             ( ( (GetKeyState( VK_LMENU    ) < 0 ) || ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1538
1539         GetCursorPos ( &mouse_pos ) ;
1540         ScreenToClient ( window->Window.Handle, &mouse_pos ) ;
1541
1542         window->State.MouseX = mouse_pos.x ;
1543         window->State.MouseY = mouse_pos.y ;
1544
1545         /*
1546          * Convert the Win32 keystroke codes to GLUTtish way
1547          */
1548 #       define KEY(a,b) case a: keypress = b; break;
1549
1550         switch( wParam )
1551         {
1552             KEY( VK_F1,     GLUT_KEY_F1        ); KEY( VK_F2,     GLUT_KEY_F2        );
1553             KEY( VK_F3,     GLUT_KEY_F3        ); KEY( VK_F4,     GLUT_KEY_F4        );
1554             KEY( VK_F5,     GLUT_KEY_F5        ); KEY( VK_F6,     GLUT_KEY_F6        );
1555             KEY( VK_F7,     GLUT_KEY_F7        ); KEY( VK_F8,     GLUT_KEY_F8        );
1556             KEY( VK_F9,     GLUT_KEY_F9        ); KEY( VK_F10,    GLUT_KEY_F10       );
1557             KEY( VK_F11,    GLUT_KEY_F11       ); KEY( VK_F12,    GLUT_KEY_F12       );
1558             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   ); KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
1559             KEY( VK_HOME,   GLUT_KEY_HOME      ); KEY( VK_END,    GLUT_KEY_END       );
1560             KEY( VK_LEFT,   GLUT_KEY_LEFT      ); KEY( VK_UP,     GLUT_KEY_UP        );
1561             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     ); KEY( VK_DOWN,   GLUT_KEY_DOWN      );
1562             KEY( VK_INSERT, GLUT_KEY_INSERT    );
1563
1564         case VK_DELETE:
1565                 /*
1566                  * The delete key should be treated as an ASCII keypress:
1567                  */
1568                 if( window->Callbacks.Keyboard != NULL )
1569                 {
1570                     fgSetWindow( window );
1571                     window->Callbacks.Keyboard( 127, window->State.MouseX, window->State.MouseY );
1572                 }
1573         }
1574
1575         if( (keypress != -1) && (window->Callbacks.Special != NULL) )
1576         {
1577             fgSetWindow( window );
1578             window->Callbacks.Special( keypress, window->State.MouseX, window->State.MouseY );
1579         }
1580
1581         window->State.Modifiers = 0xffffffff;
1582     }
1583     break;
1584
1585     case WM_SYSKEYUP:
1586     case WM_KEYUP:
1587     {
1588         int keypress = -1;
1589         POINT mouse_pos ;
1590
1591         /*
1592          * Remember the current modifiers state. This is done here in order 
1593          * to make sure the VK_DELETE keyboard callback is executed properly.
1594          */
1595         window->State.Modifiers = 
1596             ( ( (GetKeyState( VK_LSHIFT   ) < 0 ) || ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1597             ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1598             ( ( (GetKeyState( VK_LMENU    ) < 0 ) || ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1599
1600         GetCursorPos ( &mouse_pos ) ;
1601         ScreenToClient ( window->Window.Handle, &mouse_pos ) ;
1602
1603         window->State.MouseX = mouse_pos.x ;
1604         window->State.MouseY = mouse_pos.y ;
1605
1606         /*
1607          * Convert the Win32 keystroke codes to GLUTtish way.  "KEY(a,b)" was defined under "WM_KEYDOWN"
1608          */
1609
1610         switch( wParam )
1611         {
1612           KEY( VK_F1,     GLUT_KEY_F1        ); KEY( VK_F2,     GLUT_KEY_F2        );
1613           KEY( VK_F3,     GLUT_KEY_F3        ); KEY( VK_F4,     GLUT_KEY_F4        );
1614           KEY( VK_F5,     GLUT_KEY_F5        ); KEY( VK_F6,     GLUT_KEY_F6        );
1615           KEY( VK_F7,     GLUT_KEY_F7        ); KEY( VK_F8,     GLUT_KEY_F8        );
1616           KEY( VK_F9,     GLUT_KEY_F9        ); KEY( VK_F10,    GLUT_KEY_F10       );
1617           KEY( VK_F11,    GLUT_KEY_F11       ); KEY( VK_F12,    GLUT_KEY_F12       );
1618           KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   ); KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
1619           KEY( VK_HOME,   GLUT_KEY_HOME      ); KEY( VK_END,    GLUT_KEY_END       );
1620           KEY( VK_LEFT,   GLUT_KEY_LEFT      ); KEY( VK_UP,     GLUT_KEY_UP        );
1621           KEY( VK_RIGHT,  GLUT_KEY_RIGHT     ); KEY( VK_DOWN,   GLUT_KEY_DOWN      );
1622           KEY( VK_INSERT, GLUT_KEY_INSERT    );
1623
1624           case VK_DELETE:
1625             /*
1626              * The delete key should be treated as an ASCII keypress:
1627              */
1628             if( window->Callbacks.KeyboardUp != NULL )
1629             {
1630                 fgSetWindow ( window ) ;
1631                 window->Callbacks.KeyboardUp( 127, window->State.MouseX, window->State.MouseY );
1632             }
1633
1634             break ;
1635           default:
1636             {
1637               BYTE state[ 256 ];
1638               WORD code[ 2 ];
1639
1640               GetKeyboardState(state);
1641
1642               if ( ToAscii( wParam, 0, state, code, 0 ) == 1 )
1643                 wParam=code[ 0 ];
1644
1645               if( window->Callbacks.KeyboardUp != NULL )
1646               {
1647                 fgSetWindow( window );
1648                 window->Callbacks.KeyboardUp( (char)wParam, window->State.MouseX, window->State.MouseY );
1649               }
1650             }
1651         }
1652
1653         if( (keypress != -1) && (window->Callbacks.SpecialUp != NULL) )
1654         {
1655             fgSetWindow( window );
1656             window->Callbacks.SpecialUp( keypress, window->State.MouseX, window->State.MouseY );
1657         }
1658
1659         window->State.Modifiers = 0xffffffff;
1660     }
1661     break;
1662
1663     case WM_SYSCHAR:
1664     case WM_CHAR:
1665     {
1666         if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
1667             break;
1668
1669         if( window->Callbacks.Keyboard != NULL )
1670         {
1671             fgSetWindow( window );
1672             window->State.Modifiers = 
1673                 ( ( (GetKeyState( VK_LSHIFT   ) < 0 ) || ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1674                 ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1675                 ( ( (GetKeyState( VK_LMENU    ) < 0 ) || ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1676
1677             window->Callbacks.Keyboard( (char)wParam, window->State.MouseX, window->State.MouseY );
1678             window->State.Modifiers = 0xffffffff;
1679         }
1680     }
1681     break;
1682
1683     case WM_CAPTURECHANGED :  /* User has finished resizing the window, force a redraw */
1684       if ( window->Callbacks.Display )
1685       {
1686         fgSetWindow( window );
1687
1688         window->Callbacks.Display () ;
1689       }
1690
1691 /*      lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ) ; */
1692       break ;
1693
1694       /*
1695        * Other messages that I have seen and which are not handled already
1696        */
1697     case WM_SETTEXT :  /* 0x000c */
1698       lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );  /* Pass it on to "DefWindowProc" to set the window text */
1699       break ;
1700
1701     case WM_GETTEXT :  /* 0x000d */
1702       /* Ideally we would copy the title of the window into "lParam" */
1703 /*      strncpy ( (char *)lParam, "Window Title", wParam ) ;
1704       lRet = ( wParam > 12 ) ? 12 : wParam ;  */ /* the number of characters copied */
1705       lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1706       break ;
1707
1708     case WM_GETTEXTLENGTH :  /* 0x000e */
1709       /* Ideally we would get the length of the title of the window */
1710       lRet = 12 ;  /* the number of characters in "Window Title\0" (see above) */
1711       break ;
1712
1713     case WM_ERASEBKGND :  /* 0x0014 */
1714       lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1715       break ;
1716
1717     case WM_SYNCPAINT :  /* 0x0088 */
1718       /* Another window has moved, need to update this one */
1719       window->State.Redisplay = TRUE ;
1720       lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );  /* Help screen says this message must be passed to "DefWindowProc" */
1721       break ;
1722
1723     case WM_NCPAINT :  /* 0x0085 */
1724       /* Need to update the border of this window */
1725       lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );  /* Pass it on to "DefWindowProc" to repaint a standard border */
1726       break ;
1727
1728     default:
1729         /*
1730          * Handle unhandled messages
1731          */
1732         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1733         break;
1734     }
1735
1736     return( lRet );
1737 }
1738 #endif
1739
1740 /*** END OF FILE ***/