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