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