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