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