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