Added a "case" for an X event that we turn on but do not use.
[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 ||
413         fgHavePendingRedisplays() )
414         return;
415     socket = ConnectionNumber( fgDisplay.Display );
416     FD_ZERO( &fdset );
417     FD_SET( socket, &fdset );
418     
419     msec = fgNextTimer();
420     if( fgHaveJoystick() )
421         msec = MIN( msec, 10 );
422     
423     wait.tv_sec = msec / 1000;
424     wait.tv_usec = (msec % 1000) * 1000;
425     err = select( socket+1, &fdset, NULL, NULL, &wait );
426
427     if( -1 == err )
428         printf( "freeglut select() error: %d\n", errno );
429     
430 #elif TARGET_HOST_WIN32
431 #endif
432 }
433
434 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
435
436 /*
437  * Executes a single iteration in the freeglut processing loop.
438  */
439 void FGAPIENTRY glutMainLoopEvent( void )
440 {
441 #if TARGET_HOST_UNIX_X11
442     SFG_Window* window;
443     XEvent event;
444     int modifiers;
445
446     /*
447      * This code was repeated constantly, so here it goes into a definition:
448      */
449 #define GETWINDOW(a)                             \
450     window = fgWindowByHandle( event.a.window ); \
451     if( window == NULL )                         \
452         break;
453
454 #define GETMOUSE(a)                              \
455     window->State.MouseX = event.a.x;            \
456     window->State.MouseY = event.a.y;
457
458     freeglut_assert_ready;
459
460     while( XPending( fgDisplay.Display ) )
461     {
462         XNextEvent( fgDisplay.Display, &event );
463
464         switch( event.type )
465         {
466         case DestroyNotify:
467             /*
468              * This is sent to confirm the XDestroyWindow call.
469              * XXX WHY is this commented out?  Should we re-enable it?
470              */
471             /* fgAddToWindowDestroyList ( window, FALSE ); */
472             break;
473
474         case ClientMessage:
475             /*
476              * Destroy the window when the WM_DELETE_WINDOW message arrives
477              */
478             if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.DeleteWindow )
479             {
480                 GETWINDOW( xclient ); 
481
482                 fgCloseWindow ( window ) ;
483                 fgAddToWindowDestroyList ( window, FALSE );
484             }
485             break;
486
487         case MapNotify:
488         case UnmapNotify:
489             /*
490              * If we never do anything with this, can we just not ask to
491              * get these messages?
492              */
493             break;
494
495         case Expose:
496             /*
497              * We are too dumb to process partial exposes...
498              * XXX Well, we could do it.  However, it seems to only
499              * XXX be potentially useful for single-buffered (since
500              * XXX double-buffered does not respect viewport when we
501              * XXX do a buffer-swap).
502              */
503             if( event.xexpose.count == 0 )
504                 fghRedrawWindowByHandle( event.xexpose.window );
505             break;
506
507             /*
508              * CreateNotify causes a configure-event so that sub-windows are
509              * handled compatibly with GLUT.  Otherwise, your sub-windows
510              * (in freeglut only) will not get an initial reshape event,
511              * which can break things.
512              *
513              * XXX NOTE that it is possible that you will more than one Reshape
514              * XXX event for your top-level window, but something like this
515              * XXX appears to be required for compatbility.
516              *
517              * GLUT presumably does this because it generally tries to treat
518              * sub-windows the same as windows.
519              */
520         case CreateNotify:
521         case ConfigureNotify:
522             fghReshapeWindowByHandle(
523                 event.xconfigure.window,
524                 event.xconfigure.width,
525                 event.xconfigure.height
526             );
527             break;
528
529         case MappingNotify:
530             /*
531              * Have the client's keyboard knowledge updated (xlib.ps,
532              * page 206, says that's a good thing to do)
533              */
534             XRefreshKeyboardMapping( (XMappingEvent *) &event );
535             break;
536
537         case VisibilityNotify:
538         {
539             GETWINDOW( xvisibility ); 
540             if( window->Callbacks.WindowStatus == NULL )
541                 break;
542             fgSetWindow( window );
543
544             /*
545              * Sending this event, the X server can notify us that the window
546              * has just acquired one of the three possible visibility states:
547              * VisibilityUnobscured, VisibilityPartiallyObscured or
548              * VisibilityFullyObscured
549              */
550             switch( event.xvisibility.state )
551             {
552             case VisibilityUnobscured:
553                 window->Callbacks.WindowStatus( GLUT_FULLY_RETAINED );
554                 window->State.Visible = TRUE;
555                 break;
556                 
557             case VisibilityPartiallyObscured:
558                 window->Callbacks.WindowStatus( GLUT_PARTIALLY_RETAINED );
559                 window->State.Visible = TRUE;
560                 break;
561                 
562             case VisibilityFullyObscured:
563                 window->Callbacks.WindowStatus( GLUT_FULLY_COVERED );
564                 window->State.Visible = FALSE;
565                 break;
566
567             default:
568                 fgWarning( "Uknown X visibility state: %d",
569                            event.xvisibility.state );
570                 break;
571             }
572         }
573         break;
574
575         case EnterNotify:
576         {
577             GETWINDOW( xcrossing );
578             GETMOUSE( xcrossing );
579             if( window->Callbacks.Entry )
580             {
581                 fgSetWindow( window ) ;
582                 window->Callbacks.Entry( GLUT_ENTERED );
583             }
584         }
585         break;
586
587         case LeaveNotify:
588         {
589             GETWINDOW( xcrossing );
590             GETMOUSE( xcrossing );
591             if( window->Callbacks.Entry )
592             {
593                 fgSetWindow( window ) ;
594                 window->Callbacks.Entry( GLUT_LEFT );
595             }
596         }
597         break;
598
599         case MotionNotify:
600         {
601             GETWINDOW( xmotion );
602             GETMOUSE( xmotion );
603
604             if( window->ActiveMenu )
605             {
606                 if( window == window->ActiveMenu->ParentWindow )
607                 {
608                     window->ActiveMenu->Window->State.MouseX =
609                         event.xmotion.x_root - window->ActiveMenu->X;
610                     window->ActiveMenu->Window->State.MouseY =
611                         event.xmotion.y_root - window->ActiveMenu->Y;
612                 }
613                 window->ActiveMenu->Window->State.Redisplay = TRUE ;
614                 fgSetWindow( window->ActiveMenu->ParentWindow ) ;
615
616                 break;
617             }
618
619             /*
620              * XXX For more than 5 buttons, just check {event.xmotion.state},
621              * XXX rather than a host of bit-masks?
622              */
623             if( (event.xmotion.state & Button1Mask) ||
624                 (event.xmotion.state & Button2Mask) ||
625                 (event.xmotion.state & Button3Mask) ||
626                 (event.xmotion.state & Button4Mask) ||
627                 (event.xmotion.state & Button5Mask) )
628             {
629                 /*
630                  * A mouse button was pressed during the movement...
631                  * Is there a motion callback hooked to the window?
632                  */
633                 if( window->Callbacks.Motion )
634                 {
635                     fgSetWindow ( window ) ;
636                     window->Callbacks.Motion( event.xmotion.x,
637                                               event.xmotion.y );
638                 }
639             }
640             else if( window->Callbacks.Passive )
641             {
642                 fgSetWindow ( window ) ;
643                 window->Callbacks.Passive( event.xmotion.x, event.xmotion.y );
644             }
645         }
646         break;
647
648         case ButtonRelease:
649         case ButtonPress:
650         {
651             GLboolean pressed = TRUE;
652             int button;
653
654             if( event.type == ButtonRelease )
655                 pressed = FALSE ;
656
657             /*
658              * A mouse button has been pressed or released. Traditionally,
659              * break if the window was found within the freeglut structures.
660              */
661             GETWINDOW( xbutton );
662             GETMOUSE( xbutton );
663           
664             /*
665              * An X button (at least in XFree86) is numbered from 1.
666              * A GLUT button is numbered from 0.
667              * Old GLUT passed through buttons other than just the first
668              * three, though it only gave symbolic names and official
669              * support to the first three.
670              */
671             button = event.xbutton.button - 1;
672
673             /*
674              * Do not execute the application's mouse callback if a menu
675              * is hooked to this button.  In that case an appropriate
676              * private call should be generated.
677              * Near as I can tell, this is the menu behaviour:
678              *  - Down-click the menu button, menu not active:  activate
679              *    the menu with its upper left-hand corner at the mouse
680              *    location.
681              *  - Down-click any button outside the menu, menu active:
682              *    deactivate the menu
683              *  - Down-click any button inside the menu, menu active:
684              *    select the menu entry and deactivate the menu
685              *  - Up-click the menu button, menu not active:  nothing happens
686              *  - Up-click the menu button outside the menu, menu active:
687              *    nothing happens
688              *  - Up-click the menu button inside the menu, menu active:
689              *    select the menu entry and deactivate the menu
690              */
691             /* Window has an active menu, it absorbs any mouse click */
692             if( window->ActiveMenu )
693             {
694                 if( window == window->ActiveMenu->ParentWindow )
695                 {
696                     window->ActiveMenu->Window->State.MouseX =
697                         event.xbutton.x_root - window->ActiveMenu->X;
698                     window->ActiveMenu->Window->State.MouseY =
699                         event.xbutton.y_root - window->ActiveMenu->Y;
700                 }
701               
702                 /* In the menu, invoke the callback and deactivate the menu*/
703                 if( fgCheckActiveMenu( window->ActiveMenu->Window,
704                                        window->ActiveMenu ) == TRUE )
705                 {
706                     /*
707                      * Save the current window and menu and set the current
708                      * window to the window whose menu this is
709                      */
710                     SFG_Window *save_window = fgStructure.Window;
711                     SFG_Menu *save_menu = fgStructure.Menu;
712                     SFG_Window *parent_window =
713                         window->ActiveMenu->ParentWindow;
714                     fgSetWindow( parent_window );
715                     fgStructure.Menu = window->ActiveMenu;
716
717                     /* Execute the menu callback */
718                     fgExecuteMenuCallback( window->ActiveMenu );
719                     fgDeactivateMenu( parent_window );
720
721                     /* Restore the current window and menu */
722                     fgSetWindow( save_window );
723                     fgStructure.Menu = save_menu;
724                 }
725                 else if( pressed )
726                     /*
727                      * Outside the menu, deactivate if it's a downclick
728                      * XXX This isn't enough.  A downclick outside of
729                      * XXX the interior of our freeglut windows should also
730                      * XXX deactivate the menu.  This is more complicated.
731                      */
732                     fgDeactivateMenu( window->ActiveMenu->ParentWindow );
733               
734                 window->State.Redisplay = TRUE;
735                 break;
736             }
737
738             /*
739              * No active menu, let's check whether we need to activate one.
740              */
741             if( ( 0 <= button ) &&
742                 ( 2 >= button ) &&
743                 ( window->Menu[ button ] ) &&
744                 pressed )
745             {
746                 window->State.Redisplay = TRUE;
747                 fgSetWindow( window );
748                 fgActivateMenu( window, button );
749                 break;
750             }
751
752             /*
753              * Check if there is a mouse or mouse wheel callback hooked to the
754              * window
755              */
756             if( ( window->Callbacks.Mouse == NULL ) &&
757                 ( window->Callbacks.MouseWheel == NULL ) )
758                 break;
759
760             fgSetWindow( window );
761
762             modifiers = 0;
763             if( event.xbutton.state & ( ShiftMask | LockMask ) )
764                 modifiers |= GLUT_ACTIVE_SHIFT;
765             if( event.xbutton.state & ControlMask )
766                 modifiers |= GLUT_ACTIVE_CTRL;
767             if( event.xbutton.state & Mod1Mask )
768                 modifiers |= GLUT_ACTIVE_ALT;
769             fgStructure.Window->State.Modifiers = modifiers;
770
771             /*
772              * Finally execute the mouse or mouse wheel callback
773              *
774              * XXX Use a symbolic constant, *not* "4"!
775              */
776             if( ( button < 4 ) || ( !( window->Callbacks.MouseWheel ) ) )
777             {
778                 if( window->Callbacks.Mouse )
779                     fgStructure.Window->Callbacks.Mouse(
780                         button,
781                         event.type == ButtonPress ? GLUT_DOWN : GLUT_UP,
782                         event.xbutton.x,
783                         event.xbutton.y
784                     );
785             }
786             else
787             {
788                 if( ( button >= 4 ) && window->Callbacks.MouseWheel )
789                 {
790                     /*
791                      * Map 4 and 5 to wheel zero; EVEN to +1, ODD to -1
792                      *  "  6 and 7 "    "   one; ...
793                      *
794                      * XXX This *should* be behind some variables/macros,
795                      * XXX since the order and numbering isn't certain
796                      * XXX See XFree86 configuration docs (even back in the
797                      * XXX 3.x days, and especially with 4.x).
798                      */
799                     int wheel_number = (button - 4) / 2;
800                     int direction = (button & 1)*2 - 1;
801
802                     if( ButtonPress )
803                         fgStructure.Window->Callbacks.MouseWheel(
804                             wheel_number,
805                             direction,
806                             event.xbutton.x,
807                             event.xbutton.y
808                         );
809                 }
810             }
811
812             /*
813              * Trash the modifiers state
814              */
815             fgStructure.Window->State.Modifiers = 0xffffffff;
816             break;
817         }
818
819         case KeyRelease:
820         case KeyPress:
821         {
822             FGCBkeyboard keyboard_cb;
823             FGCBspecial special_cb;
824
825             GETWINDOW( xkey );
826             GETMOUSE( xkey );
827
828             if( event.type == KeyPress )
829             {
830                 keyboard_cb = window->Callbacks.Keyboard;
831                 special_cb = window->Callbacks.Special;
832             }
833             else
834             {
835                 keyboard_cb = window->Callbacks.KeyboardUp;
836                 special_cb = window->Callbacks.SpecialUp;
837             }
838
839             /*
840              * Is there a keyboard/special callback hooked for this window?
841              */
842             if( keyboard_cb || special_cb )
843             {
844                 XComposeStatus composeStatus;
845                 char asciiCode[ 32 ];
846                 KeySym keySym;
847                 int len;
848
849                 /*
850                  * Check for the ASCII/KeySym codes associated with the event:
851                  */
852                 len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode),
853                                      &keySym, &composeStatus );
854
855                 /*
856                  * GLUT API tells us to have two separate callbacks...
857                  */
858                 if( len > 0 )
859                 {
860                     /*
861                      * ...one for the ASCII translateable keypresses...
862                      */
863                     if( keyboard_cb )
864                     {
865                         fgSetWindow( window );
866
867                         /*
868                          * Remember the current modifiers state
869                          */
870                         modifiers = 0;
871                         if( event.xkey.state & ( ShiftMask | LockMask ) )
872                             modifiers |= GLUT_ACTIVE_SHIFT;
873                         if( event.xkey.state & ControlMask )
874                             modifiers |= GLUT_ACTIVE_CTRL;
875                         if( event.xkey.state & Mod1Mask )
876                             modifiers |= GLUT_ACTIVE_ALT;
877                         window->State.Modifiers = modifiers;
878
879                         keyboard_cb( asciiCode[ 0 ],
880                                      event.xkey.x, event.xkey.y );
881
882                         /*
883                          * Trash the modifiers state
884                          */
885                         window->State.Modifiers = 0xffffffff;
886                     }
887                 }
888                 else
889                 {
890                     int special = -1;
891
892                     /*
893                      * ...and one for all the others, which need to be
894                      * translated to GLUT_KEY_Xs...
895                      */
896                     switch( keySym )
897                     {
898                     case XK_F1:     special = GLUT_KEY_F1;     break;
899                     case XK_F2:     special = GLUT_KEY_F2;     break;
900                     case XK_F3:     special = GLUT_KEY_F3;     break;
901                     case XK_F4:     special = GLUT_KEY_F4;     break;
902                     case XK_F5:     special = GLUT_KEY_F5;     break;
903                     case XK_F6:     special = GLUT_KEY_F6;     break;
904                     case XK_F7:     special = GLUT_KEY_F7;     break;
905                     case XK_F8:     special = GLUT_KEY_F8;     break;
906                     case XK_F9:     special = GLUT_KEY_F9;     break;
907                     case XK_F10:    special = GLUT_KEY_F10;    break;
908                     case XK_F11:    special = GLUT_KEY_F11;    break;
909                     case XK_F12:    special = GLUT_KEY_F12;    break;
910
911                     case XK_Left:   special = GLUT_KEY_LEFT;   break;
912                     case XK_Right:  special = GLUT_KEY_RIGHT;  break;
913                     case XK_Up:     special = GLUT_KEY_UP;     break;
914                     case XK_Down:   special = GLUT_KEY_DOWN;   break;
915
916                     case XK_KP_Prior:
917                     case XK_Prior:  special = GLUT_KEY_PAGE_UP; break;
918                     case XK_KP_Next:
919                     case XK_Next:   special = GLUT_KEY_PAGE_DOWN; break;
920                     case XK_KP_Home:
921                     case XK_Home:   special = GLUT_KEY_HOME;   break;
922                     case XK_KP_End:
923                     case XK_End:    special = GLUT_KEY_END;    break;
924                     case XK_KP_Insert:
925                     case XK_Insert: special = GLUT_KEY_INSERT; break;
926                     }
927
928                     /*
929                      * Execute the callback (if one has been specified),
930                      * given that the special code seems to be valid...
931                      */
932                     if( special_cb && (special != -1) )
933                     {
934                         fgSetWindow( window );
935
936                         /*
937                          * Remember the current modifiers state
938                          */
939                         modifiers = 0;
940                         if( event.xkey.state & ( ShiftMask | LockMask ) )
941                             modifiers |= GLUT_ACTIVE_SHIFT;
942                         if( event.xkey.state & ControlMask )
943                             modifiers |= GLUT_ACTIVE_CTRL;
944                         if( event.xkey.state & Mod1Mask )
945                             modifiers |= GLUT_ACTIVE_ALT;
946                         window->State.Modifiers = modifiers;
947
948                         special_cb( special, event.xkey.x, event.xkey.y );
949
950                         /*
951                          * Trash the modifiers state
952                          */
953                         window->State.Modifiers = 0xffffffff;
954                     }
955                 }
956             }
957         }
958         break;
959
960         default:
961             fgWarning ("Unknown X event type: %d", event.type);
962             break;
963         }
964     }
965
966 #elif TARGET_HOST_WIN32
967
968     MSG stMsg;
969
970     while( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
971     {
972         if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
973             fgState.ExecState = GLUT_EXEC_STATE_STOP ;
974
975         TranslateMessage( &stMsg );
976         DispatchMessage( &stMsg );
977     }
978 #endif
979
980     fghCheckTimers( );
981     fghCheckJoystickPolls( );
982     fghDisplayAll( );
983
984     fgCloseWindows( );
985 }
986
987 /*
988  * Enters the freeglut processing loop.
989  * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
990  */
991 void FGAPIENTRY glutMainLoop( void )
992 {
993 #if TARGET_HOST_WIN32
994     SFG_Window *window = (SFG_Window *)fgStructure.Windows.First ;
995 #endif
996
997     freeglut_assert_ready;
998
999 #if TARGET_HOST_WIN32
1000     /*
1001      * Processing before the main loop:  If there is a window which is open and
1002      * which has a visibility callback, call it.  I know this is an ugly hack,
1003      * but I'm not sure what else to do about it.  Ideally we should leave
1004      * something uninitialized in the create window code and initialize it in
1005      * the main loop, and have that initialization create a "WM_ACTIVATE"
1006      * message.  Then we would put the visibility callback code in the
1007      * "case WM_ACTIVATE" block below.         - John Fay -- 10/24/02
1008      */
1009     while( window )
1010     {
1011         if ( window->Callbacks.Visibility )
1012         {
1013             SFG_Window *current_window = fgStructure.Window ;
1014
1015             fgSetWindow( window );
1016             window->Callbacks.Visibility ( window->State.Visible ) ;
1017             fgSetWindow( current_window );
1018         }
1019         
1020         window = (SFG_Window *)window->Node.Next ;
1021     }
1022 #endif
1023
1024     fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
1025     while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
1026     {
1027         glutMainLoopEvent( );
1028
1029         if( fgStructure.Windows.First == NULL )
1030             fgState.ExecState = GLUT_EXEC_STATE_STOP;
1031         else
1032         {
1033             if( fgState.IdleCallback )
1034                 fgState.IdleCallback( );
1035
1036             fgSleepForEvents();
1037         }
1038     }
1039
1040     {
1041         fgExecutionState execState = fgState.ActionOnWindowClose;
1042         
1043         /*
1044          * When this loop terminates, destroy the display, state and structure
1045          * of a freeglut session, so that another glutInit() call can happen
1046          */
1047         fgDeinitialize( );
1048
1049         if( execState == GLUT_ACTION_EXIT )
1050             exit( 0 ) ;
1051     }
1052 }
1053
1054 /*
1055  * Leaves the freeglut processing loop.
1056  */
1057 void FGAPIENTRY glutLeaveMainLoop( void )
1058 {
1059     fgState.ExecState = GLUT_EXEC_STATE_STOP ;
1060 }
1061
1062 /*
1063  * The window procedure for handling Win32 events
1064  */
1065 #if TARGET_HOST_WIN32
1066 LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
1067                                LPARAM lParam )
1068 {
1069     SFG_Window* window = fgWindowByHandle( hWnd );
1070     PAINTSTRUCT ps;
1071     LONG lRet = 1;
1072
1073     if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
1074       return( DefWindowProc( hWnd, uMsg, wParam, lParam ) );
1075
1076     /* printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0,
1077              uMsg, wParam, lParam ); */
1078     switch( uMsg )
1079     {
1080     case WM_CREATE:
1081         /*
1082          * The window structure is passed as the creation structure paramter...
1083          */
1084         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
1085         assert( window != NULL );
1086
1087         window->Window.Handle = hWnd;
1088         window->Window.Device = GetDC( hWnd );
1089         if( fgState.BuildingAMenu )
1090         {
1091           unsigned int current_DisplayMode = fgState.DisplayMode;
1092           fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH;
1093           fgSetupPixelFormat( window, FALSE, PFD_MAIN_PLANE );
1094           fgState.DisplayMode = current_DisplayMode;
1095
1096           if( fgStructure.MenuContext )
1097               wglMakeCurrent( window->Window.Device,
1098                               fgStructure.MenuContext->Context ) ;
1099           else
1100           {
1101               fgStructure.MenuContext =
1102                   (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
1103               fgStructure.MenuContext->Context =
1104                   wglCreateContext( window->Window.Device );
1105           }
1106
1107           /* window->Window.Context = wglGetCurrentContext () ;   */
1108           window->Window.Context = wglCreateContext( window->Window.Device );
1109         }
1110         else
1111         {
1112             fgSetupPixelFormat( window, FALSE, PFD_MAIN_PLANE );
1113
1114             if( fgState.UseCurrentContext != TRUE )
1115                 window->Window.Context =
1116                     wglCreateContext( window->Window.Device );
1117             else
1118             {
1119                 window->Window.Context = wglGetCurrentContext( );
1120                 if( ! window->Window.Context )
1121                     window->Window.Context =
1122                         wglCreateContext( window->Window.Device );
1123             }
1124         }
1125
1126         window->State.NeedToResize = TRUE;
1127         ReleaseDC( window->Window.Handle, window->Window.Device );
1128         break;
1129
1130     case WM_SIZE:
1131         /*
1132          * We got resized... But check if the window has been already added...
1133          */
1134         fghReshapeWindowByHandle( hWnd, LOWORD(lParam), HIWORD(lParam) );
1135         break;
1136 #if 0
1137     case WM_SETFOCUS: 
1138         printf("WM_SETFOCUS: %p\n", window );
1139         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1140         break;
1141
1142     case WM_ACTIVATE: 
1143         if (LOWORD(wParam) != WA_INACTIVE)
1144         {
1145             /* glutSetCursor( fgStructure.Window->State.Cursor ); */
1146             printf("WM_ACTIVATE: glutSetCursor( %p, %d)\n", window,
1147                    window->State.Cursor );
1148
1149             glutSetCursor( window->State.Cursor );
1150         }
1151
1152         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1153         break;
1154 #endif
1155
1156     case WM_SETCURSOR: 
1157         /*
1158          * Windows seems to need reminding to erase the cursor for NONE.
1159          */
1160 #if 0
1161         if ((LOWORD(lParam) == HTCLIENT) &&
1162             (fgStructure.Window->State.Cursor == GLUT_CURSOR_NONE))
1163           SetCursor( NULL );
1164 #else
1165         /* Set the cursor AND change it for this window class. */
1166 #        define MAP_CURSOR(a,b) case a: SetCursor( LoadCursor( NULL, b ) ); \
1167         break;
1168         /* Nuke the cursor AND change it for this window class. */
1169 #        define ZAP_CURSOR(a,b) case a: 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
1257             /*
1258              * Since the window is a menu, make the parent window current
1259              */
1260             fgSetWindow ( window->ActiveMenu->ParentWindow ) ;
1261
1262             break;
1263         }
1264
1265         window->State.Modifiers = 
1266             ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
1267                 ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1268             ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
1269                 ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1270             ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
1271                 ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1272
1273         if( ( wParam & MK_LBUTTON ) ||
1274             ( wParam & MK_MBUTTON ) ||
1275             ( wParam & MK_RBUTTON ) )
1276         {
1277             if( window->Callbacks.Motion )
1278             {
1279                 fgSetWindow( window );
1280                 window->Callbacks.Motion( window->State.MouseX,
1281                                           window->State.MouseY );
1282             }
1283         }
1284         else
1285         {
1286             if( window->Callbacks.Passive )
1287             {
1288                 fgSetWindow( window );
1289                 window->Callbacks.Passive( window->State.MouseX,
1290                                            window->State.MouseY );
1291             }
1292         }
1293
1294         window->State.Modifiers = 0xffffffff;
1295     }
1296     break;
1297
1298     case WM_LBUTTONDOWN:
1299     case WM_MBUTTONDOWN:
1300     case WM_RBUTTONDOWN:
1301     case WM_LBUTTONUP:
1302     case WM_MBUTTONUP:
1303     case WM_RBUTTONUP:
1304     {
1305         GLboolean pressed = TRUE;
1306         int button;
1307
1308         window->State.MouseX = LOWORD( lParam );
1309         window->State.MouseY = HIWORD( lParam );
1310
1311         switch( uMsg )
1312         {
1313         case WM_LBUTTONDOWN:
1314             pressed = TRUE;  button = GLUT_LEFT_BUTTON;   break;
1315         case WM_MBUTTONDOWN:
1316             pressed = TRUE;  button = GLUT_MIDDLE_BUTTON; break;
1317         case WM_RBUTTONDOWN:
1318             pressed = TRUE;  button = GLUT_RIGHT_BUTTON;  break;
1319         case WM_LBUTTONUP:
1320             pressed = FALSE; button = GLUT_LEFT_BUTTON;   break;
1321         case WM_MBUTTONUP:
1322             pressed = FALSE; button = GLUT_MIDDLE_BUTTON; break;
1323         case WM_RBUTTONUP:
1324             pressed = FALSE; button = GLUT_RIGHT_BUTTON;  break;
1325         default:
1326             pressed = FALSE; button = -1;                 break;
1327         }
1328
1329         if( GetSystemMetrics( SM_SWAPBUTTON ) )
1330             if( button == GLUT_LEFT_BUTTON )
1331                 button = GLUT_RIGHT_BUTTON;
1332             else if( button == GLUT_RIGHT_BUTTON )
1333                 button = GLUT_LEFT_BUTTON;
1334
1335         if( button == -1 )
1336             return DefWindowProc( hWnd, uMsg, lParam, wParam );
1337
1338         /*
1339          * Do not execute the application's mouse callback if a
1340          * menu is hooked to this button.
1341          * In that case an appropriate private call should be generated.
1342          * Near as I can tell, this is the menu behaviour:
1343          *  - Down-click the menu button, menu not active:  activate
1344          *    the menu with its upper left-hand corner at the mouse location.
1345          *  - Down-click any button outside the menu, menu active:
1346          *    deactivate the menu
1347          *  - Down-click any button inside the menu, menu active:
1348          *    select the menu entry and deactivate the menu
1349          *  - Up-click the menu button, menu not active:  nothing happens
1350          *  - Up-click the menu button outside the menu, menu active:
1351          *    nothing happens
1352          *  - Up-click the menu button inside the menu, menu active:
1353          *    select the menu entry and deactivate the menu
1354          */
1355         /* Window has an active menu, it absorbs any mouse click */
1356         if( window->ActiveMenu )
1357         {
1358             /* Inside the menu, invoke the callback and deactivate the menu*/
1359             if( fgCheckActiveMenu( window, window->ActiveMenu ) == TRUE )
1360             {
1361                 /*
1362                  * Save the current window and menu and set the current
1363                  * window to the window whose menu this is
1364                  */
1365                 SFG_Window *save_window = fgStructure.Window;
1366                 SFG_Menu *save_menu = fgStructure.Menu;
1367                 SFG_Window *parent_window = window->ActiveMenu->ParentWindow;
1368                 fgSetWindow( parent_window );
1369                 fgStructure.Menu = window->ActiveMenu;
1370
1371                 /* Execute the menu callback */
1372                 fgExecuteMenuCallback( window->ActiveMenu );
1373                 fgDeactivateMenu( parent_window );
1374
1375                 /* Restore the current window and menu */
1376                 fgSetWindow( save_window );
1377                 fgStructure.Menu = save_menu;
1378             }
1379             else  /* Out of menu, deactivate the menu if it's a downclick */
1380             {
1381                 if( pressed == TRUE )
1382                     fgDeactivateMenu( window->ActiveMenu->ParentWindow );
1383             }
1384
1385             /*
1386              * Let's make the window redraw as a result of the mouse
1387              * click and menu activity.
1388              */
1389             if( ! window->IsMenu )
1390                 window->State.Redisplay = TRUE;
1391
1392             break;
1393         }
1394
1395         if ( ( window->Menu[ button ] != NULL ) && ( pressed == TRUE ) )
1396         {
1397             window->State.Redisplay = TRUE;
1398             fgSetWindow( window );
1399             fgActivateMenu( window, button );
1400
1401             break;
1402         }
1403
1404         if( window->Callbacks.Mouse == NULL )
1405             break;
1406
1407         fgSetWindow( window );
1408
1409         fgStructure.Window->State.Modifiers = 
1410             ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
1411                 ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1412             ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
1413                 ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1414             ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
1415                 ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1416
1417         window->Callbacks.Mouse(
1418             button,
1419             pressed == TRUE ? GLUT_DOWN : GLUT_UP,
1420             window->State.MouseX,
1421             window->State.MouseY
1422         );
1423
1424         fgStructure.Window->State.Modifiers = 0xffffffff;
1425     }
1426     break;
1427
1428     case 0x020a:
1429         /* Should be WM_MOUSEWHEEL but my compiler doesn't recognize it */
1430     {
1431         int wheel_number = LOWORD( lParam );
1432         /* THIS IS SPECULATIVE -- John Fay, 10/2/03 */
1433         int ticks = HIWORD( lParam ) / 120;
1434         /* Should be WHEEL_DELTA instead of 120 */
1435         int direction = 1;
1436
1437         if( ticks < 0 )
1438         {
1439             direction = -1;
1440             ticks = -ticks;
1441         }
1442
1443         /*
1444          * The mouse cursor has moved. Remember the new mouse cursor's position
1445          */
1446         /*        window->State.MouseX = LOWORD( lParam ); */
1447         /* Need to adjust by window position, */
1448         /*        window->State.MouseY = HIWORD( lParam ); */
1449         /* change "lParam" to other parameter */
1450
1451         if( ( window->Callbacks.MouseWheel == NULL ) &&
1452             ( window->Callbacks.Mouse == NULL ) )
1453             break;
1454
1455         fgSetWindow( window );
1456         fgStructure.Window->State.Modifiers = 
1457             ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
1458                 ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1459             ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
1460                 ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1461             ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
1462                 ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1463
1464         while( ticks-- )
1465             if( window->Callbacks.MouseWheel )
1466                 window->Callbacks.MouseWheel(
1467                     wheel_number,
1468                     direction,
1469                     window->State.MouseX,
1470                     window->State.MouseY
1471                 );
1472             else  /* No mouse wheel, call the mouse button callback twice */
1473             {
1474                 /*
1475                  * XXX The below assumes that you have no more than 3 mouse
1476                  * XXX buttons.  Sorry.
1477                  */
1478                 int button = wheel_number*2 + 4;
1479                 button += (1 + direction)/2;
1480                 window->Callbacks.Mouse( button, GLUT_DOWN,
1481                                          window->State.MouseX,
1482                                          window->State.MouseY ) ;
1483                 window->Callbacks.Mouse( button, GLUT_UP,
1484                                          window->State.MouseX,
1485                                          window->State.MouseY ) ;
1486             }
1487
1488         fgStructure.Window->State.Modifiers = 0xffffffff;
1489     }
1490     break ;
1491
1492     case WM_SYSKEYDOWN:
1493     case WM_KEYDOWN:
1494     {
1495         int keypress = -1;
1496         POINT mouse_pos ;
1497
1498         if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
1499             break;
1500
1501         /*
1502          * Remember the current modifiers state. This is done here in order 
1503          * to make sure the VK_DELETE keyboard callback is executed properly.
1504          */
1505         window->State.Modifiers = 
1506             ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
1507                 ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1508             ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
1509                 ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1510             ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
1511                 ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1512
1513         GetCursorPos( &mouse_pos );
1514         ScreenToClient( window->Window.Handle, &mouse_pos );
1515
1516         window->State.MouseX = mouse_pos.x;
1517         window->State.MouseY = mouse_pos.y;
1518
1519         /*
1520          * Convert the Win32 keystroke codes to GLUTtish way
1521          */
1522 #       define KEY(a,b) case a: keypress = b; break;
1523
1524         switch( wParam )
1525         {
1526             KEY( VK_F1,     GLUT_KEY_F1        );
1527             KEY( VK_F2,     GLUT_KEY_F2        );
1528             KEY( VK_F3,     GLUT_KEY_F3        );
1529             KEY( VK_F4,     GLUT_KEY_F4        );
1530             KEY( VK_F5,     GLUT_KEY_F5        );
1531             KEY( VK_F6,     GLUT_KEY_F6        );
1532             KEY( VK_F7,     GLUT_KEY_F7        );
1533             KEY( VK_F8,     GLUT_KEY_F8        );
1534             KEY( VK_F9,     GLUT_KEY_F9        );
1535             KEY( VK_F10,    GLUT_KEY_F10       );
1536             KEY( VK_F11,    GLUT_KEY_F11       );
1537             KEY( VK_F12,    GLUT_KEY_F12       );
1538             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
1539             KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
1540             KEY( VK_HOME,   GLUT_KEY_HOME      );
1541             KEY( VK_END,    GLUT_KEY_END       );
1542             KEY( VK_LEFT,   GLUT_KEY_LEFT      );
1543             KEY( VK_UP,     GLUT_KEY_UP        );
1544             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
1545             KEY( VK_DOWN,   GLUT_KEY_DOWN      );
1546             KEY( VK_INSERT, GLUT_KEY_INSERT    );
1547
1548         case VK_DELETE:
1549             /*
1550              * The delete key should be treated as an ASCII keypress:
1551              */
1552             if( window->Callbacks.Keyboard )
1553             {
1554                 fgSetWindow( window );
1555                 window->Callbacks.Keyboard( 127, window->State.MouseX,
1556                                             window->State.MouseY );
1557             }
1558         }
1559
1560         if( ( keypress != -1 ) && window->Callbacks.Special )
1561         {
1562             fgSetWindow( window );
1563             window->Callbacks.Special( keypress, window->State.MouseX,
1564                                        window->State.MouseY );
1565         }
1566
1567         window->State.Modifiers = 0xffffffff;
1568     }
1569     break;
1570
1571     case WM_SYSKEYUP:
1572     case WM_KEYUP:
1573     {
1574         int keypress = -1;
1575         POINT mouse_pos ;
1576
1577         /*
1578          * Remember the current modifiers state. This is done here in order 
1579          * to make sure the VK_DELETE keyboard callback is executed properly.
1580          */
1581         window->State.Modifiers = 
1582             ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
1583                 ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1584             ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
1585                 ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1586             ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
1587                 ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1588
1589         GetCursorPos( &mouse_pos );
1590         ScreenToClient( window->Window.Handle, &mouse_pos );
1591
1592         window->State.MouseX = mouse_pos.x;
1593         window->State.MouseY = mouse_pos.y;
1594
1595         /*
1596          * Convert the Win32 keystroke codes to GLUTtish way.
1597          * "KEY(a,b)" was defined under "WM_KEYDOWN"
1598          */
1599
1600         switch( wParam )
1601         {
1602             KEY( VK_F1,     GLUT_KEY_F1        );
1603             KEY( VK_F2,     GLUT_KEY_F2        );
1604             KEY( VK_F3,     GLUT_KEY_F3        );
1605             KEY( VK_F4,     GLUT_KEY_F4        );
1606             KEY( VK_F5,     GLUT_KEY_F5        );
1607             KEY( VK_F6,     GLUT_KEY_F6        );
1608             KEY( VK_F7,     GLUT_KEY_F7        );
1609             KEY( VK_F8,     GLUT_KEY_F8        );
1610             KEY( VK_F9,     GLUT_KEY_F9        );
1611             KEY( VK_F10,    GLUT_KEY_F10       );
1612             KEY( VK_F11,    GLUT_KEY_F11       );
1613             KEY( VK_F12,    GLUT_KEY_F12       );
1614             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
1615             KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
1616             KEY( VK_HOME,   GLUT_KEY_HOME      );
1617             KEY( VK_END,    GLUT_KEY_END       );
1618             KEY( VK_LEFT,   GLUT_KEY_LEFT      );
1619             KEY( VK_UP,     GLUT_KEY_UP        );
1620             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
1621             KEY( VK_DOWN,   GLUT_KEY_DOWN      );
1622             KEY( VK_INSERT, GLUT_KEY_INSERT    );
1623
1624           case VK_DELETE:
1625               /*
1626                * The delete key should be treated as an ASCII keypress:
1627                */
1628               if( window->Callbacks.KeyboardUp )
1629               {
1630                   fgSetWindow( window );
1631                   window->Callbacks.KeyboardUp( 127, window->State.MouseX,
1632                                                 window->State.MouseY );
1633               }
1634               
1635               break;
1636
1637         default:
1638         {
1639             BYTE state[ 256 ];
1640             WORD code[ 2 ];
1641             
1642             GetKeyboardState( state );
1643             
1644             if( ToAscii( wParam, 0, state, code, 0 ) == 1 )
1645                 wParam=code[ 0 ];
1646
1647             if( window->Callbacks.KeyboardUp )
1648             {
1649                 fgSetWindow( window );
1650                 window->Callbacks.KeyboardUp( (char)wParam,
1651                                               window->State.MouseX,
1652                                               window->State.MouseY );
1653             }
1654         }
1655         }
1656
1657         if( (keypress != -1) && window->Callbacks.SpecialUp )
1658         {
1659             fgSetWindow( window );
1660             window->Callbacks.SpecialUp( keypress, window->State.MouseX,
1661                                          window->State.MouseY );
1662         }
1663
1664         window->State.Modifiers = 0xffffffff;
1665     }
1666     break;
1667
1668     case WM_SYSCHAR:
1669     case WM_CHAR:
1670     {
1671         if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
1672             break;
1673
1674         if( window->Callbacks.Keyboard )
1675         {
1676             fgSetWindow( window );
1677             window->State.Modifiers = 
1678                 ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
1679                     ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1680                 ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
1681                     ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1682                 ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
1683                     ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1684
1685             window->Callbacks.Keyboard( (char)wParam, window->State.MouseX,
1686                                         window->State.MouseY );
1687             window->State.Modifiers = 0xffffffff;
1688         }
1689     }
1690     break;
1691
1692     case WM_CAPTURECHANGED:
1693         /* User has finished resizing the window, force a redraw */
1694         if( window->Callbacks.Display )
1695         {
1696             fgSetWindow( window );
1697
1698             window->Callbacks.Display( );
1699         }
1700
1701         /*lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ) ; */
1702         break;
1703
1704         /*
1705          * Other messages that I have seen and which are not handled already
1706          */
1707     case WM_SETTEXT:  /* 0x000c */
1708         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1709         /* Pass it on to "DefWindowProc" to set the window text */
1710         break;
1711
1712     case WM_GETTEXT:  /* 0x000d */
1713         /* Ideally we would copy the title of the window into "lParam" */
1714         /* strncpy ( (char *)lParam, "Window Title", wParam );
1715            lRet = ( wParam > 12 ) ? 12 : wParam;  */
1716         /* the number of characters copied */
1717         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1718         break;
1719
1720     case WM_GETTEXTLENGTH:  /* 0x000e */
1721         /* Ideally we would get the length of the title of the window */
1722         lRet = 12;
1723         /* the number of characters in "Window Title\0" (see above) */
1724         break;
1725
1726     case WM_ERASEBKGND:  /* 0x0014 */
1727         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1728         break;
1729
1730     case WM_SYNCPAINT:  /* 0x0088 */
1731         /* Another window has moved, need to update this one */
1732         window->State.Redisplay = TRUE;
1733         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1734         /* Help screen says this message must be passed to "DefWindowProc" */
1735         break;
1736
1737     case WM_NCPAINT:  /* 0x0085 */
1738       /* Need to update the border of this window */
1739         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1740         /* Pass it on to "DefWindowProc" to repaint a standard border */
1741         break;
1742
1743     default:
1744         /*
1745          * Handle unhandled messages
1746          */
1747         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1748         break;
1749     }
1750
1751     return lRet ;
1752 }
1753 #endif
1754
1755 /*** END OF FILE ***/