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