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