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