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