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