220852fcd25f2f08b4b08c3504473915518ae060
[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      * Have the callback executed now. The buffers should
85      * be swapped by the glutSwapBuffers() execution inside
86      * the callback itself.
87      */
88     window->Callbacks.Display();
89 }
90
91 /*
92  * Handle a window configuration change. When no reshape
93  * callback is hooked, the viewport size is updated to
94  * match the new window size.
95  */
96 static void fghReshapeWindowByHandle
97 #if TARGET_HOST_UNIX_X11
98     ( Window handle, int width, int height )
99 #elif TARGET_HOST_WIN32
100     ( HWND handle, int width, int height )
101 #endif
102 {
103     /*
104      * Find the window that received the reshape event
105      */
106     SFG_Window* window = fgWindowByHandle( handle );
107     freeglut_return_if_fail( window != NULL );
108
109     /*
110      * Remember about setting the current window...
111      */
112     glutSetWindow( window->ID );
113
114     /*
115      * Check if there is a reshape callback hooked
116      */
117     if( window->Callbacks.Reshape != NULL )
118     {
119         /*
120          * OKi, have it called immediately
121          */
122         window->Callbacks.Reshape( width, height );
123     }
124     else
125     {
126         /*
127          * Otherwise just resize the viewport
128          */
129         glViewport( 0, 0, width, height );
130     }
131 }
132
133 /*
134  * A static helper function to execute display callback for a window
135  */
136 static void fghcbDisplayWindow( SFG_Window *window, SFG_Enumerator *enumerator )
137 {
138 #if TARGET_HOST_UNIX_X11
139     /*
140      * Check if there is an idle callback hooked
141      */
142 // #   warning there is a redisplay hack here (see the code commented out)
143     if( (window->Callbacks.Display != NULL) &&
144         /*(window->State.Redisplay == TRUE) &&*/
145         (window->State.Visible == TRUE) )
146     {
147         /*
148          * OKi, this is the case: have the window set as the current one
149          */
150         glutSetWindow( window->ID );
151
152         /*
153          * Do not exagerate with the redisplaying
154          */
155         window->State.Redisplay = FALSE;
156
157         /*
158          * And execute the display callback immediately after
159          */
160         window->Callbacks.Display();
161     }
162
163 #elif TARGET_HOST_WIN32
164
165     /*
166      * Do we need to explicitly resize the window?
167      */
168     if( window->State.NeedToResize )
169     {
170         glutSetWindow( window->ID );
171
172         fghReshapeWindowByHandle( 
173             window->Window.Handle,
174             glutGet( GLUT_WINDOW_WIDTH ),
175             glutGet( GLUT_WINDOW_HEIGHT )
176         );
177
178         /*
179          * Never ever do that again:
180          */
181         window->State.NeedToResize = FALSE;
182     }
183
184     /*
185      * This is done in a bit different way under Windows
186      */
187     RedrawWindow( 
188         window->Window.Handle, NULL, NULL, 
189         RDW_NOERASE | RDW_INTERNALPAINT | RDW_INVALIDATE 
190     );
191
192 #endif
193
194     /*
195      * Process this window's children (if any)
196      */
197     fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
198 }
199
200 /*
201  * Make all windows perform a display call
202  */
203 static void fghDisplayAll( void )
204 {
205     SFG_Enumerator enumerator;
206
207     /*
208      * Uses a method very similiar for fgWindowByHandle...
209      */
210     enumerator.found = FALSE;
211     enumerator.data  =  NULL;
212
213     /*
214      * Start the enumeration now:
215      */
216     fgEnumWindows( fghcbDisplayWindow, &enumerator );
217 }
218
219 /*
220  * Window enumerator callback to check for the joystick polling code
221  */
222 static void fghcbCheckJoystickPolls( SFG_Window *window, SFG_Enumerator *enumerator )
223 {
224     double checkTime = fgElapsedTime();
225
226     /*
227      * Check if actually need to do the poll for the currently enumerated window:
228      */
229     if( window->State.JoystickLastPoll + window->State.JoystickPollRate >= checkTime )
230     {
231         /*
232          * Yeah, that's it. Poll the joystick...
233          */
234         fgJoystickPollWindow( window );
235
236         /*
237          * ...and reset the polling counters:
238          */
239         window->State.JoystickLastPoll = checkTime;
240     }
241
242     /*
243      * Process this window's children (if any)
244      */
245     fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
246 }
247
248 /*
249  * Check all windows for joystick polling
250  */
251 static void fghCheckJoystickPolls( void )
252 {
253     SFG_Enumerator enumerator;
254
255     /*
256      * Uses a method very similiar for fgWindowByHandle...
257      */
258     enumerator.found = FALSE;
259     enumerator.data  =  NULL;
260
261     /*
262      * Start the enumeration now:
263      */
264     fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
265 }
266
267 /*
268  * Check the global timers
269  */
270 static void fghCheckTimers( void )
271 {
272     long checkTime = fgElapsedTime();
273     SFG_Timer *timer, *next;
274     SFG_List timedOut;
275
276     fgListInit(&timedOut);
277
278     /*
279      * For every timer that is waiting for triggering
280      */
281     for( timer = fgState.Timers.First; timer; timer = next )
282     {
283         next = timer->Node.Next;
284
285         /*
286          * Check for the timeout:
287          */
288         if( timer->TriggerTime <= checkTime )
289         {
290             /*
291              * Add the timer to the timed out timers list
292              */
293             fgListRemove( &fgState.Timers, &timer->Node );
294             fgListAppend( &timedOut, &timer->Node );
295         }
296     }
297
298     /*
299      * Now feel free to execute all the hooked and timed out timer callbacks
300      * And delete the timed out timers...
301      */
302     while ( (timer = timedOut.First) )
303     {
304         if( timer->Callback != NULL )
305             timer->Callback( timer->ID );
306         fgListRemove( &timedOut, &timer->Node );
307         free( timer );
308     }
309 }
310
311 /*
312  * Elapsed Time
313  */
314 long fgElapsedTime( void )
315 {
316 #ifndef WIN32
317         struct timeval now;
318         long elapsed;
319
320         gettimeofday( &now, NULL );
321
322         elapsed = (now.tv_usec - fgState.Time.Value.tv_usec) / 1000;
323         elapsed += (now.tv_sec - fgState.Time.Value.tv_sec) * 1000;
324
325         return( elapsed );
326 #else
327         return (timeGetTime() - fgState.Time.Value);
328 #endif
329 }
330
331 /*
332  * Error Messages.
333  */
334 void fgError( const char *fmt, ... )
335 {
336     va_list ap;
337
338     va_start( ap, fmt );
339
340     fprintf( stderr, "freeglut: ");
341     vfprintf( stderr, fmt, ap );
342     fprintf( stderr, "\n" );
343
344     va_end( ap );
345
346     exit( 1 );
347 }
348
349 void fgWarning( const char *fmt, ... )
350 {
351     va_list ap;
352
353     va_start( ap, fmt );
354
355     fprintf( stderr, "freeglut: ");
356     vfprintf( stderr, fmt, ap );
357     fprintf( stderr, "\n" );
358
359     va_end( ap );
360 }
361
362 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
363
364 /*
365  * Enters the FreeGLUT processing loop. Never returns.
366  */
367 void FGAPIENTRY glutMainLoop( void )
368 {
369 #if TARGET_HOST_UNIX_X11
370     SFG_Window* window;
371     XEvent event;
372
373     /*
374      * This code was repeated constantly, so here it goes into a definition:
375      */
376 #   define GETWINDOW(a) window = fgWindowByHandle( event.a.window );if( window == NULL ) break;
377 #   define GETMOUSE(a) window->State.MouseX = event.a.x; window->State.MouseY = event.a.y;
378
379     /*
380      * Make sure the display has been created etc.
381      */
382     freeglut_assert_ready;
383
384     /*
385      * Enter the loop. Iterate as long as there are
386      * any windows in the freeglut structure.
387      */
388     while( fgStructure.Windows.First != NULL )
389     {
390         /*
391          * Do we have any event messages pending?
392          */
393         if( XPending( fgDisplay.Display ) )
394         {
395             /*
396              * Grab the next event to be processed...
397              */
398             XNextEvent( fgDisplay.Display, &event );
399
400             /*
401              * Check the event's type
402              */
403             switch( event.type )
404             {
405             case CreateNotify:
406                 /*
407                  * The window creation confirmation
408                  */
409                 break;
410
411             case DestroyNotify:
412                 /*
413                  * This is sent to confirm the XDestroyWindow call. Ignore it.
414                  */
415                 break;
416
417             case ClientMessage:
418                 /*
419                  * Destroy the window when the WM_DELETE_WINDOW message arrives
420                  */
421                 if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.DeleteWindow )
422                 {
423                     /*
424                      * I wonder if the window still exists ;-)
425                      */
426                     fgDestroyWindow( fgWindowByHandle( event.xclient.window ), TRUE );
427                 }
428                 break;
429
430             case UnmapNotify:
431                 /*
432                  * A window of ours has been unmapped...
433                  */
434                 break;
435
436             case Expose:
437                 /*
438                  * We are too dumb to process partial exposes...
439                  */
440                 if( event.xexpose.count == 0 )
441                     fghRedrawWindowByHandle( event.xexpose.window );
442                 break;
443
444             case ConfigureNotify:
445                 /*
446                  * The window gets resized
447                  */
448                 fghReshapeWindowByHandle(
449                     event.xconfigure.window,
450                     event.xconfigure.width,
451                     event.xconfigure.height
452                 );
453                 break;
454
455             case MappingNotify:
456                 /*
457                  * Have the client's keyboard knowledge updated (xlib.ps,
458                  * page 206, says that's a good thing to do)
459                  */
460                 XRefreshKeyboardMapping( (XMappingEvent *) &event );
461                 break;
462
463             case VisibilityNotify:
464                 {
465                     /*
466                      * The window's visiblity might have changed
467                      */
468                     GETWINDOW( xvisibility );
469
470                     /*
471                      * Break now if no window status callback has been hooked to that window
472                      */
473                     if( window->Callbacks.WindowStatus == NULL )
474                         break;
475
476                     /*
477                      * We're going to send a callback to a window. Make it current.
478                      */
479                     glutSetWindow( window->ID );
480
481                     /*
482                      * Sending this event, the X server can notify us that the window has just
483                      * acquired one of the three possible visibility states: VisibilityUnobscured,
484                      * VisibilityPartiallyObscured or VisibilityFullyObscured
485                      */
486                     switch( event.xvisibility.state )
487                     {
488                     case VisibilityUnobscured:
489                         /*
490                          * We are fully visible...
491                          */
492                         window->Callbacks.WindowStatus( GLUT_FULLY_RETAINED );
493                         window->State.Visible = TRUE;
494                         break;
495
496                     case VisibilityPartiallyObscured:
497                         /*
498                          * The window is partially visible
499                          */
500                         window->Callbacks.WindowStatus( GLUT_PARTIALLY_RETAINED );
501                         window->State.Visible = TRUE;
502                         break;
503
504                     case VisibilityFullyObscured:
505                         /*
506                          * The window is totally obscured
507                          */
508                         window->Callbacks.WindowStatus( GLUT_FULLY_COVERED );
509                         window->State.Visible = FALSE;
510                         break;
511                     }
512                 }
513                 break;
514
515             case EnterNotify:
516                 {
517                     /*
518                      * Mouse is over one of our windows
519                      */
520                     GETWINDOW( xcrossing ); GETMOUSE( xcrossing );
521
522                     /*
523                      * Is there an entry callback hooked to the window?
524                      */
525                     if( window->Callbacks.Entry != NULL )
526                     {
527                         /*
528                          * Yeah. Notify the window about having the mouse cursor over
529                          */
530                         window->Callbacks.Entry( GLUT_ENTERED );
531                     }
532                 }
533                 break;
534
535             case LeaveNotify:
536                 {
537                     /*
538                      * Mouse is no longer over one of our windows
539                      */
540                     GETWINDOW( xcrossing ); GETMOUSE( xcrossing );
541
542                     /*
543                      * Is there an entry callback hooked to the window?
544                      */
545                     if( window->Callbacks.Entry != NULL )
546                     {
547                         /*
548                          * Yeah. Notify the window about having the mouse cursor over
549                          */
550                         window->Callbacks.Entry( GLUT_LEFT );
551                     }
552                 }
553                 break;
554
555             case MotionNotify:
556                 {
557                     /*
558                      * The mouse cursor was moved...
559                      */
560                     GETWINDOW( xmotion ); GETMOUSE( xmotion );
561
562                     /*
563                      * What kind of a movement was it?
564                      */
565                     if( (event.xmotion.state & Button1Mask) || (event.xmotion.state & Button2Mask) ||
566                         (event.xmotion.state & Button3Mask) || (event.xmotion.state & Button4Mask) ||
567                         (event.xmotion.state & Button5Mask) )
568                     {
569                         /*
570                          * A mouse button was pressed during the movement...
571                          * Is there a motion callback hooked to the window?
572                          */
573                         if( window->Callbacks.Motion != NULL )
574                         {
575                             /*
576                              * Yup. Have it executed immediately
577                              */
578                             window->Callbacks.Motion( event.xmotion.x, event.xmotion.y );
579                         }
580                     }
581                     else
582                     {
583                         /*
584                          * Otherwise it was a passive movement...
585                          */
586                         if( window->Callbacks.Passive != NULL )
587                         {
588                             /*
589                              * That's right, and there is a passive callback, too.
590                              */
591                             window->Callbacks.Passive( event.xmotion.x, event.xmotion.y );
592                         }
593                     }
594                 }
595                 break;
596
597             case ButtonRelease:
598             case ButtonPress:
599                 {
600                     int button;
601
602                     /*
603                      * A mouse button has been pressed or released. Traditionally,
604                      * break if the window was found within the freeglut structures.
605                      */
606                     GETWINDOW( xbutton ); GETMOUSE( xbutton );
607
608                     /*
609                      * GLUT API assumes that you can't have more than three mouse buttons, so:
610                      */
611                     switch( event.xbutton.button )
612                     {
613                     /*
614                      * WARNING: this might be wrong, if we only have two mouse buttons,
615                      *          Button2 might mean the right button, isn't that right?
616                      */
617                     case Button1:   button = GLUT_LEFT_BUTTON;   break;
618                     case Button2:   button = GLUT_MIDDLE_BUTTON; break;
619                     case Button3:   button = GLUT_RIGHT_BUTTON;  break;
620                     default:        button = -1;                 break;
621                     }
622
623                     /*
624                      * Skip the unwanted mouse buttons...
625                      */
626                     if( button == -1 )
627                         break;
628
629                     /*
630                      * Do not execute the callback if a menu is hooked to this key.
631                      * In that case an appropriate private call should be generated
632                      */
633                     if( window->Menu[ button ] != NULL )
634                     {
635                         /*
636                          * Set the current window
637                          */
638                         glutSetWindow( window->ID );
639
640                         if( event.type == ButtonPress )
641                         {
642                             /*
643                              * Activate the appropriate menu structure...
644                              */
645                             fgActivateMenu( button );
646                         }
647                         else
648                         {
649                             /*
650                              * There are two general cases generated when a menu button
651                              * is released -- it can provoke a menu call (when released
652                              * over a menu area) or just deactivate the menu (when released
653                              * somewhere else). Unfortunately, both cases must be checked
654                              * recursively due to the submenu possibilities.
655                              */
656                             fgDeactivateMenu( button );
657                         }
658                         break;
659                     }
660
661                     /*
662                      * Check if there is a mouse callback hooked to the window
663                      */
664                     if( window->Callbacks.Mouse == NULL )
665                         break;
666
667                     /*
668                      * Set the current window
669                      */
670                     glutSetWindow( window->ID );
671
672                     /*
673                      * Remember the current modifiers state
674                      */
675                     window->State.Modifiers = event.xbutton.state;
676
677                     /*
678                      * Finally execute the mouse callback
679                      */
680                     window->Callbacks.Mouse(
681                         button,
682                         event.type == ButtonPress ? GLUT_DOWN : GLUT_UP,
683                         event.xbutton.x,
684                         event.xbutton.y
685                     );
686
687                     /*
688                      * Trash the modifiers state
689                      */
690                     window->State.Modifiers = 0xffffffff;
691                 }
692                 break;
693
694             case KeyPress:
695                 {
696                     /*
697                      * A key has been pressed, find the window that had the focus:
698                      */
699                     GETWINDOW( xkey ); GETMOUSE( xkey );
700
701                     /*
702                      * Is there a keyboard/special callback hooked for this window?
703                      */
704                     if( (window->Callbacks.Keyboard != NULL) || (window->Callbacks.Special != NULL) )
705                     {
706                         XComposeStatus composeStatus;
707                         char asciiCode[ 32 ];
708                         KeySym keySym;
709                         int len;
710
711                         /*
712                          * Check for the ASCII/KeySym codes associated with the event:
713                          */
714                         len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode), &keySym, &composeStatus );
715
716                         /*
717                          * Get ready to calling the keyboard/special callbacks
718                          */
719                         glutSetWindow( window->ID );
720
721                         /*
722                          * GLUT API tells us to have two separate callbacks...
723                          */
724                         if( len > 0 )
725                         {
726                             /*
727                              * ...one for the ASCII translateable keypresses...
728                              */
729                             if( window->Callbacks.Keyboard != NULL )
730                             {
731                                 /*
732                                  * Remember the current modifiers state
733                                  */
734                                 window->State.Modifiers = event.xkey.state;
735
736                                 /*
737                                  * Execute the callback
738                                  */
739                                 window->Callbacks.Keyboard( asciiCode[ 0 ], event.xkey.x, event.xkey.y );
740
741                                 /*
742                                  * Trash the modifiers state
743                                  */
744                                 window->State.Modifiers = 0xffffffff;
745                             }
746                         }
747                         else
748                         {
749                             int special = -1;
750
751                             /*
752                              * ...and one for all the others, which need to be translated to GLUT_KEY_Xs...
753                              */
754                             switch( keySym )
755                             {
756                             /*
757                              * First the function keys come:
758                              */
759                             case XK_F1:     special = GLUT_KEY_F1;     break;
760                             case XK_F2:     special = GLUT_KEY_F2;     break;
761                             case XK_F3:     special = GLUT_KEY_F3;     break;
762                             case XK_F4:     special = GLUT_KEY_F4;     break;
763                             case XK_F5:     special = GLUT_KEY_F5;     break;
764                             case XK_F6:     special = GLUT_KEY_F6;     break;
765                             case XK_F7:     special = GLUT_KEY_F7;     break;
766                             case XK_F8:     special = GLUT_KEY_F8;     break;
767                             case XK_F9:     special = GLUT_KEY_F9;     break;
768                             case XK_F10:    special = GLUT_KEY_F10;    break;
769                             case XK_F11:    special = GLUT_KEY_F11;    break;
770                             case XK_F12:    special = GLUT_KEY_F12;    break;
771
772                             /*
773                              * Then the arrows and stuff:
774                              */
775                             case XK_Left:   special = GLUT_KEY_LEFT;   break;
776                             case XK_Right:  special = GLUT_KEY_RIGHT;  break;
777                             case XK_Up:     special = GLUT_KEY_UP;     break;
778                             case XK_Down:   special = GLUT_KEY_DOWN;   break;
779                             }
780
781                             /*
782                              * Execute the callback (if one has been specified),
783                              * given that the special code seems to be valid...
784                              */
785                             if( (window->Callbacks.Special != NULL) && (special != -1) )
786                             {
787                                  /*
788                                   * Remember the current modifiers state
789                                   */
790                                  window->State.Modifiers = event.xkey.state;
791
792                                  window->Callbacks.Special( special, event.xkey.x, event.xkey.y );
793
794                                  /*
795                                   * Trash the modifiers state
796                                   */
797                                  window->State.Modifiers = 0xffffffff;
798                             }
799                         }
800                     }
801                 }
802                 break;
803             }
804         }
805         else
806         {
807             /*
808              * Have all the timers checked.
809              */
810             fghCheckTimers();
811
812             /*
813              * Poll the joystick and notify all windows that want to be notified...
814              */
815             fghCheckJoystickPolls();
816
817             /*
818              * No messages in the queue, which means we are idling...
819              */
820             if( fgState.IdleCallback != NULL )
821                 fgState.IdleCallback();
822
823             /*
824              * Remember about displaying all the windows that have
825              * been marked for a redisplay (possibly in the idle call):
826              */
827             fghDisplayAll();
828         }
829     }
830
831 #elif TARGET_HOST_WIN32
832
833     GLboolean bLoop = TRUE;
834     MSG stMsg;
835
836     /*
837      * The windows main loop is considerably smaller
838      */
839     while( bLoop )
840     {
841         if( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
842         {
843             /*
844              * Grab the message now, checking for WM_QUIT
845              */
846             if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
847                 bLoop = FALSE;
848
849             /*
850              * Translate virtual-key messages and send them to the window...
851              */
852             TranslateMessage( &stMsg );
853             DispatchMessage( &stMsg );
854         }
855         else
856         {
857             /*
858              * Have all the timers checked.
859              */
860             fghCheckTimers();
861
862             /*
863              * Poll the joystick and notify all windows that want to be notified...
864              */
865             fghCheckJoystickPolls();
866
867             /*
868              * No messages in the queue, which means we are idling...
869              */
870             if( fgState.IdleCallback != NULL )
871                 fgState.IdleCallback();
872
873             /*
874              * Remember about displaying all the windows that have
875              * been marked for a redisplay (possibly in the idle call):
876              */
877             fghDisplayAll();
878
879             /*
880              * We need to terminate the main loop if no windows are left
881              */
882             bLoop = (g_list_length( fgStructure.Windows ) != 0);
883         }
884     }
885
886 #endif
887
888     /*
889      * When this loop terminates, destroy the display, state and structure
890      * of a freeglut session, so that another glutInit() call can happen
891      */
892     fgDeinitialize();
893 }
894
895 /*
896  * The window procedure for handling Win32 events
897  */
898 #if TARGET_HOST_WIN32
899 LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
900 {
901     SFG_Window* window = fgWindowByHandle( hWnd );
902     PAINTSTRUCT ps;
903     LONG lRet = 1;
904
905 #   define  assert_window_registered  if( window == NULL ) return( DefWindowProc( hWnd, uMsg, wParam, lParam ) );
906
907     /*
908      * Check what type of message are we receiving
909      */
910     switch( uMsg )
911     {
912     case WM_CREATE:
913         /*
914          * The window structure is passed as the creation structure paramter...
915          */
916         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
917         assert( window != NULL );
918
919         /*
920          * We can safely store the window's handle now:
921          */
922         window->Window.Handle = hWnd;
923
924         /*
925          * Get the window's device context
926          */
927         window->Window.Device = GetDC( hWnd );
928
929         /*
930          * Setup the pixel format of our window
931          */
932         fgSetupPixelFormat( window, FALSE );
933
934         /*
935          * Create the OpenGL rendering context now
936          */
937         window->Window.Context = wglCreateContext( window->Window.Device );
938
939         /*
940          * Still, we'll be needing to explicitly resize the window
941          */
942         window->State.NeedToResize = TRUE;
943
944         /*
945          * Finally, have the window's device context released
946          */
947         ReleaseDC( window->Window.Handle, window->Window.Device );
948         break;
949
950     case WM_SIZE:
951         /*
952          * We got resized... But check if the window has been already added...
953          */
954         fghReshapeWindowByHandle( hWnd, LOWORD(lParam), HIWORD(lParam) );
955         break;
956
957     case WM_PAINT:
958         /*
959          * Start the painting job
960          */
961         BeginPaint( hWnd, &ps );
962
963         /*
964          * Call the engine's main frame drawing method
965          */
966         fghRedrawWindowByHandle( hWnd );
967
968         /*
969          * End the painting job, release the device context
970          */
971         EndPaint( hWnd, &ps );
972         break;
973
974     case WM_CLOSE:
975         /*
976          * Make sure we don't close a window with current context active
977          */
978         if( fgStructure.Window == window )
979         {
980             wglMakeCurrent( NULL, NULL );
981             wglDeleteContext( window->Window.Context );
982         }
983
984         /*
985          * Proceed with the window destruction
986          */
987         DestroyWindow( window->Window.Handle );
988         break;
989
990     case WM_DESTROY:
991         /*
992          * The window already got destroyed, so forget about it's existence:
993          */
994         fgDestroyWindow( window, FALSE );
995         return( 0 );
996
997     case WM_MOUSEMOVE:
998     {
999         assert_window_registered;
1000
1001         /*
1002          * The mouse cursor has moved. Remember the new mouse cursor's position
1003          */
1004         window->State.MouseX = LOWORD( lParam );
1005         window->State.MouseY = HIWORD( lParam );
1006
1007         /*
1008          * Fallback if there's an active menu hooked to this window
1009          */
1010         if( window->MenuActive[ 0 ] || window->MenuActive[ 1 ] || window->MenuActive[ 2 ] )
1011             break;
1012
1013         /*
1014          * Remember the current modifiers state.
1015          */
1016         window->State.Modifiers = 
1017             (GetKeyState( VK_LSHIFT   ) || GetKeyState( VK_RSHIFT   )) ? GLUT_ACTIVE_SHIFT : 0 |
1018             (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL  : 0 |
1019             (GetKeyState( VK_LMENU    ) || GetKeyState( VK_RMENU    )) ? GLUT_ACTIVE_ALT   : 0;
1020
1021         /*
1022          * Check if any of the mouse buttons is pressed...
1023          */
1024         if( (wParam & MK_LBUTTON) || (wParam & MK_MBUTTON) || (wParam & MK_RBUTTON) )
1025         {
1026             /*
1027              * Yeah, indeed. We need to use the motion callback then:
1028              */
1029             if( window->Callbacks.Motion != NULL )
1030             {
1031                 /*
1032                  * Make sure the current window is set...
1033                  */
1034                 glutSetWindow( window->ID );
1035
1036                 /*
1037                  * Execute the active mouse motion callback now
1038                  */
1039                 window->Callbacks.Motion( window->State.MouseX, window->State.MouseY );
1040             }
1041         }
1042         else
1043         {
1044             /*
1045              * All mouse buttons are up, execute the passive mouse motion callback
1046              */
1047             if( window->Callbacks.Passive != NULL )
1048             {
1049                 /*
1050                  * Make sure the current window is set
1051                  */
1052                 glutSetWindow( window->ID );
1053
1054                 /*
1055                  * Execute the passive mouse motion callback
1056                  */
1057                 window->Callbacks.Passive( window->State.MouseX, window->State.MouseY );
1058             }
1059         }
1060
1061         /*
1062          * Thrash the current modifiers state now
1063          */
1064         window->State.Modifiers = 0xffffffff;
1065     }
1066     break;
1067
1068     case WM_LBUTTONDOWN:
1069     case WM_MBUTTONDOWN:
1070     case WM_RBUTTONDOWN:
1071     case WM_LBUTTONUP:
1072     case WM_MBUTTONUP:
1073     case WM_RBUTTONUP:
1074     {
1075         GLboolean pressed = TRUE;
1076         int button;
1077
1078         /*
1079          * A mouse button has been pressed *or* released. Again, break off
1080          * if the message was not directed towards a freeglut window...
1081          */
1082         assert_window_registered;
1083
1084         /*
1085          * The mouse cursor has moved. Remember the new mouse cursor's position
1086          */
1087         window->State.MouseX = LOWORD( lParam );
1088         window->State.MouseY = HIWORD( lParam );
1089
1090         /*
1091          * We're curious about the GLUT API button name...
1092          */
1093         switch( uMsg )
1094         {
1095         case WM_LBUTTONDOWN: pressed = TRUE;  button = GLUT_LEFT_BUTTON;   break;
1096         case WM_MBUTTONDOWN: pressed = TRUE;  button = GLUT_MIDDLE_BUTTON; break;
1097         case WM_RBUTTONDOWN: pressed = TRUE;  button = GLUT_RIGHT_BUTTON;  break;
1098         case WM_LBUTTONUP:   pressed = FALSE; button = GLUT_LEFT_BUTTON;   break;
1099         case WM_MBUTTONUP:   pressed = FALSE; button = GLUT_MIDDLE_BUTTON; break;
1100         case WM_RBUTTONUP:   pressed = FALSE; button = GLUT_RIGHT_BUTTON;  break;
1101         default:             pressed = FALSE; button = -1;                 break;
1102         }
1103
1104         /*
1105          * The left and right mouse buttons might have been swapped...
1106          */
1107         if( GetSystemMetrics( SM_SWAPBUTTON ) )
1108             if( button == GLUT_LEFT_BUTTON ) button = GLUT_RIGHT_BUTTON;
1109             else if( button == GLUT_RIGHT_BUTTON ) button = GLUT_LEFT_BUTTON;
1110
1111         /*
1112          * Hey, what's up with you?
1113          */
1114         if( button == -1 )
1115             return( DefWindowProc( hWnd, uMsg, lParam, wParam ) );
1116
1117         /*
1118          * Do not execute the callback if a menu is hooked to this key.
1119          * In that case an appropriate private call should be generated
1120          */
1121         if( window->Menu[ button ] != NULL )
1122         {
1123             /*
1124              * Set the current window
1125              */
1126             glutSetWindow( window->ID );
1127
1128             if( pressed == TRUE )
1129             {
1130                 /*
1131                  * Activate the appropriate menu structure...
1132                  */
1133                 fgActivateMenu( button );
1134             }
1135             else
1136             {
1137                 /*
1138                  * There are two general cases generated when a menu button
1139                  * is released -- it can provoke a menu call (when released
1140                  * over a menu area) or just deactivate the menu (when released
1141                  * somewhere else). Unfortunately, both cases must be checked
1142                  * recursively due to the submenu possibilities.
1143                  */
1144                 fgDeactivateMenu( button );
1145             }
1146             break;
1147         }
1148
1149         /*
1150          * Check if there is a mouse callback hooked to the window
1151          */
1152         if( window->Callbacks.Mouse == NULL )
1153             break;
1154
1155         /*
1156          * Set the current window
1157          */
1158         glutSetWindow( window->ID );
1159
1160         /*
1161          * Remember the current modifiers state.
1162          */
1163         window->State.Modifiers = 
1164             (GetKeyState( VK_LSHIFT   ) || GetKeyState( VK_RSHIFT   )) ? GLUT_ACTIVE_SHIFT : 0 |
1165             (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL  : 0 |
1166             (GetKeyState( VK_LMENU    ) || GetKeyState( VK_RMENU    )) ? GLUT_ACTIVE_ALT   : 0;
1167
1168         /*
1169          * Finally execute the mouse callback
1170          */
1171         window->Callbacks.Mouse(
1172             button,
1173             pressed == TRUE ? GLUT_DOWN : GLUT_UP,
1174             window->State.MouseX,
1175             window->State.MouseY
1176         );
1177
1178         /*
1179          * Trash the modifiers state
1180          */
1181         window->State.Modifiers = 0xffffffff;
1182     }
1183     break;
1184
1185     case WM_SYSKEYDOWN:
1186     case WM_KEYDOWN:
1187     {
1188         int keypress = -1;
1189
1190         /*
1191          * First of all, make sure that there is a window to be notified of this
1192          */
1193         assert_window_registered;
1194
1195         /*
1196          * Ignore the automatic key repetition if needed:
1197          */
1198         if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
1199             break;
1200
1201         /*
1202          * Remember the current modifiers state. This is done here in order 
1203          * to make sure the VK_DELETE keyboard callback is executed properly.
1204          */
1205         window->State.Modifiers = 
1206             (GetKeyState( VK_LSHIFT   ) || GetKeyState( VK_RSHIFT   )) ? GLUT_ACTIVE_SHIFT : 0 |
1207             (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL  : 0 |
1208             (GetKeyState( VK_LMENU    ) || GetKeyState( VK_RMENU    )) ? GLUT_ACTIVE_ALT   : 0;
1209
1210         /*
1211          * Convert the Win32 keystroke codes to GLUTtish way
1212          */
1213 #       define KEY(a,b) case a: keypress = b; break;
1214
1215         switch( wParam )
1216         {
1217             /*
1218              * Most of the special characters can be handled automagically...
1219              */
1220             KEY( VK_F1,     GLUT_KEY_F1        ); KEY( VK_F2,     GLUT_KEY_F2        );
1221             KEY( VK_F3,     GLUT_KEY_F3        ); KEY( VK_F4,     GLUT_KEY_F4        );
1222             KEY( VK_F5,     GLUT_KEY_F5        ); KEY( VK_F6,     GLUT_KEY_F6        );
1223             KEY( VK_F7,     GLUT_KEY_F7        ); KEY( VK_F8,     GLUT_KEY_F8        );
1224             KEY( VK_F9,     GLUT_KEY_F9        ); KEY( VK_F10,    GLUT_KEY_F10       );
1225             KEY( VK_F11,    GLUT_KEY_F11       ); KEY( VK_F12,    GLUT_KEY_F12       );
1226             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   ); KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
1227             KEY( VK_HOME,   GLUT_KEY_HOME      ); KEY( VK_END,    GLUT_KEY_END       );
1228             KEY( VK_LEFT,   GLUT_KEY_LEFT      ); KEY( VK_UP,     GLUT_KEY_UP        );
1229             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     ); KEY( VK_DOWN,   GLUT_KEY_DOWN      );
1230             KEY( VK_INSERT, GLUT_KEY_INSERT    );
1231
1232             /*
1233              * ...yet there is a small exception we need to have handled...
1234              */
1235             case VK_DELETE:
1236                 /*
1237                  * The delete key should be treated as an ASCII keypress:
1238                  */
1239                 if( window->Callbacks.Keyboard != NULL )
1240                     window->Callbacks.Keyboard( 127, window->State.MouseX, window->State.MouseY );
1241         }
1242
1243         /*
1244          * Execute the special callback, if present, given the conversion was a success:
1245          */
1246         if( (keypress != -1) && (window->Callbacks.Special != NULL) )
1247         {
1248             /*
1249              * Have the special callback executed:
1250              */
1251             window->Callbacks.Special( keypress, window->State.MouseX, window->State.MouseY );
1252         }
1253
1254         /*
1255          * Thrash the modifiers register now
1256          */
1257         window->State.Modifiers = 0xffffffff;
1258     }
1259     break;
1260
1261     case WM_SYSCHAR:
1262     case WM_CHAR:
1263     {
1264         /*
1265          * First of all, make sure that there is a window to be notified of this
1266          */
1267         assert_window_registered;
1268
1269         /*
1270          * Ignore the automatic key repetition if needed:
1271          */
1272         if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
1273             break;
1274
1275         /*
1276          * Clear to go with the keyboard callback, if registered:
1277          */
1278         if( window->Callbacks.Keyboard != NULL )
1279         {
1280             /*
1281              * Remember the current modifiers state
1282              */
1283             window->State.Modifiers = 
1284                 (GetKeyState( VK_LSHIFT   ) || GetKeyState( VK_RSHIFT   )) ? GLUT_ACTIVE_SHIFT : 0 |
1285                 (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL  : 0 |
1286                 (GetKeyState( VK_LMENU    ) || GetKeyState( VK_RMENU    )) ? GLUT_ACTIVE_ALT   : 0;
1287
1288             /*
1289              * Have the special callback executed:
1290              */
1291             window->Callbacks.Keyboard( wParam, window->State.MouseX, window->State.MouseY );
1292
1293             /*
1294              * Thrash the modifiers register now
1295              */
1296             window->State.Modifiers = 0xffffffff;
1297         }
1298     }
1299     break;
1300
1301     default:
1302         /*
1303          * Handle unhandled messages
1304          */
1305         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1306         break;
1307     }
1308
1309     return( lRet );
1310 }
1311 #endif
1312
1313 /*** END OF FILE ***/