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