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