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