86ee47c8bb99713045b20fcaf1c502f27102891b
[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 KeyRelease:
702             case KeyPress:
703                 {
704                     FGCBkeyboard keyboard_cb;
705                     FGCBspecial special_cb;
706
707                     /*
708                      * A key has been pressed, find the window that had the focus:
709                      */
710                     GETWINDOW( xkey ); GETMOUSE( xkey );
711
712                     if( event.type == KeyPress )
713                     {
714                         keyboard_cb = window->Callbacks.Keyboard;
715                         special_cb = window->Callbacks.Special;
716                     }
717                     else
718                     {
719                         keyboard_cb = window->Callbacks.KeyboardUp;
720                         special_cb = window->Callbacks.SpecialUp;
721                     }
722
723                     /*
724                      * Is there a keyboard/special callback hooked for this window?
725                      */
726                     if( (keyboard_cb != NULL) || (special_cb != NULL) )
727                     {
728                         XComposeStatus composeStatus;
729                         char asciiCode[ 32 ];
730                         KeySym keySym;
731                         int len;
732
733                         /*
734                          * Check for the ASCII/KeySym codes associated with the event:
735                          */
736                         len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode), &keySym, &composeStatus );
737
738                         /*
739                          * Get ready to calling the keyboard/special callbacks
740                          */
741                         glutSetWindow( window->ID );
742
743                         /*
744                          * GLUT API tells us to have two separate callbacks...
745                          */
746                         if( len > 0 )
747                         {
748                             /*
749                              * ...one for the ASCII translateable keypresses...
750                              */
751                             if( keyboard_cb != NULL )
752                             {
753                                 /*
754                                  * Remember the current modifiers state
755                                  */
756                                 modifiers = 0;
757                                 if (event.xkey.state & (ShiftMask|LockMask))
758                                     modifiers |= GLUT_ACTIVE_SHIFT;
759                                 if (event.xkey.state & ControlMask)
760                                     modifiers |= GLUT_ACTIVE_CTRL;
761                                 if (event.xkey.state & Mod1Mask)
762                                     modifiers |= GLUT_ACTIVE_ALT;
763                                 window->State.Modifiers = modifiers;
764
765                                 /*
766                                  * Execute the callback
767                                  */
768                                 keyboard_cb( asciiCode[ 0 ], event.xkey.x, event.xkey.y );
769
770                                 /*
771                                  * Trash the modifiers state
772                                  */
773                                 window->State.Modifiers = 0xffffffff;
774                             }
775                         }
776                         else
777                         {
778                             int special = -1;
779
780                             /*
781                              * ...and one for all the others, which need to be translated to GLUT_KEY_Xs...
782                              */
783                             switch( keySym )
784                             {
785                             /*
786                              * First the function keys come:
787                              */
788                             case XK_F1:     special = GLUT_KEY_F1;     break;
789                             case XK_F2:     special = GLUT_KEY_F2;     break;
790                             case XK_F3:     special = GLUT_KEY_F3;     break;
791                             case XK_F4:     special = GLUT_KEY_F4;     break;
792                             case XK_F5:     special = GLUT_KEY_F5;     break;
793                             case XK_F6:     special = GLUT_KEY_F6;     break;
794                             case XK_F7:     special = GLUT_KEY_F7;     break;
795                             case XK_F8:     special = GLUT_KEY_F8;     break;
796                             case XK_F9:     special = GLUT_KEY_F9;     break;
797                             case XK_F10:    special = GLUT_KEY_F10;    break;
798                             case XK_F11:    special = GLUT_KEY_F11;    break;
799                             case XK_F12:    special = GLUT_KEY_F12;    break;
800
801                             /*
802                              * Then the arrows and stuff:
803                              */
804                             case XK_Left:   special = GLUT_KEY_LEFT;   break;
805                             case XK_Right:  special = GLUT_KEY_RIGHT;  break;
806                             case XK_Up:     special = GLUT_KEY_UP;     break;
807                             case XK_Down:   special = GLUT_KEY_DOWN;   break;
808
809                             case XK_KP_Prior:
810                             case XK_Prior:  special = GLUT_KEY_PAGE_UP; break;
811                             case XK_KP_Next:
812                             case XK_Next:   special = GLUT_KEY_PAGE_DOWN; break;
813                             case XK_KP_Home:
814                             case XK_Home:   special = GLUT_KEY_HOME;   break;
815                             case XK_KP_End:
816                             case XK_End:    special = GLUT_KEY_END;    break;
817                             case XK_KP_Insert:
818                             case XK_Insert: special = GLUT_KEY_INSERT; break;
819                             }
820
821                             /*
822                              * Execute the callback (if one has been specified),
823                              * given that the special code seems to be valid...
824                              */
825                             if( (special_cb != NULL) && (special != -1) )
826                             {
827                                 /*
828                                  * Remember the current modifiers state
829                                  */
830                                 modifiers = 0;
831                                 if (event.xkey.state & (ShiftMask|LockMask))
832                                     modifiers |= GLUT_ACTIVE_SHIFT;
833                                 if (event.xkey.state & ControlMask)
834                                     modifiers |= GLUT_ACTIVE_CTRL;
835                                 if (event.xkey.state & Mod1Mask)
836                                     modifiers |= GLUT_ACTIVE_ALT;
837                                 window->State.Modifiers = modifiers;
838
839                                 special_cb( special, event.xkey.x, event.xkey.y );
840
841                                 /*
842                                  * Trash the modifiers state
843                                  */
844                                 window->State.Modifiers = 0xffffffff;
845                             }
846                         }
847                     }
848                 }
849                 break;
850             }
851         }
852         else
853         {
854             /*
855              * Have all the timers checked.
856              */
857             fghCheckTimers();
858
859             /*
860              * Poll the joystick and notify all windows that want to be notified...
861              */
862             fghCheckJoystickPolls();
863
864             /*
865              * No messages in the queue, which means we are idling...
866              */
867             if( fgState.IdleCallback != NULL )
868                 fgState.IdleCallback();
869
870             /*
871              * Remember about displaying all the windows that have
872              * been marked for a redisplay (possibly in the idle call):
873              */
874             fghDisplayAll();
875         }
876     }
877
878 #elif TARGET_HOST_WIN32
879
880     GLboolean bLoop = TRUE;
881     MSG stMsg;
882
883     /*
884      * The windows main loop is considerably smaller
885      */
886     while( bLoop )
887     {
888         if( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
889         {
890             /*
891              * Grab the message now, checking for WM_QUIT
892              */
893             if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
894                 bLoop = FALSE;
895
896             /*
897              * Translate virtual-key messages and send them to the window...
898              */
899             TranslateMessage( &stMsg );
900             DispatchMessage( &stMsg );
901         }
902         else
903         {
904             /*
905              * Have all the timers checked.
906              */
907             fghCheckTimers();
908
909             /*
910              * Poll the joystick and notify all windows that want to be notified...
911              */
912             fghCheckJoystickPolls();
913
914             /*
915              * No messages in the queue, which means we are idling...
916              */
917             if( fgState.IdleCallback != NULL )
918                 fgState.IdleCallback();
919
920             /*
921              * Remember about displaying all the windows that have
922              * been marked for a redisplay (possibly in the idle call):
923              */
924             fghDisplayAll();
925
926             /*
927              * We need to terminate the main loop if no windows are left
928              */
929             bLoop = (fgStructure.Windows.First != NULL);
930         }
931     }
932
933 #endif
934
935     /*
936      * When this loop terminates, destroy the display, state and structure
937      * of a freeglut session, so that another glutInit() call can happen
938      */
939     fgDeinitialize();
940 }
941
942 /*
943  * The window procedure for handling Win32 events
944  */
945 #if TARGET_HOST_WIN32
946 LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
947 {
948     SFG_Window* window = fgWindowByHandle( hWnd );
949     PAINTSTRUCT ps;
950     LONG lRet = 1;
951
952 #   define  assert_window_registered  if( window == NULL ) return( DefWindowProc( hWnd, uMsg, wParam, lParam ) );
953
954     /*
955      * Check what type of message are we receiving
956      */
957     switch( uMsg )
958     {
959     case WM_CREATE:
960         /*
961          * The window structure is passed as the creation structure paramter...
962          */
963         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
964         assert( window != NULL );
965
966         /*
967          * We can safely store the window's handle now:
968          */
969         window->Window.Handle = hWnd;
970
971         /*
972          * Get the window's device context
973          */
974         window->Window.Device = GetDC( hWnd );
975
976         /*
977          * Setup the pixel format of our window
978          */
979         fgSetupPixelFormat( window, FALSE );
980
981         /*
982          * Create the OpenGL rendering context now
983          */
984         window->Window.Context = wglCreateContext( window->Window.Device );
985
986         /*
987          * Still, we'll be needing to explicitly resize the window
988          */
989         window->State.NeedToResize = TRUE;
990
991         /*
992          * Finally, have the window's device context released
993          */
994         ReleaseDC( window->Window.Handle, window->Window.Device );
995         break;
996
997     case WM_SIZE:
998         /*
999          * We got resized... But check if the window has been already added...
1000          */
1001         fghReshapeWindowByHandle( hWnd, LOWORD(lParam), HIWORD(lParam) );
1002         break;
1003
1004     case WM_PAINT:
1005         /*
1006          * Start the painting job
1007          */
1008         BeginPaint( hWnd, &ps );
1009
1010         /*
1011          * Call the engine's main frame drawing method
1012          */
1013         fghRedrawWindowByHandle( hWnd );
1014
1015         /*
1016          * End the painting job, release the device context
1017          */
1018         EndPaint( hWnd, &ps );
1019         break;
1020
1021     case WM_CLOSE:
1022         /*
1023          * Make sure we don't close a window with current context active
1024          */
1025         if( fgStructure.Window == window )
1026         {
1027             wglMakeCurrent( NULL, NULL );
1028             wglDeleteContext( window->Window.Context );
1029         }
1030
1031         /*
1032          * Proceed with the window destruction
1033          */
1034         DestroyWindow( window->Window.Handle );
1035         break;
1036
1037     case WM_DESTROY:
1038         /*
1039          * The window already got destroyed, so forget about it's existence:
1040          */
1041         fgDestroyWindow( window, FALSE );
1042         return( 0 );
1043
1044     case WM_MOUSEMOVE:
1045     {
1046         assert_window_registered;
1047
1048         /*
1049          * The mouse cursor has moved. Remember the new mouse cursor's position
1050          */
1051         window->State.MouseX = LOWORD( lParam );
1052         window->State.MouseY = HIWORD( lParam );
1053
1054         /*
1055          * Fallback if there's an active menu hooked to this window
1056          */
1057         if( window->MenuActive[ 0 ] || window->MenuActive[ 1 ] || window->MenuActive[ 2 ] )
1058             break;
1059
1060         /*
1061          * Remember the current modifiers state.
1062          */
1063         window->State.Modifiers = 
1064             (GetKeyState( VK_LSHIFT   ) || GetKeyState( VK_RSHIFT   )) ? GLUT_ACTIVE_SHIFT : 0 |
1065             (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL  : 0 |
1066             (GetKeyState( VK_LMENU    ) || GetKeyState( VK_RMENU    )) ? GLUT_ACTIVE_ALT   : 0;
1067
1068         /*
1069          * Check if any of the mouse buttons is pressed...
1070          */
1071         if( (wParam & MK_LBUTTON) || (wParam & MK_MBUTTON) || (wParam & MK_RBUTTON) )
1072         {
1073             /*
1074              * Yeah, indeed. We need to use the motion callback then:
1075              */
1076             if( window->Callbacks.Motion != NULL )
1077             {
1078                 /*
1079                  * Make sure the current window is set...
1080                  */
1081                 glutSetWindow( window->ID );
1082
1083                 /*
1084                  * Execute the active mouse motion callback now
1085                  */
1086                 window->Callbacks.Motion( window->State.MouseX, window->State.MouseY );
1087             }
1088         }
1089         else
1090         {
1091             /*
1092              * All mouse buttons are up, execute the passive mouse motion callback
1093              */
1094             if( window->Callbacks.Passive != NULL )
1095             {
1096                 /*
1097                  * Make sure the current window is set
1098                  */
1099                 glutSetWindow( window->ID );
1100
1101                 /*
1102                  * Execute the passive mouse motion callback
1103                  */
1104                 window->Callbacks.Passive( window->State.MouseX, window->State.MouseY );
1105             }
1106         }
1107
1108         /*
1109          * Thrash the current modifiers state now
1110          */
1111         window->State.Modifiers = 0xffffffff;
1112     }
1113     break;
1114
1115     case WM_LBUTTONDOWN:
1116     case WM_MBUTTONDOWN:
1117     case WM_RBUTTONDOWN:
1118     case WM_LBUTTONUP:
1119     case WM_MBUTTONUP:
1120     case WM_RBUTTONUP:
1121     {
1122         GLboolean pressed = TRUE;
1123         int button;
1124
1125         /*
1126          * A mouse button has been pressed *or* released. Again, break off
1127          * if the message was not directed towards a freeglut window...
1128          */
1129         assert_window_registered;
1130
1131         /*
1132          * The mouse cursor has moved. Remember the new mouse cursor's position
1133          */
1134         window->State.MouseX = LOWORD( lParam );
1135         window->State.MouseY = HIWORD( lParam );
1136
1137         /*
1138          * We're curious about the GLUT API button name...
1139          */
1140         switch( uMsg )
1141         {
1142         case WM_LBUTTONDOWN: pressed = TRUE;  button = GLUT_LEFT_BUTTON;   break;
1143         case WM_MBUTTONDOWN: pressed = TRUE;  button = GLUT_MIDDLE_BUTTON; break;
1144         case WM_RBUTTONDOWN: pressed = TRUE;  button = GLUT_RIGHT_BUTTON;  break;
1145         case WM_LBUTTONUP:   pressed = FALSE; button = GLUT_LEFT_BUTTON;   break;
1146         case WM_MBUTTONUP:   pressed = FALSE; button = GLUT_MIDDLE_BUTTON; break;
1147         case WM_RBUTTONUP:   pressed = FALSE; button = GLUT_RIGHT_BUTTON;  break;
1148         default:             pressed = FALSE; button = -1;                 break;
1149         }
1150
1151         /*
1152          * The left and right mouse buttons might have been swapped...
1153          */
1154         if( GetSystemMetrics( SM_SWAPBUTTON ) )
1155             if( button == GLUT_LEFT_BUTTON ) button = GLUT_RIGHT_BUTTON;
1156             else if( button == GLUT_RIGHT_BUTTON ) button = GLUT_LEFT_BUTTON;
1157
1158         /*
1159          * Hey, what's up with you?
1160          */
1161         if( button == -1 )
1162             return( DefWindowProc( hWnd, uMsg, lParam, wParam ) );
1163
1164         /*
1165          * Do not execute the callback if a menu is hooked to this key.
1166          * In that case an appropriate private call should be generated
1167          */
1168         if( window->Menu[ button ] != NULL )
1169         {
1170             /*
1171              * Set the current window
1172              */
1173             glutSetWindow( window->ID );
1174
1175             if( pressed == TRUE )
1176             {
1177                 /*
1178                  * Activate the appropriate menu structure...
1179                  */
1180                 fgActivateMenu( button );
1181             }
1182             else
1183             {
1184                 /*
1185                  * There are two general cases generated when a menu button
1186                  * is released -- it can provoke a menu call (when released
1187                  * over a menu area) or just deactivate the menu (when released
1188                  * somewhere else). Unfortunately, both cases must be checked
1189                  * recursively due to the submenu possibilities.
1190                  */
1191                 fgDeactivateMenu( button );
1192             }
1193             break;
1194         }
1195
1196         /*
1197          * Check if there is a mouse callback hooked to the window
1198          */
1199         if( window->Callbacks.Mouse == NULL )
1200             break;
1201
1202         /*
1203          * Set the current window
1204          */
1205         glutSetWindow( window->ID );
1206
1207         /*
1208          * Remember the current modifiers state.
1209          */
1210         window->State.Modifiers = 
1211             (GetKeyState( VK_LSHIFT   ) || GetKeyState( VK_RSHIFT   )) ? GLUT_ACTIVE_SHIFT : 0 |
1212             (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL  : 0 |
1213             (GetKeyState( VK_LMENU    ) || GetKeyState( VK_RMENU    )) ? GLUT_ACTIVE_ALT   : 0;
1214
1215         /*
1216          * Finally execute the mouse callback
1217          */
1218         window->Callbacks.Mouse(
1219             button,
1220             pressed == TRUE ? GLUT_DOWN : GLUT_UP,
1221             window->State.MouseX,
1222             window->State.MouseY
1223         );
1224
1225         /*
1226          * Trash the modifiers state
1227          */
1228         window->State.Modifiers = 0xffffffff;
1229     }
1230     break;
1231
1232     case WM_SYSKEYDOWN:
1233     case WM_KEYDOWN:
1234     {
1235         int keypress = -1;
1236
1237         /*
1238          * First of all, make sure that there is a window to be notified of this
1239          */
1240         assert_window_registered;
1241
1242         /*
1243          * Ignore the automatic key repetition if needed:
1244          */
1245         if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
1246             break;
1247
1248         /*
1249          * Remember the current modifiers state. This is done here in order 
1250          * to make sure the VK_DELETE keyboard callback is executed properly.
1251          */
1252         window->State.Modifiers = 
1253             (GetKeyState( VK_LSHIFT   ) || GetKeyState( VK_RSHIFT   )) ? GLUT_ACTIVE_SHIFT : 0 |
1254             (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL  : 0 |
1255             (GetKeyState( VK_LMENU    ) || GetKeyState( VK_RMENU    )) ? GLUT_ACTIVE_ALT   : 0;
1256
1257         /*
1258          * Convert the Win32 keystroke codes to GLUTtish way
1259          */
1260 #       define KEY(a,b) case a: keypress = b; break;
1261
1262         switch( wParam )
1263         {
1264             /*
1265              * Most of the special characters can be handled automagically...
1266              */
1267             KEY( VK_F1,     GLUT_KEY_F1        ); KEY( VK_F2,     GLUT_KEY_F2        );
1268             KEY( VK_F3,     GLUT_KEY_F3        ); KEY( VK_F4,     GLUT_KEY_F4        );
1269             KEY( VK_F5,     GLUT_KEY_F5        ); KEY( VK_F6,     GLUT_KEY_F6        );
1270             KEY( VK_F7,     GLUT_KEY_F7        ); KEY( VK_F8,     GLUT_KEY_F8        );
1271             KEY( VK_F9,     GLUT_KEY_F9        ); KEY( VK_F10,    GLUT_KEY_F10       );
1272             KEY( VK_F11,    GLUT_KEY_F11       ); KEY( VK_F12,    GLUT_KEY_F12       );
1273             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   ); KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
1274             KEY( VK_HOME,   GLUT_KEY_HOME      ); KEY( VK_END,    GLUT_KEY_END       );
1275             KEY( VK_LEFT,   GLUT_KEY_LEFT      ); KEY( VK_UP,     GLUT_KEY_UP        );
1276             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     ); KEY( VK_DOWN,   GLUT_KEY_DOWN      );
1277             KEY( VK_INSERT, GLUT_KEY_INSERT    );
1278
1279             /*
1280              * ...yet there is a small exception we need to have handled...
1281              */
1282             case VK_DELETE:
1283                 /*
1284                  * The delete key should be treated as an ASCII keypress:
1285                  */
1286                 if( window->Callbacks.Keyboard != NULL )
1287                     window->Callbacks.Keyboard( 127, window->State.MouseX, window->State.MouseY );
1288         }
1289
1290         /*
1291          * Execute the special callback, if present, given the conversion was a success:
1292          */
1293         if( (keypress != -1) && (window->Callbacks.Special != NULL) )
1294         {
1295             /*
1296              * Have the special callback executed:
1297              */
1298             window->Callbacks.Special( keypress, window->State.MouseX, window->State.MouseY );
1299         }
1300
1301         /*
1302          * Thrash the modifiers register now
1303          */
1304         window->State.Modifiers = 0xffffffff;
1305     }
1306     break;
1307
1308     case WM_SYSCHAR:
1309     case WM_CHAR:
1310     {
1311         /*
1312          * First of all, make sure that there is a window to be notified of this
1313          */
1314         assert_window_registered;
1315
1316         /*
1317          * Ignore the automatic key repetition if needed:
1318          */
1319         if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
1320             break;
1321
1322         /*
1323          * Clear to go with the keyboard callback, if registered:
1324          */
1325         if( window->Callbacks.Keyboard != NULL )
1326         {
1327             /*
1328              * Remember the current modifiers state
1329              */
1330             window->State.Modifiers = 
1331                 (GetKeyState( VK_LSHIFT   ) || GetKeyState( VK_RSHIFT   )) ? GLUT_ACTIVE_SHIFT : 0 |
1332                 (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL  : 0 |
1333                 (GetKeyState( VK_LMENU    ) || GetKeyState( VK_RMENU    )) ? GLUT_ACTIVE_ALT   : 0;
1334
1335             /*
1336              * Have the special callback executed:
1337              */
1338             window->Callbacks.Keyboard( wParam, window->State.MouseX, window->State.MouseY );
1339
1340             /*
1341              * Thrash the modifiers register now
1342              */
1343             window->State.Modifiers = 0xffffffff;
1344         }
1345     }
1346     break;
1347
1348     default:
1349         /*
1350          * Handle unhandled messages
1351          */
1352         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1353         break;
1354     }
1355
1356     return( lRet );
1357 }
1358 #endif
1359
1360 /*** END OF FILE ***/