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