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