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