1fae543b98a81bc5182d31c1492a7787ca685f36
[freeglut] / src / 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
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include <GL/freeglut.h>
34 #include "freeglut_internal.h"
35
36 #include <limits.h>
37 #if TARGET_HOST_UNIX_X11
38 #include <sys/types.h>
39 #include <sys/time.h>
40 #include <unistd.h>
41 #include <errno.h>
42 #include <sys/stat.h>
43 #elif TARGET_HOST_WIN32
44 #elif TARGET_HOST_WINCE
45
46 typedef struct GXDisplayProperties GXDisplayProperties;
47 typedef struct GXKeyList GXKeyList;
48 #include <gx.h>
49
50 typedef struct GXKeyList (*GXGETDEFAULTKEYS)(int);
51 typedef int (*GXOPENINPUT)();
52
53 GXGETDEFAULTKEYS GXGetDefaultKeys_ = NULL;
54 GXOPENINPUT GXOpenInput_ = NULL;
55
56 struct GXKeyList gxKeyList;
57
58 #endif
59
60 #ifndef MAX
61 #define MAX(a,b) (((a)>(b)) ? (a) : (b))
62 #endif
63
64 #ifndef MIN
65 #define MIN(a,b) (((a)<(b)) ? (a) : (b))
66 #endif
67
68
69 /*
70  * TODO BEFORE THE STABLE RELEASE:
71  *
72  * There are some issues concerning window redrawing under X11, and maybe
73  * some events are not handled. The Win32 version lacks some more features,
74  * but seems acceptable for not demanding purposes.
75  *
76  * Need to investigate why the X11 version breaks out with an error when
77  * closing a window (using the window manager, not glutDestroyWindow)...
78  */
79
80 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
81
82 /*
83  * Handle a window configuration change. When no reshape
84  * callback is hooked, the viewport size is updated to
85  * match the new window size.
86  */
87 static void fghReshapeWindow ( SFG_Window *window, int width, int height )
88 {
89     SFG_Window *current_window = fgStructure.Window;
90
91     freeglut_return_if_fail( window != NULL );
92
93
94 #if TARGET_HOST_UNIX_X11
95
96     XResizeWindow( fgDisplay.Display, window->Window.Handle,
97                    width, height );
98     XFlush( fgDisplay.Display ); /* XXX Shouldn't need this */
99
100 #elif TARGET_HOST_WIN32 || TARGET_HOST_WINCE
101
102 #if !TARGET_HOST_WINCE
103     {
104         RECT winRect;
105         int x, y, w, h;
106
107         /*
108          * For windowed mode, get the current position of the
109          * window and resize taking the size of the frame
110          * decorations into account.
111          */
112
113         /* "GetWindowRect" returns the pixel coordinates of the outside of the window */
114         GetWindowRect( window->Window.Handle, &winRect );
115         x = winRect.left;
116         y = winRect.top;
117         w = width;
118         h = height;
119
120         if ( window->Parent == NULL )
121         {
122             if ( ! window->IsMenu && !window->State.IsGameMode )
123             {
124                 w += GetSystemMetrics( SM_CXSIZEFRAME ) * 2;
125                 h += GetSystemMetrics( SM_CYSIZEFRAME ) * 2 +
126                      GetSystemMetrics( SM_CYCAPTION );
127             }
128         }
129         else
130         {
131             RECT parentRect;
132             GetWindowRect( window->Parent->Window.Handle, &parentRect );
133             x -= parentRect.left + GetSystemMetrics( SM_CXSIZEFRAME ) * 2;
134             y -= parentRect.top  + GetSystemMetrics( SM_CYSIZEFRAME ) * 2 +
135                                    GetSystemMetrics( SM_CYCAPTION );
136         }
137
138         /*
139          * SWP_NOACTIVATE      Do not activate the window
140          * SWP_NOOWNERZORDER   Do not change position in z-order
141          * SWP_NOSENDCHANGING  Supress WM_WINDOWPOSCHANGING message
142          * SWP_NOZORDER        Retains the current Z order (ignore 2nd param)
143          */
144
145         SetWindowPos( window->Window.Handle,
146                       HWND_TOP,
147                       x, y, w, h,
148                       SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING |
149                       SWP_NOZORDER
150         );
151     }
152 #endif /* TARGET_HOST_WINCE */
153
154     /*
155      * XXX Should update {window->State.OldWidth, window->State.OldHeight}
156      * XXX to keep in lockstep with UNIX_X11 code.
157      */
158     if( FETCH_WCB( *window, Reshape ) )
159         INVOKE_WCB( *window, Reshape, ( width, height ) );
160     else
161     {
162         fgSetWindow( window );
163         glViewport( 0, 0, width, height );
164     }
165
166 #endif
167
168     /*
169      * Force a window redraw.  In Windows at least this is only a partial
170      * solution:  if the window is increasing in size in either dimension,
171      * the already-drawn part does not get drawn again and things look funny.
172      * But without this we get this bad behaviour whenever we resize the
173      * window.
174      */
175     window->State.Redisplay = GL_TRUE;
176
177     if( window->IsMenu )
178         fgSetWindow( current_window );
179 }
180
181 /*
182  * Calls a window's redraw method. This is used when
183  * a redraw is forced by the incoming window messages.
184  */
185 static void fghRedrawWindow ( SFG_Window *window )
186 {
187     SFG_Window *current_window = fgStructure.Window;
188
189     freeglut_return_if_fail( window );
190     freeglut_return_if_fail( FETCH_WCB ( *window, Display ) );
191
192     window->State.Redisplay = GL_FALSE;
193
194     freeglut_return_if_fail( window->State.Visible );
195
196     fgSetWindow( window );
197
198     if( window->State.NeedToResize )
199     {
200         fghReshapeWindow(
201             window,
202             window->State.Width,
203             window->State.Height
204         );
205
206         window->State.NeedToResize = GL_FALSE;
207     }
208
209     INVOKE_WCB( *window, Display, ( ) );
210
211     fgSetWindow( current_window );
212 }
213
214 /*
215  * A static helper function to execute display callback for a window
216  */
217 static void fghcbDisplayWindow( SFG_Window *window,
218                                 SFG_Enumerator *enumerator )
219 {
220     if( window->State.Redisplay &&
221         window->State.Visible )
222     {
223         window->State.Redisplay = GL_FALSE;
224
225 #if TARGET_HOST_UNIX_X11
226         fghRedrawWindow ( window ) ;
227 #elif TARGET_HOST_WIN32 || TARGET_HOST_WINCE
228         RedrawWindow(
229             window->Window.Handle, NULL, NULL,
230             RDW_NOERASE | RDW_INTERNALPAINT | RDW_INVALIDATE | RDW_UPDATENOW
231         );
232 #endif
233     }
234
235     fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
236 }
237
238 /*
239  * Make all windows perform a display call
240  */
241 static void fghDisplayAll( void )
242 {
243     SFG_Enumerator enumerator;
244
245     enumerator.found = GL_FALSE;
246     enumerator.data  =  NULL;
247
248     fgEnumWindows( fghcbDisplayWindow, &enumerator );
249 }
250
251 /*
252  * Window enumerator callback to check for the joystick polling code
253  */
254 static void fghcbCheckJoystickPolls( SFG_Window *window,
255                                      SFG_Enumerator *enumerator )
256 {
257     long int checkTime = fgElapsedTime( );
258
259     if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=
260         checkTime )
261     {
262 #if !TARGET_HOST_WINCE
263         fgJoystickPollWindow( window );
264 #endif /* !TARGET_HOST_WINCE */
265         window->State.JoystickLastPoll = checkTime;
266     }
267
268     fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
269 }
270
271 /*
272  * Check all windows for joystick polling
273  */
274 static void fghCheckJoystickPolls( void )
275 {
276     SFG_Enumerator enumerator;
277
278     enumerator.found = GL_FALSE;
279     enumerator.data  =  NULL;
280
281     fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
282 }
283
284 /*
285  * Check the global timers
286  */
287 static void fghCheckTimers( void )
288 {
289     long checkTime = fgElapsedTime( );
290
291     while( fgState.Timers.First )
292     {
293         SFG_Timer *timer = fgState.Timers.First;
294
295         if( timer->TriggerTime > checkTime )
296             break;
297
298         fgListRemove( &fgState.Timers, &timer->Node );
299         fgListAppend( &fgState.FreeTimers, &timer->Node );
300
301         timer->Callback( timer->ID );
302     }
303 }
304
305 /*
306  * Elapsed Time
307  */
308 long fgElapsedTime( void )
309 {
310     if ( fgState.Time.Set )
311     {
312 #if TARGET_HOST_UNIX_X11
313         struct timeval now;
314         long elapsed;
315
316         gettimeofday( &now, NULL );
317
318         elapsed = (now.tv_usec - fgState.Time.Value.tv_usec) / 1000;
319         elapsed += (now.tv_sec - fgState.Time.Value.tv_sec) * 1000;
320
321         return elapsed;
322 #elif TARGET_HOST_WIN32
323         return timeGetTime() - fgState.Time.Value;
324 #elif TARGET_HOST_WINCE
325         return GetTickCount() - fgState.Time.Value;
326 #endif
327     }
328     else
329     {
330 #if TARGET_HOST_UNIX_X11
331         gettimeofday( &fgState.Time.Value, NULL );
332 #elif TARGET_HOST_WIN32
333         fgState.Time.Value = timeGetTime ();
334 #elif TARGET_HOST_WINCE
335         fgState.Time.Value = GetTickCount();
336 #endif
337         fgState.Time.Set = GL_TRUE ;
338
339         return 0 ;
340     }
341 }
342
343 /*
344  * Error Messages.
345  */
346 void fgError( const char *fmt, ... )
347 {
348     va_list ap;
349
350     va_start( ap, fmt );
351
352     fprintf( stderr, "freeglut ");
353     if( fgState.ProgramName )
354         fprintf (stderr, "(%s): ", fgState.ProgramName);
355     vfprintf( stderr, fmt, ap );
356     fprintf( stderr, "\n" );
357
358     va_end( ap );
359
360     if ( fgState.Initialised )
361         fgDeinitialize ();
362
363     exit( 1 );
364 }
365
366 void fgWarning( const char *fmt, ... )
367 {
368     va_list ap;
369
370     va_start( ap, fmt );
371
372     fprintf( stderr, "freeglut ");
373     if( fgState.ProgramName )
374         fprintf( stderr, "(%s): ", fgState.ProgramName );
375     vfprintf( stderr, fmt, ap );
376     fprintf( stderr, "\n" );
377
378     va_end( ap );
379 }
380
381 /*
382  * Indicates whether Joystick events are being used by ANY window.
383  *
384  * The current mechanism is to walk all of the windows and ask if
385  * there is a joystick callback.  We have a short-circuit early
386  * return if we find any joystick handler registered.
387  *
388  * The real way to do this is to make use of the glutTimer() API
389  * to more cleanly re-implement the joystick API.  Then, this code
390  * and all other "joystick timer" code can be yanked.
391  *
392  */
393 static void fghCheckJoystickCallback( SFG_Window* w, SFG_Enumerator* e)
394 {
395     if( FETCH_WCB( *w, Joystick ) )
396     {
397         e->found = GL_TRUE;
398         e->data = w;
399     }
400     fgEnumSubWindows( w, fghCheckJoystickCallback, e );
401 }
402 static int fghHaveJoystick( void )
403 {
404     SFG_Enumerator enumerator;
405     enumerator.found = GL_FALSE;
406     enumerator.data = NULL;
407     fgEnumWindows( fghCheckJoystickCallback, &enumerator );
408     return !!enumerator.data;
409 }
410 static void fghHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)
411 {
412     if( w->State.Redisplay )
413     {
414         e->found = GL_TRUE;
415         e->data = w;
416     }
417     fgEnumSubWindows( w, fghHavePendingRedisplaysCallback, e );
418 }
419 static int fghHavePendingRedisplays (void)
420 {
421     SFG_Enumerator enumerator;
422     enumerator.found = GL_FALSE;
423     enumerator.data = NULL;
424     fgEnumWindows( fghHavePendingRedisplaysCallback, &enumerator );
425     return !!enumerator.data;
426 }
427 /*
428  * Returns the number of GLUT ticks (milliseconds) till the next timer event.
429  */
430 static long fghNextTimer( void )
431 {
432     long ret = INT_MAX;
433     SFG_Timer *timer = fgState.Timers.First;
434
435     if( timer )
436         ret = timer->TriggerTime - fgElapsedTime();
437     if( ret < 0 )
438         ret = 0;
439
440     return ret;
441 }
442 /*
443  * Does the magic required to relinquish the CPU until something interesting
444  * happens.
445  */
446 static void fghSleepForEvents( void )
447 {
448     long msec;
449
450     if( fgState.IdleCallback || fghHavePendingRedisplays( ) )
451         return;
452
453     msec = fghNextTimer( );
454     if( fghHaveJoystick( ) )     /* XXX Use GLUT timers for joysticks... */
455         msec = MIN( msec, 10 );  /* XXX Dumb; forces granularity to .01sec */
456
457 #if TARGET_HOST_UNIX_X11
458     /*
459      * Possibly due to aggressive use of XFlush() and friends,
460      * it is possible to have our socket drained but still have
461      * unprocessed events.  (Or, this may just be normal with
462      * X, anyway?)  We do non-trivial processing of X events
463      * after the event-reading loop, in any case, so we
464      * need to allow that we may have an empty socket but non-
465      * empty event queue.
466      */
467     if( ! XPending( fgDisplay.Display ) )
468     {
469         fd_set fdset;
470         int err;
471         int socket;
472         struct timeval wait;
473
474         socket = ConnectionNumber( fgDisplay.Display );
475         FD_ZERO( &fdset );
476         FD_SET( socket, &fdset );
477         wait.tv_sec = msec / 1000;
478         wait.tv_usec = (msec % 1000) * 1000;
479         err = select( socket+1, &fdset, NULL, NULL, &wait );
480
481         if( -1 == err )
482             fgWarning ( "freeglut select() error: %d", errno );
483     }
484 #elif TARGET_HOST_WIN32 || TARGET_HOST_WINCE
485     MsgWaitForMultipleObjects( 0, NULL, FALSE, msec, QS_ALLEVENTS );
486 #endif
487 }
488
489 #if TARGET_HOST_UNIX_X11
490 /*
491  * Returns GLUT modifier mask for an XEvent.
492  */
493 static int fghGetXModifiers( XEvent *event )
494 {
495     int ret = 0;
496
497     if( event->xkey.state & ( ShiftMask | LockMask ) )
498         ret |= GLUT_ACTIVE_SHIFT;
499     if( event->xkey.state & ControlMask )
500         ret |= GLUT_ACTIVE_CTRL;
501     if( event->xkey.state & Mod1Mask )
502         ret |= GLUT_ACTIVE_ALT;
503
504     return ret;
505 }
506 #endif
507
508
509 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
510
511 /*
512  * Executes a single iteration in the freeglut processing loop.
513  */
514 void FGAPIENTRY glutMainLoopEvent( void )
515 {
516 #if TARGET_HOST_UNIX_X11
517     SFG_Window* window;
518     XEvent event;
519
520     /* This code was repeated constantly, so here it goes into a definition: */
521 #define GETWINDOW(a)                             \
522     window = fgWindowByHandle( event.a.window ); \
523     if( window == NULL )                         \
524         break;
525
526 #define GETMOUSE(a)                              \
527     window->State.MouseX = event.a.x;            \
528     window->State.MouseY = event.a.y;
529
530     freeglut_assert_ready;
531
532     while( XPending( fgDisplay.Display ) )
533     {
534         XNextEvent( fgDisplay.Display, &event );
535
536         switch( event.type )
537         {
538         case ClientMessage:
539             /* Destroy the window when the WM_DELETE_WINDOW message arrives */
540             if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.DeleteWindow )
541             {
542                 GETWINDOW( xclient );
543
544                 fgDestroyWindow ( window );
545
546                 if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
547                 {
548                     fgDeinitialize( );
549                     exit( 0 );
550                 }
551                 else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
552                     fgState.ExecState = GLUT_EXEC_STATE_STOP;
553
554                 return;
555             }
556             break;
557
558             /*
559              * CreateNotify causes a configure-event so that sub-windows are
560              * handled compatibly with GLUT.  Otherwise, your sub-windows
561              * (in freeglut only) will not get an initial reshape event,
562              * which can break things.
563              *
564              * GLUT presumably does this because it generally tries to treat
565              * sub-windows the same as windows.
566              *
567              * XXX Technically, GETWINDOW( xconfigure ) and
568              * XXX {event.xconfigure} may not be legit ways to get at
569              * XXX data for CreateNotify events.  In practice, the data
570              * XXX is in a union which is laid out much the same either
571              * XXX way.  But if you want to split hairs, this isn't legit,
572              * XXX and we should instead duplicate some code.
573              */
574         case CreateNotify:
575         case ConfigureNotify:
576             GETWINDOW( xconfigure );
577             {
578                 int width = event.xconfigure.width;
579                 int height = event.xconfigure.height;
580
581                 if( ( width != window->State.OldWidth ) ||
582                     ( height != window->State.OldHeight ) )
583                 {
584                     window->State.OldWidth = width;
585                     window->State.OldHeight = height;
586                     if( FETCH_WCB( *window, Reshape ) )
587                         INVOKE_WCB( *window, Reshape, ( width, height ) );
588                     else
589                     {
590                         fgSetWindow( window );
591                         glViewport( 0, 0, width, height );
592                     }
593                     glutPostRedisplay( );
594                 }
595             }
596             break;
597
598         case DestroyNotify:
599             /*
600              * This is sent to confirm the XDestroyWindow call.
601              *
602              * XXX WHY is this commented out?  Should we re-enable it?
603              */
604             /* fgAddToWindowDestroyList ( window ); */
605             break;
606
607         case Expose:
608             /*
609              * We are too dumb to process partial exposes...
610              *
611              * XXX Well, we could do it.  However, it seems to only
612              * XXX be potentially useful for single-buffered (since
613              * XXX double-buffered does not respect viewport when we
614              * XXX do a buffer-swap).
615              *
616              */
617             if( event.xexpose.count == 0 )
618             {
619                 GETWINDOW( xexpose );
620                 fgSetWindow( window );
621                 glutPostRedisplay( );
622             }
623             break;
624
625         case MapNotify:
626         case UnmapNotify:
627             /*
628              * If we never do anything with this, can we just not ask to
629              * get these messages?
630              */
631             break;
632
633         case MappingNotify:
634             /*
635              * Have the client's keyboard knowledge updated (xlib.ps,
636              * page 206, says that's a good thing to do)
637              */
638             XRefreshKeyboardMapping( (XMappingEvent *) &event );
639             break;
640
641         case VisibilityNotify:
642         {
643             GETWINDOW( xvisibility );
644             /*
645              * XXX INVOKE_WCB() does this check for us.
646              */
647             if( ! FETCH_WCB( *window, WindowStatus ) )
648                 break;
649             fgSetWindow( window );
650
651             /*
652              * Sending this event, the X server can notify us that the window
653              * has just acquired one of the three possible visibility states:
654              * VisibilityUnobscured, VisibilityPartiallyObscured or
655              * VisibilityFullyObscured
656              */
657             switch( event.xvisibility.state )
658             {
659             case VisibilityUnobscured:
660                 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_RETAINED ) );
661                 window->State.Visible = GL_TRUE;
662                 break;
663
664             case VisibilityPartiallyObscured:
665                 INVOKE_WCB( *window, WindowStatus,
666                             ( GLUT_PARTIALLY_RETAINED ) );
667                 window->State.Visible = GL_TRUE;
668                 break;
669
670             case VisibilityFullyObscured:
671                 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_COVERED ) );
672                 window->State.Visible = GL_FALSE;
673                 break;
674
675             default:
676                 fgWarning( "Unknown X visibility state: %d",
677                            event.xvisibility.state );
678                 break;
679             }
680         }
681         break;
682
683         case EnterNotify:
684         case LeaveNotify:
685             GETWINDOW( xcrossing );
686             GETMOUSE( xcrossing );
687             INVOKE_WCB( *window, Entry, ( ( EnterNotify == event.type ) ?
688                                           GLUT_ENTERED :
689                                           GLUT_LEFT ) );
690             break;
691
692         case MotionNotify:
693         {
694             GETWINDOW( xmotion );
695             GETMOUSE( xmotion );
696
697             if( window->ActiveMenu )
698             {
699                 if( window == window->ActiveMenu->ParentWindow )
700                 {
701                     window->ActiveMenu->Window->State.MouseX =
702                         event.xmotion.x_root - window->ActiveMenu->X;
703                     window->ActiveMenu->Window->State.MouseY =
704                         event.xmotion.y_root - window->ActiveMenu->Y;
705                 }
706                 window->ActiveMenu->Window->State.Redisplay = GL_TRUE ;
707                 fgSetWindow( window->ActiveMenu->ParentWindow );
708
709                 break;
710             }
711
712             /*
713              * XXX For more than 5 buttons, just check {event.xmotion.state},
714              * XXX rather than a host of bit-masks?  Or maybe we need to
715              * XXX track ButtonPress/ButtonRelease events in our own
716              * XXX bit-mask?
717              */
718 #define BUTTON_MASK \
719   ( Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask )
720             if ( event.xmotion.state & BUTTON_MASK )
721                 INVOKE_WCB( *window, Motion, ( event.xmotion.x,
722                                                event.xmotion.y ) );
723             else
724                 INVOKE_WCB( *window, Passive, ( event.xmotion.x,
725                                                 event.xmotion.y ) );
726         }
727         break;
728
729         case ButtonRelease:
730         case ButtonPress:
731         {
732             GLboolean pressed = GL_TRUE;
733             int button;
734
735             if( event.type == ButtonRelease )
736                 pressed = GL_FALSE ;
737
738             /*
739              * A mouse button has been pressed or released. Traditionally,
740              * break if the window was found within the freeglut structures.
741              */
742             GETWINDOW( xbutton );
743             GETMOUSE( xbutton );
744
745             /*
746              * An X button (at least in XFree86) is numbered from 1.
747              * A GLUT button is numbered from 0.
748              * Old GLUT passed through buttons other than just the first
749              * three, though it only gave symbolic names and official
750              * support to the first three.
751              */
752             button = event.xbutton.button - 1;
753
754             /*
755              * XXX This comment is replicated in the WIN32 section and
756              * XXX maybe also in the menu code.  Can we move the info
757              * XXX to one central place and *reference* it from here?
758              *
759              * Do not execute the application's mouse callback if a menu
760              * is hooked to this button.  In that case an appropriate
761              * private call should be generated.
762              * Near as I can tell, this is the menu behaviour:
763              *  - Down-click the menu button, menu not active:  activate
764              *    the menu with its upper left-hand corner at the mouse
765              *    location.
766              *  - Down-click any button outside the menu, menu active:
767              *    deactivate the menu
768              *  - Down-click any button inside the menu, menu active:
769              *    select the menu entry and deactivate the menu
770              *  - Up-click the menu button, menu not active:  nothing happens
771              *  - Up-click the menu button outside the menu, menu active:
772              *    nothing happens
773              *  - Up-click the menu button inside the menu, menu active:
774              *    select the menu entry and deactivate the menu
775              */
776             /* Window has an active menu, it absorbs any mouse click */
777             if( window->ActiveMenu )
778             {
779                 if( window == window->ActiveMenu->ParentWindow )
780                 {
781                     window->ActiveMenu->Window->State.MouseX =
782                         event.xbutton.x_root - window->ActiveMenu->X;
783                     window->ActiveMenu->Window->State.MouseY =
784                         event.xbutton.y_root - window->ActiveMenu->Y;
785                 }
786
787                 /* In the menu, invoke the callback and deactivate the menu*/
788                 if( fgCheckActiveMenu( window->ActiveMenu->Window,
789                                        window->ActiveMenu ) )
790                 {
791                     /*
792                      * Save the current window and menu and set the current
793                      * window to the window whose menu this is
794                      */
795                     SFG_Window *save_window = fgStructure.Window;
796                     SFG_Menu *save_menu = fgStructure.Menu;
797                     SFG_Window *parent_window =
798                         window->ActiveMenu->ParentWindow;
799                     fgSetWindow( parent_window );
800                     fgStructure.Menu = window->ActiveMenu;
801
802                     /* Execute the menu callback */
803                     fgExecuteMenuCallback( window->ActiveMenu );
804                     fgDeactivateMenu( parent_window );
805
806                     /* Restore the current window and menu */
807                     fgSetWindow( save_window );
808                     fgStructure.Menu = save_menu;
809                 }
810                 else if( pressed )
811                     /*
812                      * Outside the menu, deactivate if it's a downclick
813                      *
814                      * XXX This isn't enough.  A downclick outside of
815                      * XXX the interior of our freeglut windows should also
816                      * XXX deactivate the menu.  This is more complicated.
817                      */
818                     fgDeactivateMenu( window->ActiveMenu->ParentWindow );
819
820                 /*
821                  * XXX Why does an active menu require a redisplay at
822                  * XXX this point?  If this can come out cleanly, then
823                  * XXX it probably should do so; if not, a comment should
824                  * XXX explain it.
825                  */
826                 window->State.Redisplay = GL_TRUE;
827                 break;
828             }
829
830             /* No active menu, let's check whether we need to activate one. */
831             if( ( 0 <= button ) &&
832                 ( FREEGLUT_MAX_MENUS > button ) &&
833                 ( window->Menu[ button ] ) &&
834                 pressed )
835             {
836                 /* XXX Posting a requisite Redisplay seems bogus. */
837                 window->State.Redisplay = GL_TRUE;
838                 fgSetWindow( window );
839                 fgActivateMenu( window, button );
840                 break;
841             }
842
843             /*
844              * Check if there is a mouse or mouse wheel callback hooked to the
845              * window
846              */
847             if( ! FETCH_WCB( *window, Mouse ) &&
848                 ! FETCH_WCB( *window, MouseWheel ) )
849                 break;
850
851             fgState.Modifiers = fghGetXModifiers( &event );
852
853             /*
854              * Finally execute the mouse or mouse wheel callback
855              *
856              * XXX Use a symbolic constant, *not* "4"!  ("3, sire!")
857              */
858             if( ( button < 3 ) || ( ! FETCH_WCB( *window, MouseWheel ) ) )
859                 INVOKE_WCB( *window, Mouse, ( button,
860                                               pressed ? GLUT_DOWN : GLUT_UP,
861                                               event.xbutton.x,
862                                               event.xbutton.y )
863                 );
864             else
865             {
866                 /*
867                  * Map 4 and 5 to wheel zero; EVEN to +1, ODD to -1
868                  *  "  6 and 7 "    "   one; ...
869                  *
870                  * XXX This *should* be behind some variables/macros,
871                  * XXX since the order and numbering isn't certain
872                  * XXX See XFree86 configuration docs (even back in the
873                  * XXX 3.x days, and especially with 4.x).
874                  *
875                  * XXX Note that {button} has already been decremeted
876                  * XXX in mapping from X button numbering to GLUT.
877                  */
878                 int wheel_number = (button - 3) / 2;
879                 int direction = -1;
880                 if( button % 2 )
881                     direction = 1;
882
883                 if( pressed )
884                     INVOKE_WCB( *window, MouseWheel, ( wheel_number,
885                                                        direction,
886                                                        event.xbutton.x,
887                                                        event.xbutton.y )
888                     );
889             }
890
891             /* Trash the modifiers state */
892             fgState.Modifiers = 0xffffffff;
893         }
894         break;
895
896         case KeyRelease:
897         case KeyPress:
898         {
899             FGCBKeyboard keyboard_cb;
900             FGCBSpecial special_cb;
901
902             GETWINDOW( xkey );
903             GETMOUSE( xkey );
904
905             /* Detect auto repeated keys, if configured globally or per-window */
906
907             if ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE )
908             {
909                 if (event.type==KeyRelease)
910                 {
911                     /*
912                      * Look at X11 keystate to detect repeat mode.
913                      * While X11 says the key is actually held down, we'll ignore KeyRelease/KeyPress pairs.
914                      */
915
916                     char keys[32];
917                     XQueryKeymap( fgDisplay.Display, keys ); /* Look at X11 keystate to detect repeat mode */
918
919                     if ( event.xkey.keycode<256 )            /* XQueryKeymap is limited to 256 keycodes    */
920                     {
921                         if ( keys[event.xkey.keycode>>3] & (1<<(event.xkey.keycode%8)) )
922                             window->State.KeyRepeating = GL_TRUE;
923                         else
924                             window->State.KeyRepeating = GL_FALSE;
925                     }
926                 }
927             }
928             else
929                 window->State.KeyRepeating = GL_FALSE;
930
931             /* Cease processing this event if it is auto repeated */
932
933             if (window->State.KeyRepeating)
934                 break;
935
936             if( event.type == KeyPress )
937             {
938                 keyboard_cb = FETCH_WCB( *window, Keyboard );
939                 special_cb  = FETCH_WCB( *window, Special  );
940             }
941             else
942             {
943                 keyboard_cb = FETCH_WCB( *window, KeyboardUp );
944                 special_cb  = FETCH_WCB( *window, SpecialUp  );
945             }
946
947             /*
948              * Is there a keyboard/special callback hooked for this window?
949              */
950             if( keyboard_cb || special_cb )
951             {
952                 XComposeStatus composeStatus;
953                 char asciiCode[ 32 ];
954                 KeySym keySym;
955                 int len;
956
957                 /*
958                  * Check for the ASCII/KeySym codes associated with the event:
959                  */
960                 len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode),
961                                      &keySym, &composeStatus
962                 );
963
964                 /*
965                  * GLUT API tells us to have two separate callbacks...
966                  */
967                 if( len > 0 )
968                 {
969                     /*
970                      * ...one for the ASCII translateable keypresses...
971                      */
972                     if( keyboard_cb )
973                     {
974                         fgSetWindow( window );
975                         fgState.Modifiers = fghGetXModifiers( &event );
976                         keyboard_cb( asciiCode[ 0 ],
977                                      event.xkey.x, event.xkey.y
978                         );
979                         fgState.Modifiers = 0xffffffff;
980                     }
981                 }
982                 else
983                 {
984                     int special = -1;
985
986                     /*
987                      * ...and one for all the others, which need to be
988                      * translated to GLUT_KEY_Xs...
989                      */
990                     switch( keySym )
991                     {
992                     case XK_F1:     special = GLUT_KEY_F1;     break;
993                     case XK_F2:     special = GLUT_KEY_F2;     break;
994                     case XK_F3:     special = GLUT_KEY_F3;     break;
995                     case XK_F4:     special = GLUT_KEY_F4;     break;
996                     case XK_F5:     special = GLUT_KEY_F5;     break;
997                     case XK_F6:     special = GLUT_KEY_F6;     break;
998                     case XK_F7:     special = GLUT_KEY_F7;     break;
999                     case XK_F8:     special = GLUT_KEY_F8;     break;
1000                     case XK_F9:     special = GLUT_KEY_F9;     break;
1001                     case XK_F10:    special = GLUT_KEY_F10;    break;
1002                     case XK_F11:    special = GLUT_KEY_F11;    break;
1003                     case XK_F12:    special = GLUT_KEY_F12;    break;
1004
1005                     case XK_Left:   special = GLUT_KEY_LEFT;   break;
1006                     case XK_Right:  special = GLUT_KEY_RIGHT;  break;
1007                     case XK_Up:     special = GLUT_KEY_UP;     break;
1008                     case XK_Down:   special = GLUT_KEY_DOWN;   break;
1009
1010                     case XK_KP_Prior:
1011                     case XK_Prior:  special = GLUT_KEY_PAGE_UP; break;
1012                     case XK_KP_Next:
1013                     case XK_Next:   special = GLUT_KEY_PAGE_DOWN; break;
1014                     case XK_KP_Home:
1015                     case XK_Home:   special = GLUT_KEY_HOME;   break;
1016                     case XK_KP_End:
1017                     case XK_End:    special = GLUT_KEY_END;    break;
1018                     case XK_KP_Insert:
1019                     case XK_Insert: special = GLUT_KEY_INSERT; break;
1020                     }
1021
1022                     /*
1023                      * Execute the callback (if one has been specified),
1024                      * given that the special code seems to be valid...
1025                      */
1026                     if( special_cb && (special != -1) )
1027                     {
1028                         fgSetWindow( window );
1029                         fgState.Modifiers = fghGetXModifiers( &event );
1030                         special_cb( special, event.xkey.x, event.xkey.y );
1031                         fgState.Modifiers = 0xffffffff;
1032                     }
1033                 }
1034             }
1035         }
1036         break;
1037
1038         case ReparentNotify:
1039             break; /* XXX Should disable this event */
1040
1041         default:
1042             fgWarning ("Unknown X event type: %d", event.type);
1043             break;
1044         }
1045     }
1046
1047 #elif TARGET_HOST_WIN32 || TARGET_HOST_WINCE
1048
1049     MSG stMsg;
1050
1051     while( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
1052     {
1053         if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
1054         {
1055             if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
1056             {
1057                 fgDeinitialize( );
1058                 exit( 0 );
1059             }
1060             else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
1061                 fgState.ExecState = GLUT_EXEC_STATE_STOP;
1062
1063             return;
1064         }
1065
1066         TranslateMessage( &stMsg );
1067         DispatchMessage( &stMsg );
1068     }
1069 #endif
1070
1071     if( fgState.Timers.First )
1072         fghCheckTimers( );
1073     fghCheckJoystickPolls( );
1074     fghDisplayAll( );
1075
1076     fgCloseWindows( );
1077 }
1078
1079 /*
1080  * Enters the freeglut processing loop.
1081  * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
1082  */
1083 void FGAPIENTRY glutMainLoop( void )
1084 {
1085     int action;
1086
1087 #if TARGET_HOST_WIN32 || TARGET_HOST_WINCE
1088     SFG_Window *window = (SFG_Window *)fgStructure.Windows.First ;
1089 #endif
1090
1091     freeglut_assert_ready;
1092
1093 #if TARGET_HOST_WIN32 || TARGET_HOST_WINCE
1094     /*
1095      * Processing before the main loop:  If there is a window which is open and
1096      * which has a visibility callback, call it.  I know this is an ugly hack,
1097      * but I'm not sure what else to do about it.  Ideally we should leave
1098      * something uninitialized in the create window code and initialize it in
1099      * the main loop, and have that initialization create a "WM_ACTIVATE"
1100      * message.  Then we would put the visibility callback code in the
1101      * "case WM_ACTIVATE" block below.         - John Fay -- 10/24/02
1102      */
1103     while( window )
1104     {
1105         if ( FETCH_WCB( *window, Visibility ) )
1106         {
1107             SFG_Window *current_window = fgStructure.Window ;
1108
1109             INVOKE_WCB( *window, Visibility, ( window->State.Visible ) );
1110             fgSetWindow( current_window );
1111         }
1112
1113         window = (SFG_Window *)window->Node.Next ;
1114     }
1115 #endif
1116
1117     fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
1118     while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
1119     {
1120         SFG_Window *window;
1121
1122         glutMainLoopEvent( );
1123         /*
1124          * Step through the list of windows, seeing if there are any
1125          * that are not menus
1126          */
1127         for( window = ( SFG_Window * )fgStructure.Windows.First;
1128              window;
1129              window = ( SFG_Window * )window->Node.Next )
1130             if ( ! ( window->IsMenu ) )
1131                 break;
1132
1133         if( ! window )
1134             fgState.ExecState = GLUT_EXEC_STATE_STOP;
1135         else
1136         {
1137             if( fgState.IdleCallback )
1138                 fgState.IdleCallback( );
1139
1140             fghSleepForEvents( );
1141         }
1142     }
1143
1144     /*
1145      * When this loop terminates, destroy the display, state and structure
1146      * of a freeglut session, so that another glutInit() call can happen
1147      *
1148      * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it.
1149      */
1150     action = fgState.ActionOnWindowClose;
1151     fgDeinitialize( );
1152     if( action == GLUT_ACTION_EXIT )
1153         exit( 0 );
1154 }
1155
1156 /*
1157  * Leaves the freeglut processing loop.
1158  */
1159 void FGAPIENTRY glutLeaveMainLoop( void )
1160 {
1161     fgState.ExecState = GLUT_EXEC_STATE_STOP ;
1162 }
1163
1164
1165 #if TARGET_HOST_WIN32 || TARGET_HOST_WINCE
1166 /*
1167  * Determine a GLUT modifer mask based on MS-WINDOWS system info.
1168  */
1169 static int fghGetWin32Modifiers (void)
1170 {
1171     return
1172         ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
1173             ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1174         ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
1175             ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1176         ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
1177             ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1178 }
1179
1180 /*
1181  * The window procedure for handling Win32 events
1182  */
1183 LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
1184                                LPARAM lParam )
1185 {
1186     SFG_Window* window = fgWindowByHandle( hWnd );
1187     PAINTSTRUCT ps;
1188     LONG lRet = 1;
1189
1190     if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
1191       return DefWindowProc( hWnd, uMsg, wParam, lParam );
1192
1193     /* printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0,
1194              uMsg, wParam, lParam ); */
1195     switch( uMsg )
1196     {
1197     case WM_CREATE:
1198         /* The window structure is passed as the creation structure paramter... */
1199         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
1200         assert( window != NULL );
1201
1202         window->Window.Handle = hWnd;
1203         window->Window.Device = GetDC( hWnd );
1204         if( window->IsMenu )
1205         {
1206             unsigned int current_DisplayMode = fgState.DisplayMode;
1207             fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH;
1208 #if !TARGET_HOST_WINCE
1209             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
1210 #endif
1211             fgState.DisplayMode = current_DisplayMode;
1212
1213             if( fgStructure.MenuContext )
1214                 wglMakeCurrent( window->Window.Device,
1215                                 fgStructure.MenuContext->Context
1216                 );
1217             else
1218             {
1219                 fgStructure.MenuContext =
1220                     (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
1221                 fgStructure.MenuContext->Context =
1222                     wglCreateContext( window->Window.Device );
1223             }
1224
1225             /* window->Window.Context = wglGetCurrentContext ();   */
1226             window->Window.Context = wglCreateContext( window->Window.Device );
1227         }
1228         else
1229         {
1230 #if !TARGET_HOST_WINCE
1231             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
1232 #endif
1233
1234             if( ! fgState.UseCurrentContext )
1235                 window->Window.Context =
1236                     wglCreateContext( window->Window.Device );
1237             else
1238             {
1239                 window->Window.Context = wglGetCurrentContext( );
1240                 if( ! window->Window.Context )
1241                     window->Window.Context =
1242                         wglCreateContext( window->Window.Device );
1243             }
1244         }
1245
1246         window->State.NeedToResize = GL_TRUE;
1247         window->State.Width  = fgState.Size.X;
1248         window->State.Height = fgState.Size.Y;
1249
1250         ReleaseDC( window->Window.Handle, window->Window.Device );
1251
1252 #if TARGET_HOST_WINCE
1253         /* Take over button handling */
1254         {
1255             HINSTANCE dxDllLib=LoadLibrary(_T("gx.dll"));
1256             if (dxDllLib)
1257             {
1258                 GXGetDefaultKeys_=(GXGETDEFAULTKEYS)GetProcAddress(dxDllLib, _T("?GXGetDefaultKeys@@YA?AUGXKeyList@@H@Z"));
1259                 GXOpenInput_=(GXOPENINPUT)GetProcAddress(dxDllLib, _T("?GXOpenInput@@YAHXZ"));
1260             }
1261
1262             if(GXOpenInput_)
1263                 (*GXOpenInput_)();
1264             if(GXGetDefaultKeys_)
1265                 gxKeyList = (*GXGetDefaultKeys_)(GX_LANDSCAPEKEYS);
1266         }
1267
1268 #endif /* TARGET_HOST_WINCE */
1269         break;
1270
1271     case WM_SIZE:
1272         /*
1273          * If the window is visible, then it is the user manually resizing it.
1274          * If it is not, then it is the system sending us a dummy resize with
1275          * zero dimensions on a "glutIconifyWindow" call.
1276          */
1277         if( window->State.Visible )
1278         {
1279             window->State.NeedToResize = GL_TRUE;
1280 #if TARGET_HOST_WINCE
1281             window->State.Width  = HIWORD(lParam);
1282             window->State.Height = LOWORD(lParam);
1283 #else
1284             window->State.Width  = LOWORD(lParam);
1285             window->State.Height = HIWORD(lParam);
1286 #endif /* TARGET_HOST_WINCE */
1287         }
1288
1289         break;
1290 #if 0
1291     case WM_SETFOCUS:
1292 /*        printf("WM_SETFOCUS: %p\n", window ); */
1293         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1294         break;
1295
1296     case WM_ACTIVATE:
1297         if (LOWORD(wParam) != WA_INACTIVE)
1298         {
1299             /* glutSetCursor( fgStructure.Window->State.Cursor ); */
1300 /*            printf("WM_ACTIVATE: glutSetCursor( %p, %d)\n", window,
1301                    window->State.Cursor ); */
1302             glutSetCursor( window->State.Cursor );
1303         }
1304
1305         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1306         break;
1307 #endif
1308
1309         /*
1310          * XXX Why not re-use some common code with the glutSetCursor()
1311          * XXX function (or perhaps invoke glutSetCursor())?
1312          * XXX That is, why are we duplicating code, here, from
1313          * XXX glutSetCursor()?  The WIN32 code should be able to just
1314          * XXX call glutSetCurdsor() instead of defining two macros
1315          * XXX and implementing a nested case in-line.
1316          */
1317     case WM_SETCURSOR:
1318         /* Set the cursor AND change it for this window class. */
1319 #define MAP_CURSOR(a,b)                 \
1320     case a:                             \
1321     SetCursor( LoadCursor( NULL, b ) ); \
1322     break;
1323
1324         /* Nuke the cursor AND change it for this window class. */
1325 #define ZAP_CURSOR(a,b) \
1326     case a:             \
1327     SetCursor( NULL );  \
1328     break;
1329
1330         if( LOWORD( lParam ) == HTCLIENT )
1331             switch( window->State.Cursor )
1332             {
1333                 MAP_CURSOR( GLUT_CURSOR_RIGHT_ARROW, IDC_ARROW     );
1334                 MAP_CURSOR( GLUT_CURSOR_LEFT_ARROW,  IDC_ARROW     );
1335                 MAP_CURSOR( GLUT_CURSOR_INFO,        IDC_HELP      );
1336                 MAP_CURSOR( GLUT_CURSOR_DESTROY,     IDC_CROSS     );
1337                 MAP_CURSOR( GLUT_CURSOR_HELP,        IDC_HELP      );
1338                 MAP_CURSOR( GLUT_CURSOR_CYCLE,       IDC_SIZEALL   );
1339                 MAP_CURSOR( GLUT_CURSOR_SPRAY,       IDC_CROSS     );
1340                 MAP_CURSOR( GLUT_CURSOR_WAIT,        IDC_WAIT      );
1341                 MAP_CURSOR( GLUT_CURSOR_TEXT,        IDC_UPARROW   );
1342                 MAP_CURSOR( GLUT_CURSOR_CROSSHAIR,   IDC_CROSS     );
1343                 /* MAP_CURSOR( GLUT_CURSOR_NONE,        IDC_NO         ); */
1344                 ZAP_CURSOR( GLUT_CURSOR_NONE,        NULL          );
1345
1346             default:
1347                 MAP_CURSOR( GLUT_CURSOR_UP_DOWN,     IDC_ARROW     );
1348             }
1349         else
1350             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1351         break;
1352
1353     case WM_SHOWWINDOW:
1354         window->State.Visible = GL_TRUE;
1355         window->State.Redisplay = GL_TRUE;
1356         break;
1357
1358     case WM_PAINT:
1359         /* Turn on the visibility in case it was turned off somehow */
1360         window->State.Visible = GL_TRUE;
1361         BeginPaint( hWnd, &ps );
1362         fghRedrawWindow( window );
1363         EndPaint( hWnd, &ps );
1364         break;
1365
1366     case WM_CLOSE:
1367         fgDestroyWindow ( window );
1368         if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
1369             PostQuitMessage(0);
1370         break;
1371
1372     case WM_DESTROY:
1373         /*
1374          * The window already got destroyed, so don't bother with it.
1375          */
1376         return 0;
1377
1378     case WM_MOUSEMOVE:
1379     {
1380 #if TARGET_HOST_WINCE
1381         window->State.MouseX = 320-HIWORD( lParam );
1382         window->State.MouseY = LOWORD( lParam );
1383 #else
1384         window->State.MouseX = LOWORD( lParam );
1385         window->State.MouseY = HIWORD( lParam );
1386 #endif /* TARGET_HOST_WINCE */
1387         /* Restrict to [-32768, 32767] to match X11 behaviour       */
1388         /* See comment in "freeglut_developer" mailing list 10/4/04 */
1389         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
1390         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
1391
1392         if ( window->ActiveMenu )
1393         {
1394             window->State.Redisplay = GL_TRUE;
1395             fgSetWindow ( window->ActiveMenu->ParentWindow );
1396             break;
1397         }
1398
1399         fgState.Modifiers = fghGetWin32Modifiers( );
1400
1401         if( ( wParam & MK_LBUTTON ) ||
1402             ( wParam & MK_MBUTTON ) ||
1403             ( wParam & MK_RBUTTON ) )
1404             INVOKE_WCB( *window, Motion, ( window->State.MouseX,
1405                                            window->State.MouseY ) );
1406         else
1407             INVOKE_WCB( *window, Passive, ( window->State.MouseX,
1408                                             window->State.MouseY ) );
1409
1410         fgState.Modifiers = 0xffffffff;
1411     }
1412     break;
1413
1414     case WM_LBUTTONDOWN:
1415     case WM_MBUTTONDOWN:
1416     case WM_RBUTTONDOWN:
1417     case WM_LBUTTONUP:
1418     case WM_MBUTTONUP:
1419     case WM_RBUTTONUP:
1420     {
1421         GLboolean pressed = GL_TRUE;
1422         int button;
1423
1424 #if TARGET_HOST_WINCE
1425         window->State.MouseX = 320-HIWORD( lParam );
1426         window->State.MouseY = LOWORD( lParam );
1427 #else
1428         window->State.MouseX = LOWORD( lParam );
1429         window->State.MouseY = HIWORD( lParam );
1430 #endif /* TARGET_HOST_WINCE */
1431
1432         /* Restrict to [-32768, 32767] to match X11 behaviour       */
1433         /* See comment in "freeglut_developer" mailing list 10/4/04 */
1434         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
1435         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
1436
1437         switch( uMsg )
1438         {
1439         case WM_LBUTTONDOWN:
1440             pressed = GL_TRUE;
1441             button = GLUT_LEFT_BUTTON;
1442             break;
1443         case WM_MBUTTONDOWN:
1444             pressed = GL_TRUE;
1445             button = GLUT_MIDDLE_BUTTON;
1446             break;
1447         case WM_RBUTTONDOWN:
1448             pressed = GL_TRUE;
1449             button = GLUT_RIGHT_BUTTON;
1450             break;
1451         case WM_LBUTTONUP:
1452             pressed = GL_FALSE;
1453             button = GLUT_LEFT_BUTTON;
1454             break;
1455         case WM_MBUTTONUP:
1456             pressed = GL_FALSE;
1457             button = GLUT_MIDDLE_BUTTON;
1458             break;
1459         case WM_RBUTTONUP:
1460             pressed = GL_FALSE;
1461             button = GLUT_RIGHT_BUTTON;
1462             break;
1463         default:
1464             pressed = GL_FALSE;
1465             button = -1;
1466             break;
1467         }
1468
1469 #if !TARGET_HOST_WINCE
1470         if( GetSystemMetrics( SM_SWAPBUTTON ) )
1471         {
1472             if( button == GLUT_LEFT_BUTTON )
1473                 button = GLUT_RIGHT_BUTTON;
1474             else
1475                 if( button == GLUT_RIGHT_BUTTON )
1476                     button = GLUT_LEFT_BUTTON;
1477         }
1478 #endif /* !TARGET_HOST_WINCE */
1479
1480         if( button == -1 )
1481             return DefWindowProc( hWnd, uMsg, lParam, wParam );
1482
1483         /*
1484          * XXX This comment is duplicated in two other spots.
1485          * XXX Can we centralize it?
1486          *
1487          * Do not execute the application's mouse callback if a
1488          * menu is hooked to this button.
1489          * In that case an appropriate private call should be generated.
1490          * Near as I can tell, this is the menu behaviour:
1491          *  - Down-click the menu button, menu not active:  activate
1492          *    the menu with its upper left-hand corner at the mouse location.
1493          *  - Down-click any button outside the menu, menu active:
1494          *    deactivate the menu
1495          *  - Down-click any button inside the menu, menu active:
1496          *    select the menu entry and deactivate the menu
1497          *  - Up-click the menu button, menu not active:  nothing happens
1498          *  - Up-click the menu button outside the menu, menu active:
1499          *    nothing happens
1500          *  - Up-click the menu button inside the menu, menu active:
1501          *    select the menu entry and deactivate the menu
1502          */
1503         /* Window has an active menu, it absorbs any mouse click */
1504         if( window->ActiveMenu )
1505         {
1506             /* Outside the menu, deactivate the menu if it's a downclick */
1507             if( ! fgCheckActiveMenu( window, window->ActiveMenu ) )
1508             {
1509                 if( pressed )
1510                     fgDeactivateMenu( window->ActiveMenu->ParentWindow );
1511             }
1512             else  /* In menu, invoke the callback and deactivate the menu*/
1513             {
1514                 /*
1515                  * Save the current window and menu and set the current
1516                  * window to the window whose menu this is
1517                  */
1518                 SFG_Window *save_window = fgStructure.Window;
1519                 SFG_Menu *save_menu = fgStructure.Menu;
1520                 SFG_Window *parent_window = window->ActiveMenu->ParentWindow;
1521                 fgSetWindow( parent_window );
1522                 fgStructure.Menu = window->ActiveMenu;
1523
1524                 /* Execute the menu callback */
1525                 fgExecuteMenuCallback( window->ActiveMenu );
1526                 fgDeactivateMenu( parent_window );
1527
1528                 /* Restore the current window and menu */
1529                 fgSetWindow( save_window );
1530                 fgStructure.Menu = save_menu;
1531             }
1532
1533             /*
1534              * Let's make the window redraw as a result of the mouse
1535              * click and menu activity.
1536              */
1537             if( ! window->IsMenu )
1538                 window->State.Redisplay = GL_TRUE;
1539
1540             break;
1541         }
1542
1543         if( window->Menu[ button ] && pressed )
1544         {
1545             window->State.Redisplay = GL_TRUE;
1546             fgSetWindow( window );
1547             fgActivateMenu( window, button );
1548
1549             break;
1550         }
1551
1552         /* Set capture so that the window captures all the mouse messages */
1553         /*
1554          * XXX - Multiple button support:  Under X11, the mouse is not released
1555          * XXX - from the window until all buttons have been released, even if the
1556          * XXX - user presses a button in another window.  This will take more
1557          * XXX - code changes than I am up to at the moment (10/5/04).  The present
1558          * XXX - is a 90 percent solution.
1559          */
1560         if ( pressed == GL_TRUE )
1561           SetCapture ( window->Window.Handle ) ;
1562         else
1563           ReleaseCapture () ;
1564
1565         if( ! FETCH_WCB( *window, Mouse ) )
1566             break;
1567
1568         fgSetWindow( window );
1569         fgState.Modifiers = fghGetWin32Modifiers( );
1570
1571         INVOKE_WCB(
1572             *window, Mouse,
1573             ( button,
1574               pressed ? GLUT_DOWN : GLUT_UP,
1575               window->State.MouseX,
1576               window->State.MouseY
1577             )
1578         );
1579
1580         fgState.Modifiers = 0xffffffff;
1581     }
1582     break;
1583
1584     case 0x020a:
1585         /* Should be WM_MOUSEWHEEL but my compiler doesn't recognize it */
1586     {
1587         /*
1588          * XXX THIS IS SPECULATIVE -- John Fay, 10/2/03
1589          * XXX Should use WHEEL_DELTA instead of 120
1590          */
1591         int wheel_number = LOWORD( wParam );
1592         short ticks = ( short )HIWORD( wParam ) / 120;
1593         int direction = 1;
1594
1595         if( ticks < 0 )
1596         {
1597             direction = -1;
1598             ticks = -ticks;
1599         }
1600
1601         /*
1602          * The mouse cursor has moved. Remember the new mouse cursor's position
1603          */
1604         /*        window->State.MouseX = LOWORD( lParam ); */
1605         /* Need to adjust by window position, */
1606         /*        window->State.MouseY = HIWORD( lParam ); */
1607         /* change "lParam" to other parameter */
1608
1609         if( ! FETCH_WCB( *window, MouseWheel ) &&
1610             ! FETCH_WCB( *window, Mouse ) )
1611             break;
1612
1613         fgSetWindow( window );
1614         fgState.Modifiers = fghGetWin32Modifiers( );
1615
1616         while( ticks-- )
1617             if( FETCH_WCB( *window, MouseWheel ) )
1618                 INVOKE_WCB( *window, MouseWheel,
1619                             ( wheel_number,
1620                               direction,
1621                               window->State.MouseX,
1622                               window->State.MouseY
1623                             )
1624                 );
1625             else  /* No mouse wheel, call the mouse button callback twice */
1626             {
1627                 /*
1628                  * XXX The below assumes that you have no more than 3 mouse
1629                  * XXX buttons.  Sorry.
1630                  */
1631                 int button = wheel_number*2 + 4;
1632                 if( direction > 0 )
1633                     ++button;
1634                 INVOKE_WCB( *window, Mouse,
1635                             ( button, GLUT_DOWN,
1636                               window->State.MouseX, window->State.MouseY )
1637                 );
1638                 INVOKE_WCB( *window, Mouse,
1639                             ( button, GLUT_UP,
1640                               window->State.MouseX, window->State.MouseX )
1641                 );
1642             }
1643
1644         fgState.Modifiers = 0xffffffff;
1645     }
1646     break ;
1647
1648     case WM_SYSKEYDOWN:
1649     case WM_KEYDOWN:
1650     {
1651         int keypress = -1;
1652         POINT mouse_pos ;
1653
1654         if( ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) && (HIWORD(lParam) & KF_REPEAT) )
1655             break;
1656
1657         /*
1658          * Remember the current modifiers state. This is done here in order
1659          * to make sure the VK_DELETE keyboard callback is executed properly.
1660          */
1661         fgState.Modifiers = fghGetWin32Modifiers( );
1662
1663         GetCursorPos( &mouse_pos );
1664         ScreenToClient( window->Window.Handle, &mouse_pos );
1665
1666         window->State.MouseX = mouse_pos.x;
1667         window->State.MouseY = mouse_pos.y;
1668
1669         /*
1670          * Convert the Win32 keystroke codes to GLUTtish way
1671          */
1672 #       define KEY(a,b) case a: keypress = b; break;
1673
1674         switch( wParam )
1675         {
1676             KEY( VK_F1,     GLUT_KEY_F1        );
1677             KEY( VK_F2,     GLUT_KEY_F2        );
1678             KEY( VK_F3,     GLUT_KEY_F3        );
1679             KEY( VK_F4,     GLUT_KEY_F4        );
1680             KEY( VK_F5,     GLUT_KEY_F5        );
1681             KEY( VK_F6,     GLUT_KEY_F6        );
1682             KEY( VK_F7,     GLUT_KEY_F7        );
1683             KEY( VK_F8,     GLUT_KEY_F8        );
1684             KEY( VK_F9,     GLUT_KEY_F9        );
1685             KEY( VK_F10,    GLUT_KEY_F10       );
1686             KEY( VK_F11,    GLUT_KEY_F11       );
1687             KEY( VK_F12,    GLUT_KEY_F12       );
1688             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
1689             KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
1690             KEY( VK_HOME,   GLUT_KEY_HOME      );
1691             KEY( VK_END,    GLUT_KEY_END       );
1692             KEY( VK_LEFT,   GLUT_KEY_LEFT      );
1693             KEY( VK_UP,     GLUT_KEY_UP        );
1694             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
1695             KEY( VK_DOWN,   GLUT_KEY_DOWN      );
1696             KEY( VK_INSERT, GLUT_KEY_INSERT    );
1697
1698         case VK_DELETE:
1699             /*
1700              * The delete key should be treated as an ASCII keypress:
1701              */
1702             INVOKE_WCB( *window, Keyboard,
1703                         ( 127, window->State.MouseX, window->State.MouseY )
1704             );
1705         }
1706
1707 #if TARGET_HOST_WINCE
1708         if(!(lParam & 0x40000000)) /* Prevent auto-repeat */
1709         {
1710             if(wParam==(unsigned)gxKeyList.vkRight)
1711                 keypress = GLUT_KEY_RIGHT;
1712             else if(wParam==(unsigned)gxKeyList.vkLeft)
1713                 keypress = GLUT_KEY_LEFT;
1714             else if(wParam==(unsigned)gxKeyList.vkUp)
1715                 keypress = GLUT_KEY_UP;
1716             else if(wParam==(unsigned)gxKeyList.vkDown)
1717                 keypress = GLUT_KEY_DOWN;
1718             else if(wParam==(unsigned)gxKeyList.vkA)
1719                 keypress = GLUT_KEY_F1;
1720             else if(wParam==(unsigned)gxKeyList.vkB)
1721                 keypress = GLUT_KEY_F2;
1722             else if(wParam==(unsigned)gxKeyList.vkC)
1723                 keypress = GLUT_KEY_F3;
1724             else if(wParam==(unsigned)gxKeyList.vkStart)
1725                 keypress = GLUT_KEY_F4;
1726         }
1727 #endif
1728
1729         if( keypress != -1 )
1730             INVOKE_WCB( *window, Special,
1731                         ( keypress,
1732                           window->State.MouseX, window->State.MouseY )
1733             );
1734
1735         fgState.Modifiers = 0xffffffff;
1736     }
1737     break;
1738
1739     case WM_SYSKEYUP:
1740     case WM_KEYUP:
1741     {
1742         int keypress = -1;
1743         POINT mouse_pos;
1744
1745         /*
1746          * Remember the current modifiers state. This is done here in order
1747          * to make sure the VK_DELETE keyboard callback is executed properly.
1748          */
1749         fgState.Modifiers = fghGetWin32Modifiers( );
1750
1751         GetCursorPos( &mouse_pos );
1752         ScreenToClient( window->Window.Handle, &mouse_pos );
1753
1754         window->State.MouseX = mouse_pos.x;
1755         window->State.MouseY = mouse_pos.y;
1756
1757         /*
1758          * Convert the Win32 keystroke codes to GLUTtish way.
1759          * "KEY(a,b)" was defined under "WM_KEYDOWN"
1760          */
1761
1762         switch( wParam )
1763         {
1764             KEY( VK_F1,     GLUT_KEY_F1        );
1765             KEY( VK_F2,     GLUT_KEY_F2        );
1766             KEY( VK_F3,     GLUT_KEY_F3        );
1767             KEY( VK_F4,     GLUT_KEY_F4        );
1768             KEY( VK_F5,     GLUT_KEY_F5        );
1769             KEY( VK_F6,     GLUT_KEY_F6        );
1770             KEY( VK_F7,     GLUT_KEY_F7        );
1771             KEY( VK_F8,     GLUT_KEY_F8        );
1772             KEY( VK_F9,     GLUT_KEY_F9        );
1773             KEY( VK_F10,    GLUT_KEY_F10       );
1774             KEY( VK_F11,    GLUT_KEY_F11       );
1775             KEY( VK_F12,    GLUT_KEY_F12       );
1776             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
1777             KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
1778             KEY( VK_HOME,   GLUT_KEY_HOME      );
1779             KEY( VK_END,    GLUT_KEY_END       );
1780             KEY( VK_LEFT,   GLUT_KEY_LEFT      );
1781             KEY( VK_UP,     GLUT_KEY_UP        );
1782             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
1783             KEY( VK_DOWN,   GLUT_KEY_DOWN      );
1784             KEY( VK_INSERT, GLUT_KEY_INSERT    );
1785
1786           case VK_DELETE:
1787               /*
1788                * The delete key should be treated as an ASCII keypress:
1789                */
1790               INVOKE_WCB( *window, KeyboardUp,
1791                           ( 127, window->State.MouseX, window->State.MouseY )
1792               );
1793               break;
1794
1795         default:
1796         {
1797 #if !TARGET_HOST_WINCE
1798             BYTE state[ 256 ];
1799             WORD code[ 2 ];
1800
1801             GetKeyboardState( state );
1802
1803             if( ToAscii( wParam, 0, state, code, 0 ) == 1 )
1804                 wParam=code[ 0 ];
1805
1806             INVOKE_WCB( *window, KeyboardUp,
1807                         ( (char)wParam,
1808                           window->State.MouseX, window->State.MouseY )
1809             );
1810 #endif /* !TARGET_HOST_WINCE */
1811         }
1812         }
1813
1814         if( keypress != -1 )
1815             INVOKE_WCB( *window, SpecialUp,
1816                         ( keypress,
1817                           window->State.MouseX, window->State.MouseY )
1818             );
1819
1820         fgState.Modifiers = 0xffffffff;
1821     }
1822     break;
1823
1824     case WM_SYSCHAR:
1825     case WM_CHAR:
1826     {
1827       if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
1828             break;
1829
1830         fgState.Modifiers = fghGetWin32Modifiers( );
1831         INVOKE_WCB( *window, Keyboard,
1832                     ( (char)wParam,
1833                       window->State.MouseX, window->State.MouseY )
1834         );
1835         fgState.Modifiers = 0xffffffff;
1836     }
1837     break;
1838
1839     case WM_CAPTURECHANGED:
1840         /* User has finished resizing the window, force a redraw */
1841         INVOKE_WCB( *window, Display, ( ) );
1842
1843         /*lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); */
1844         break;
1845
1846         /*
1847          * Other messages that I have seen and which are not handled already
1848          */
1849     case WM_SETTEXT:  /* 0x000c */
1850         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1851         /* Pass it on to "DefWindowProc" to set the window text */
1852         break;
1853
1854     case WM_GETTEXT:  /* 0x000d */
1855         /* Ideally we would copy the title of the window into "lParam" */
1856         /* strncpy ( (char *)lParam, "Window Title", wParam );
1857            lRet = ( wParam > 12 ) ? 12 : wParam;  */
1858         /* the number of characters copied */
1859         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1860         break;
1861
1862     case WM_GETTEXTLENGTH:  /* 0x000e */
1863         /* Ideally we would get the length of the title of the window */
1864         lRet = 12;
1865         /* the number of characters in "Window Title\0" (see above) */
1866         break;
1867
1868     case WM_ERASEBKGND:  /* 0x0014 */
1869         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1870         break;
1871
1872 #if !TARGET_HOST_WINCE
1873     case WM_SYNCPAINT:  /* 0x0088 */
1874         /* Another window has moved, need to update this one */
1875         window->State.Redisplay = GL_TRUE;
1876         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1877         /* Help screen says this message must be passed to "DefWindowProc" */
1878         break;
1879
1880     case WM_NCPAINT:  /* 0x0085 */
1881       /* Need to update the border of this window */
1882         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1883         /* Pass it on to "DefWindowProc" to repaint a standard border */
1884         break;
1885
1886     case WM_SYSCOMMAND :  /* 0x0112 */
1887         {
1888           /*
1889            * We have received a system command message.  Try to act on it.
1890            * The commands are passed in through the "wParam" parameter:
1891            * The least significant digit seems to be which edge of the window
1892            * is being used for a resize event:
1893            *     4  3  5
1894            *     1     2
1895            *     7  6  8
1896            * Congratulations and thanks to Richard Rauch for figuring this out..
1897            */
1898             switch ( wParam & 0xfff0 )
1899             {
1900             case SC_SIZE       :
1901                 break ;
1902
1903             case SC_MOVE       :
1904                 break ;
1905
1906             case SC_MINIMIZE   :
1907                 /* User has clicked on the "-" to minimize the window */
1908                 /* Turn off the visibility */
1909                 window->State.Visible = GL_FALSE ;
1910
1911                 break ;
1912
1913             case SC_MAXIMIZE   :
1914                 break ;
1915
1916             case SC_NEXTWINDOW :
1917                 break ;
1918
1919             case SC_PREVWINDOW :
1920                 break ;
1921
1922             case SC_CLOSE      :
1923                 /* Followed very closely by a WM_CLOSE message */
1924                 break ;
1925
1926             case SC_VSCROLL    :
1927                 break ;
1928
1929             case SC_HSCROLL    :
1930                 break ;
1931
1932             case SC_MOUSEMENU  :
1933                 break ;
1934
1935             case SC_KEYMENU    :
1936                 break ;
1937
1938             case SC_ARRANGE    :
1939                 break ;
1940
1941             case SC_RESTORE    :
1942                 break ;
1943
1944             case SC_TASKLIST   :
1945                 break ;
1946
1947             case SC_SCREENSAVE :
1948                 break ;
1949
1950             case SC_HOTKEY     :
1951                 break ;
1952
1953             default:
1954 #if _DEBUG
1955                 fgWarning( "Unknown wParam type 0x%x", wParam );
1956 #endif
1957                 break;
1958             }
1959         }
1960 #endif /* !TARGET_HOST_WINCE */
1961
1962         /* We need to pass the message on to the operating system as well */
1963         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1964         break;
1965
1966     default:
1967         /*
1968          * Handle unhandled messages
1969          */
1970         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1971         break;
1972     }
1973
1974     return lRet;
1975 }
1976 #endif
1977
1978 /*** END OF FILE ***/