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