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