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