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