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