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