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