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