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