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