Fixed redisplay bug.
[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     if( (window->Callbacks.Display != NULL) &&
143         (window->State.Redisplay == TRUE) &&
144         (window->State.Visible == TRUE) )
145     {
146         /*
147          * OKi, this is the case: have the window set as the current one
148          */
149         glutSetWindow( window->ID );
150
151         /*
152          * Do not exagerate with the redisplaying
153          */
154         window->State.Redisplay = FALSE;
155
156         /*
157          * And execute the display callback immediately after
158          */
159         window->Callbacks.Display();
160     }
161
162 #elif TARGET_HOST_WIN32
163
164     /*
165      * Do we need to explicitly resize the window?
166      */
167     if( window->State.NeedToResize )
168     {
169         glutSetWindow( window->ID );
170
171         fghReshapeWindowByHandle( 
172             window->Window.Handle,
173             glutGet( GLUT_WINDOW_WIDTH ),
174             glutGet( GLUT_WINDOW_HEIGHT )
175         );
176
177         /*
178          * Never ever do that again:
179          */
180         window->State.NeedToResize = FALSE;
181     }
182
183     /*
184      * This is done in a bit different way under Windows
185      */
186     RedrawWindow( 
187         window->Window.Handle, NULL, NULL, 
188         RDW_NOERASE | RDW_INTERNALPAINT | RDW_INVALIDATE 
189     );
190
191 #endif
192
193     /*
194      * Process this window's children (if any)
195      */
196     fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
197 }
198
199 /*
200  * Make all windows perform a display call
201  */
202 static void fghDisplayAll( void )
203 {
204     SFG_Enumerator enumerator;
205
206     /*
207      * Uses a method very similiar for fgWindowByHandle...
208      */
209     enumerator.found = FALSE;
210     enumerator.data  =  NULL;
211
212     /*
213      * Start the enumeration now:
214      */
215     fgEnumWindows( fghcbDisplayWindow, &enumerator );
216 }
217
218 /*
219  * Window enumerator callback to check for the joystick polling code
220  */
221 static void fghcbCheckJoystickPolls( SFG_Window *window, SFG_Enumerator *enumerator )
222 {
223     double checkTime = fgElapsedTime();
224
225     /*
226      * Check if actually need to do the poll for the currently enumerated window:
227      */
228     if( window->State.JoystickLastPoll + window->State.JoystickPollRate >= checkTime )
229     {
230         /*
231          * Yeah, that's it. Poll the joystick...
232          */
233         fgJoystickPollWindow( window );
234
235         /*
236          * ...and reset the polling counters:
237          */
238         window->State.JoystickLastPoll = checkTime;
239     }
240
241     /*
242      * Process this window's children (if any)
243      */
244     fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
245 }
246
247 /*
248  * Check all windows for joystick polling
249  */
250 static void fghCheckJoystickPolls( void )
251 {
252     SFG_Enumerator enumerator;
253
254     /*
255      * Uses a method very similiar for fgWindowByHandle...
256      */
257     enumerator.found = FALSE;
258     enumerator.data  =  NULL;
259
260     /*
261      * Start the enumeration now:
262      */
263     fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
264 }
265
266 /*
267  * Check the global timers
268  */
269 static void fghCheckTimers( void )
270 {
271     long checkTime = fgElapsedTime();
272     SFG_Timer *timer, *next;
273     SFG_List timedOut;
274
275     fgListInit(&timedOut);
276
277     /*
278      * For every timer that is waiting for triggering
279      */
280     for( timer = fgState.Timers.First; timer; timer = next )
281     {
282         next = timer->Node.Next;
283
284         /*
285          * Check for the timeout:
286          */
287         if( timer->TriggerTime <= checkTime )
288         {
289             /*
290              * Add the timer to the timed out timers list
291              */
292             fgListRemove( &fgState.Timers, &timer->Node );
293             fgListAppend( &timedOut, &timer->Node );
294         }
295     }
296
297     /*
298      * Now feel free to execute all the hooked and timed out timer callbacks
299      * And delete the timed out timers...
300      */
301     while ( (timer = timedOut.First) )
302     {
303         if( timer->Callback != NULL )
304             timer->Callback( timer->ID );
305         fgListRemove( &timedOut, &timer->Node );
306         free( timer );
307     }
308 }
309
310 /*
311  * Elapsed Time
312  */
313 long fgElapsedTime( void )
314 {
315 #ifndef WIN32
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 #else
326         return (timeGetTime() - fgState.Time.Value);
327 #endif
328 }
329
330 /*
331  * Error Messages.
332  */
333 void fgError( const char *fmt, ... )
334 {
335     va_list ap;
336
337     va_start( ap, fmt );
338
339     fprintf( stderr, "freeglut: ");
340     vfprintf( stderr, fmt, ap );
341     fprintf( stderr, "\n" );
342
343     va_end( ap );
344
345     exit( 1 );
346 }
347
348 void fgWarning( const char *fmt, ... )
349 {
350     va_list ap;
351
352     va_start( ap, fmt );
353
354     fprintf( stderr, "freeglut: ");
355     vfprintf( stderr, fmt, ap );
356     fprintf( stderr, "\n" );
357
358     va_end( ap );
359 }
360
361 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
362
363 /*
364  * Enters the FreeGLUT processing loop. Never returns.
365  */
366 void FGAPIENTRY glutMainLoop( void )
367 {
368 #if TARGET_HOST_UNIX_X11
369     SFG_Window* window;
370     XEvent event;
371     int modifiers;
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                     modifiers = 0;
676                     if (event.xbutton.state & (ShiftMask|LockMask))
677                         modifiers |= GLUT_ACTIVE_SHIFT;
678                     if (event.xbutton.state & ControlMask)
679                         modifiers |= GLUT_ACTIVE_CTRL;
680                     if (event.xbutton.state & Mod1Mask)
681                         modifiers |= GLUT_ACTIVE_ALT;
682                     window->State.Modifiers = modifiers;
683
684                     /*
685                      * Finally execute the mouse callback
686                      */
687                     window->Callbacks.Mouse(
688                         button,
689                         event.type == ButtonPress ? GLUT_DOWN : GLUT_UP,
690                         event.xbutton.x,
691                         event.xbutton.y
692                     );
693
694                     /*
695                      * Trash the modifiers state
696                      */
697                     window->State.Modifiers = 0xffffffff;
698                 }
699                 break;
700
701             case KeyPress:
702                 {
703                     /*
704                      * A key has been pressed, find the window that had the focus:
705                      */
706                     GETWINDOW( xkey ); GETMOUSE( xkey );
707
708                     /*
709                      * Is there a keyboard/special callback hooked for this window?
710                      */
711                     if( (window->Callbacks.Keyboard != NULL) || (window->Callbacks.Special != NULL) )
712                     {
713                         XComposeStatus composeStatus;
714                         char asciiCode[ 32 ];
715                         KeySym keySym;
716                         int len;
717
718                         /*
719                          * Check for the ASCII/KeySym codes associated with the event:
720                          */
721                         len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode), &keySym, &composeStatus );
722
723                         /*
724                          * Get ready to calling the keyboard/special callbacks
725                          */
726                         glutSetWindow( window->ID );
727
728                         /*
729                          * GLUT API tells us to have two separate callbacks...
730                          */
731                         if( len > 0 )
732                         {
733                             /*
734                              * ...one for the ASCII translateable keypresses...
735                              */
736                             if( window->Callbacks.Keyboard != NULL )
737                             {
738                                 /*
739                                  * Remember the current modifiers state
740                                  */
741                                 modifiers = 0;
742                                 if (event.xkey.state & (ShiftMask|LockMask))
743                                     modifiers |= GLUT_ACTIVE_SHIFT;
744                                 if (event.xkey.state & ControlMask)
745                                     modifiers |= GLUT_ACTIVE_CTRL;
746                                 if (event.xkey.state & Mod1Mask)
747                                     modifiers |= GLUT_ACTIVE_ALT;
748                                 window->State.Modifiers = modifiers;
749
750                                 /*
751                                  * Execute the callback
752                                  */
753                                 window->Callbacks.Keyboard( asciiCode[ 0 ], event.xkey.x, event.xkey.y );
754
755                                 /*
756                                  * Trash the modifiers state
757                                  */
758                                 window->State.Modifiers = 0xffffffff;
759                             }
760                         }
761                         else
762                         {
763                             int special = -1;
764
765                             /*
766                              * ...and one for all the others, which need to be translated to GLUT_KEY_Xs...
767                              */
768                             switch( keySym )
769                             {
770                             /*
771                              * First the function keys come:
772                              */
773                             case XK_F1:     special = GLUT_KEY_F1;     break;
774                             case XK_F2:     special = GLUT_KEY_F2;     break;
775                             case XK_F3:     special = GLUT_KEY_F3;     break;
776                             case XK_F4:     special = GLUT_KEY_F4;     break;
777                             case XK_F5:     special = GLUT_KEY_F5;     break;
778                             case XK_F6:     special = GLUT_KEY_F6;     break;
779                             case XK_F7:     special = GLUT_KEY_F7;     break;
780                             case XK_F8:     special = GLUT_KEY_F8;     break;
781                             case XK_F9:     special = GLUT_KEY_F9;     break;
782                             case XK_F10:    special = GLUT_KEY_F10;    break;
783                             case XK_F11:    special = GLUT_KEY_F11;    break;
784                             case XK_F12:    special = GLUT_KEY_F12;    break;
785
786                             /*
787                              * Then the arrows and stuff:
788                              */
789                             case XK_Left:   special = GLUT_KEY_LEFT;   break;
790                             case XK_Right:  special = GLUT_KEY_RIGHT;  break;
791                             case XK_Up:     special = GLUT_KEY_UP;     break;
792                             case XK_Down:   special = GLUT_KEY_DOWN;   break;
793                             }
794
795                             /*
796                              * Execute the callback (if one has been specified),
797                              * given that the special code seems to be valid...
798                              */
799                             if( (window->Callbacks.Special != NULL) && (special != -1) )
800                             {
801                                 /*
802                                  * Remember the current modifiers state
803                                  */
804                                 modifiers = 0;
805                                 if (event.xkey.state & (ShiftMask|LockMask))
806                                     modifiers |= GLUT_ACTIVE_SHIFT;
807                                 if (event.xkey.state & ControlMask)
808                                     modifiers |= GLUT_ACTIVE_CTRL;
809                                 if (event.xkey.state & Mod1Mask)
810                                     modifiers |= GLUT_ACTIVE_ALT;
811                                 window->State.Modifiers = modifiers;
812
813                                 window->Callbacks.Special( special, event.xkey.x, event.xkey.y );
814
815                                 /*
816                                  * Trash the modifiers state
817                                  */
818                                 window->State.Modifiers = 0xffffffff;
819                             }
820                         }
821                     }
822                 }
823                 break;
824             }
825         }
826         else
827         {
828             /*
829              * Have all the timers checked.
830              */
831             fghCheckTimers();
832
833             /*
834              * Poll the joystick and notify all windows that want to be notified...
835              */
836             fghCheckJoystickPolls();
837
838             /*
839              * No messages in the queue, which means we are idling...
840              */
841             if( fgState.IdleCallback != NULL )
842                 fgState.IdleCallback();
843
844             /*
845              * Remember about displaying all the windows that have
846              * been marked for a redisplay (possibly in the idle call):
847              */
848             fghDisplayAll();
849         }
850     }
851
852 #elif TARGET_HOST_WIN32
853
854     GLboolean bLoop = TRUE;
855     MSG stMsg;
856
857     /*
858      * The windows main loop is considerably smaller
859      */
860     while( bLoop )
861     {
862         if( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
863         {
864             /*
865              * Grab the message now, checking for WM_QUIT
866              */
867             if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
868                 bLoop = FALSE;
869
870             /*
871              * Translate virtual-key messages and send them to the window...
872              */
873             TranslateMessage( &stMsg );
874             DispatchMessage( &stMsg );
875         }
876         else
877         {
878             /*
879              * Have all the timers checked.
880              */
881             fghCheckTimers();
882
883             /*
884              * Poll the joystick and notify all windows that want to be notified...
885              */
886             fghCheckJoystickPolls();
887
888             /*
889              * No messages in the queue, which means we are idling...
890              */
891             if( fgState.IdleCallback != NULL )
892                 fgState.IdleCallback();
893
894             /*
895              * Remember about displaying all the windows that have
896              * been marked for a redisplay (possibly in the idle call):
897              */
898             fghDisplayAll();
899
900             /*
901              * We need to terminate the main loop if no windows are left
902              */
903             bLoop = (g_list_length( fgStructure.Windows ) != 0);
904         }
905     }
906
907 #endif
908
909     /*
910      * When this loop terminates, destroy the display, state and structure
911      * of a freeglut session, so that another glutInit() call can happen
912      */
913     fgDeinitialize();
914 }
915
916 /*
917  * The window procedure for handling Win32 events
918  */
919 #if TARGET_HOST_WIN32
920 LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
921 {
922     SFG_Window* window = fgWindowByHandle( hWnd );
923     PAINTSTRUCT ps;
924     LONG lRet = 1;
925
926 #   define  assert_window_registered  if( window == NULL ) return( DefWindowProc( hWnd, uMsg, wParam, lParam ) );
927
928     /*
929      * Check what type of message are we receiving
930      */
931     switch( uMsg )
932     {
933     case WM_CREATE:
934         /*
935          * The window structure is passed as the creation structure paramter...
936          */
937         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
938         assert( window != NULL );
939
940         /*
941          * We can safely store the window's handle now:
942          */
943         window->Window.Handle = hWnd;
944
945         /*
946          * Get the window's device context
947          */
948         window->Window.Device = GetDC( hWnd );
949
950         /*
951          * Setup the pixel format of our window
952          */
953         fgSetupPixelFormat( window, FALSE );
954
955         /*
956          * Create the OpenGL rendering context now
957          */
958         window->Window.Context = wglCreateContext( window->Window.Device );
959
960         /*
961          * Still, we'll be needing to explicitly resize the window
962          */
963         window->State.NeedToResize = TRUE;
964
965         /*
966          * Finally, have the window's device context released
967          */
968         ReleaseDC( window->Window.Handle, window->Window.Device );
969         break;
970
971     case WM_SIZE:
972         /*
973          * We got resized... But check if the window has been already added...
974          */
975         fghReshapeWindowByHandle( hWnd, LOWORD(lParam), HIWORD(lParam) );
976         break;
977
978     case WM_PAINT:
979         /*
980          * Start the painting job
981          */
982         BeginPaint( hWnd, &ps );
983
984         /*
985          * Call the engine's main frame drawing method
986          */
987         fghRedrawWindowByHandle( hWnd );
988
989         /*
990          * End the painting job, release the device context
991          */
992         EndPaint( hWnd, &ps );
993         break;
994
995     case WM_CLOSE:
996         /*
997          * Make sure we don't close a window with current context active
998          */
999         if( fgStructure.Window == window )
1000         {
1001             wglMakeCurrent( NULL, NULL );
1002             wglDeleteContext( window->Window.Context );
1003         }
1004
1005         /*
1006          * Proceed with the window destruction
1007          */
1008         DestroyWindow( window->Window.Handle );
1009         break;
1010
1011     case WM_DESTROY:
1012         /*
1013          * The window already got destroyed, so forget about it's existence:
1014          */
1015         fgDestroyWindow( window, FALSE );
1016         return( 0 );
1017
1018     case WM_MOUSEMOVE:
1019     {
1020         assert_window_registered;
1021
1022         /*
1023          * The mouse cursor has moved. Remember the new mouse cursor's position
1024          */
1025         window->State.MouseX = LOWORD( lParam );
1026         window->State.MouseY = HIWORD( lParam );
1027
1028         /*
1029          * Fallback if there's an active menu hooked to this window
1030          */
1031         if( window->MenuActive[ 0 ] || window->MenuActive[ 1 ] || window->MenuActive[ 2 ] )
1032             break;
1033
1034         /*
1035          * Remember the current modifiers state.
1036          */
1037         window->State.Modifiers = 
1038             (GetKeyState( VK_LSHIFT   ) || GetKeyState( VK_RSHIFT   )) ? GLUT_ACTIVE_SHIFT : 0 |
1039             (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL  : 0 |
1040             (GetKeyState( VK_LMENU    ) || GetKeyState( VK_RMENU    )) ? GLUT_ACTIVE_ALT   : 0;
1041
1042         /*
1043          * Check if any of the mouse buttons is pressed...
1044          */
1045         if( (wParam & MK_LBUTTON) || (wParam & MK_MBUTTON) || (wParam & MK_RBUTTON) )
1046         {
1047             /*
1048              * Yeah, indeed. We need to use the motion callback then:
1049              */
1050             if( window->Callbacks.Motion != NULL )
1051             {
1052                 /*
1053                  * Make sure the current window is set...
1054                  */
1055                 glutSetWindow( window->ID );
1056
1057                 /*
1058                  * Execute the active mouse motion callback now
1059                  */
1060                 window->Callbacks.Motion( window->State.MouseX, window->State.MouseY );
1061             }
1062         }
1063         else
1064         {
1065             /*
1066              * All mouse buttons are up, execute the passive mouse motion callback
1067              */
1068             if( window->Callbacks.Passive != NULL )
1069             {
1070                 /*
1071                  * Make sure the current window is set
1072                  */
1073                 glutSetWindow( window->ID );
1074
1075                 /*
1076                  * Execute the passive mouse motion callback
1077                  */
1078                 window->Callbacks.Passive( window->State.MouseX, window->State.MouseY );
1079             }
1080         }
1081
1082         /*
1083          * Thrash the current modifiers state now
1084          */
1085         window->State.Modifiers = 0xffffffff;
1086     }
1087     break;
1088
1089     case WM_LBUTTONDOWN:
1090     case WM_MBUTTONDOWN:
1091     case WM_RBUTTONDOWN:
1092     case WM_LBUTTONUP:
1093     case WM_MBUTTONUP:
1094     case WM_RBUTTONUP:
1095     {
1096         GLboolean pressed = TRUE;
1097         int button;
1098
1099         /*
1100          * A mouse button has been pressed *or* released. Again, break off
1101          * if the message was not directed towards a freeglut window...
1102          */
1103         assert_window_registered;
1104
1105         /*
1106          * The mouse cursor has moved. Remember the new mouse cursor's position
1107          */
1108         window->State.MouseX = LOWORD( lParam );
1109         window->State.MouseY = HIWORD( lParam );
1110
1111         /*
1112          * We're curious about the GLUT API button name...
1113          */
1114         switch( uMsg )
1115         {
1116         case WM_LBUTTONDOWN: pressed = TRUE;  button = GLUT_LEFT_BUTTON;   break;
1117         case WM_MBUTTONDOWN: pressed = TRUE;  button = GLUT_MIDDLE_BUTTON; break;
1118         case WM_RBUTTONDOWN: pressed = TRUE;  button = GLUT_RIGHT_BUTTON;  break;
1119         case WM_LBUTTONUP:   pressed = FALSE; button = GLUT_LEFT_BUTTON;   break;
1120         case WM_MBUTTONUP:   pressed = FALSE; button = GLUT_MIDDLE_BUTTON; break;
1121         case WM_RBUTTONUP:   pressed = FALSE; button = GLUT_RIGHT_BUTTON;  break;
1122         default:             pressed = FALSE; button = -1;                 break;
1123         }
1124
1125         /*
1126          * The left and right mouse buttons might have been swapped...
1127          */
1128         if( GetSystemMetrics( SM_SWAPBUTTON ) )
1129             if( button == GLUT_LEFT_BUTTON ) button = GLUT_RIGHT_BUTTON;
1130             else if( button == GLUT_RIGHT_BUTTON ) button = GLUT_LEFT_BUTTON;
1131
1132         /*
1133          * Hey, what's up with you?
1134          */
1135         if( button == -1 )
1136             return( DefWindowProc( hWnd, uMsg, lParam, wParam ) );
1137
1138         /*
1139          * Do not execute the callback if a menu is hooked to this key.
1140          * In that case an appropriate private call should be generated
1141          */
1142         if( window->Menu[ button ] != NULL )
1143         {
1144             /*
1145              * Set the current window
1146              */
1147             glutSetWindow( window->ID );
1148
1149             if( pressed == TRUE )
1150             {
1151                 /*
1152                  * Activate the appropriate menu structure...
1153                  */
1154                 fgActivateMenu( button );
1155             }
1156             else
1157             {
1158                 /*
1159                  * There are two general cases generated when a menu button
1160                  * is released -- it can provoke a menu call (when released
1161                  * over a menu area) or just deactivate the menu (when released
1162                  * somewhere else). Unfortunately, both cases must be checked
1163                  * recursively due to the submenu possibilities.
1164                  */
1165                 fgDeactivateMenu( button );
1166             }
1167             break;
1168         }
1169
1170         /*
1171          * Check if there is a mouse callback hooked to the window
1172          */
1173         if( window->Callbacks.Mouse == NULL )
1174             break;
1175
1176         /*
1177          * Set the current window
1178          */
1179         glutSetWindow( window->ID );
1180
1181         /*
1182          * Remember the current modifiers state.
1183          */
1184         window->State.Modifiers = 
1185             (GetKeyState( VK_LSHIFT   ) || GetKeyState( VK_RSHIFT   )) ? GLUT_ACTIVE_SHIFT : 0 |
1186             (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL  : 0 |
1187             (GetKeyState( VK_LMENU    ) || GetKeyState( VK_RMENU    )) ? GLUT_ACTIVE_ALT   : 0;
1188
1189         /*
1190          * Finally execute the mouse callback
1191          */
1192         window->Callbacks.Mouse(
1193             button,
1194             pressed == TRUE ? GLUT_DOWN : GLUT_UP,
1195             window->State.MouseX,
1196             window->State.MouseY
1197         );
1198
1199         /*
1200          * Trash the modifiers state
1201          */
1202         window->State.Modifiers = 0xffffffff;
1203     }
1204     break;
1205
1206     case WM_SYSKEYDOWN:
1207     case WM_KEYDOWN:
1208     {
1209         int keypress = -1;
1210
1211         /*
1212          * First of all, make sure that there is a window to be notified of this
1213          */
1214         assert_window_registered;
1215
1216         /*
1217          * Ignore the automatic key repetition if needed:
1218          */
1219         if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
1220             break;
1221
1222         /*
1223          * Remember the current modifiers state. This is done here in order 
1224          * to make sure the VK_DELETE keyboard callback is executed properly.
1225          */
1226         window->State.Modifiers = 
1227             (GetKeyState( VK_LSHIFT   ) || GetKeyState( VK_RSHIFT   )) ? GLUT_ACTIVE_SHIFT : 0 |
1228             (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL  : 0 |
1229             (GetKeyState( VK_LMENU    ) || GetKeyState( VK_RMENU    )) ? GLUT_ACTIVE_ALT   : 0;
1230
1231         /*
1232          * Convert the Win32 keystroke codes to GLUTtish way
1233          */
1234 #       define KEY(a,b) case a: keypress = b; break;
1235
1236         switch( wParam )
1237         {
1238             /*
1239              * Most of the special characters can be handled automagically...
1240              */
1241             KEY( VK_F1,     GLUT_KEY_F1        ); KEY( VK_F2,     GLUT_KEY_F2        );
1242             KEY( VK_F3,     GLUT_KEY_F3        ); KEY( VK_F4,     GLUT_KEY_F4        );
1243             KEY( VK_F5,     GLUT_KEY_F5        ); KEY( VK_F6,     GLUT_KEY_F6        );
1244             KEY( VK_F7,     GLUT_KEY_F7        ); KEY( VK_F8,     GLUT_KEY_F8        );
1245             KEY( VK_F9,     GLUT_KEY_F9        ); KEY( VK_F10,    GLUT_KEY_F10       );
1246             KEY( VK_F11,    GLUT_KEY_F11       ); KEY( VK_F12,    GLUT_KEY_F12       );
1247             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   ); KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
1248             KEY( VK_HOME,   GLUT_KEY_HOME      ); KEY( VK_END,    GLUT_KEY_END       );
1249             KEY( VK_LEFT,   GLUT_KEY_LEFT      ); KEY( VK_UP,     GLUT_KEY_UP        );
1250             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     ); KEY( VK_DOWN,   GLUT_KEY_DOWN      );
1251             KEY( VK_INSERT, GLUT_KEY_INSERT    );
1252
1253             /*
1254              * ...yet there is a small exception we need to have handled...
1255              */
1256             case VK_DELETE:
1257                 /*
1258                  * The delete key should be treated as an ASCII keypress:
1259                  */
1260                 if( window->Callbacks.Keyboard != NULL )
1261                     window->Callbacks.Keyboard( 127, window->State.MouseX, window->State.MouseY );
1262         }
1263
1264         /*
1265          * Execute the special callback, if present, given the conversion was a success:
1266          */
1267         if( (keypress != -1) && (window->Callbacks.Special != NULL) )
1268         {
1269             /*
1270              * Have the special callback executed:
1271              */
1272             window->Callbacks.Special( keypress, window->State.MouseX, window->State.MouseY );
1273         }
1274
1275         /*
1276          * Thrash the modifiers register now
1277          */
1278         window->State.Modifiers = 0xffffffff;
1279     }
1280     break;
1281
1282     case WM_SYSCHAR:
1283     case WM_CHAR:
1284     {
1285         /*
1286          * First of all, make sure that there is a window to be notified of this
1287          */
1288         assert_window_registered;
1289
1290         /*
1291          * Ignore the automatic key repetition if needed:
1292          */
1293         if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
1294             break;
1295
1296         /*
1297          * Clear to go with the keyboard callback, if registered:
1298          */
1299         if( window->Callbacks.Keyboard != NULL )
1300         {
1301             /*
1302              * Remember the current modifiers state
1303              */
1304             window->State.Modifiers = 
1305                 (GetKeyState( VK_LSHIFT   ) || GetKeyState( VK_RSHIFT   )) ? GLUT_ACTIVE_SHIFT : 0 |
1306                 (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL  : 0 |
1307                 (GetKeyState( VK_LMENU    ) || GetKeyState( VK_RMENU    )) ? GLUT_ACTIVE_ALT   : 0;
1308
1309             /*
1310              * Have the special callback executed:
1311              */
1312             window->Callbacks.Keyboard( wParam, window->State.MouseX, window->State.MouseY );
1313
1314             /*
1315              * Thrash the modifiers register now
1316              */
1317             window->State.Modifiers = 0xffffffff;
1318         }
1319     }
1320     break;
1321
1322     default:
1323         /*
1324          * Handle unhandled messages
1325          */
1326         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1327         break;
1328     }
1329
1330     return( lRet );
1331 }
1332 #endif
1333
1334 /*** END OF FILE ***/