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