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