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