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