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