Adding a multitouch patch from Eero Pajarre per e-mail dated 6/8/2011 1:14 AM
[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 #ifdef HAVE_ERRNO_H
31 #    include <errno.h>
32 #endif
33 #include <stdarg.h>
34 #ifdef  HAVE_VFPRINTF
35 #    define VFPRINTF(s,f,a) vfprintf((s),(f),(a))
36 #elif defined(HAVE__DOPRNT)
37 #    define VFPRINTF(s,f,a) _doprnt((f),(a),(s))
38 #else
39 #    define VFPRINTF(s,f,a)
40 #endif
41
42 #ifdef _WIN32_WCE
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 /* _WIN32_WCE */
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 #ifdef 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 #ifdef WM_TOUCH
74     typedef BOOL (WINAPI *pGetTouchInputInfo)(HTOUCHINPUT,UINT,PTOUCHINPUT,int);
75     typedef BOOL (WINAPI *pCloseTouchInputHandle)(HTOUCHINPUT);
76         static pGetTouchInputInfo fghGetTouchInputInfo = (pGetTouchInputInfo)0xDEADBEEF;
77         static pCloseTouchInputHandle fghCloseTouchInputHandle = (pCloseTouchInputHandle)0xDEADBEEF;
78 #endif
79
80 /*
81  * TODO BEFORE THE STABLE RELEASE:
82  *
83  * There are some issues concerning window redrawing under X11, and maybe
84  * some events are not handled. The Win32 version lacks some more features,
85  * but seems acceptable for not demanding purposes.
86  *
87  * Need to investigate why the X11 version breaks out with an error when
88  * closing a window (using the window manager, not glutDestroyWindow)...
89  */
90
91 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
92
93 /*
94  * Handle a window configuration change. When no reshape
95  * callback is hooked, the viewport size is updated to
96  * match the new window size.
97  */
98 static void fghReshapeWindow ( SFG_Window *window, int width, int height )
99 {
100     SFG_Window *current_window = fgStructure.CurrentWindow;
101
102     freeglut_return_if_fail( window != NULL );
103
104 #if TARGET_HOST_POSIX_X11
105
106     XResizeWindow( fgDisplay.Display, window->Window.Handle,
107                    width, height );
108     XFlush( fgDisplay.Display ); /* XXX Shouldn't need this */
109
110 #elif TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
111     {
112         RECT winRect;
113         int x, y, w, h;
114
115         /*
116          * For windowed mode, get the current position of the
117          * window and resize taking the size of the frame
118          * decorations into account.
119          */
120
121         /* "GetWindowRect" returns the pixel coordinates of the outside of the window */
122         GetWindowRect( window->Window.Handle, &winRect );
123         x = winRect.left;
124         y = winRect.top;
125         w = width;
126         h = height;
127
128         if ( window->Parent == NULL )
129         {
130            if ( ! window->IsMenu && (window != fgStructure.GameModeWindow) &&
131                !( fgState.DisplayMode & GLUT_BORDERLESS ))
132             {
133                 w += GetSystemMetrics( SM_CXSIZEFRAME ) * 2;
134                 h += GetSystemMetrics( SM_CYSIZEFRAME ) * 2 +
135                      GetSystemMetrics( SM_CYCAPTION );
136             }
137         }
138         else
139         {
140             RECT parentRect;
141             GetWindowRect( window->Parent->Window.Handle, &parentRect );
142             x -= parentRect.left + GetSystemMetrics( SM_CXSIZEFRAME ) * 2;
143             y -= parentRect.top  + GetSystemMetrics( SM_CYSIZEFRAME ) * 2 +
144                                    GetSystemMetrics( SM_CYCAPTION );
145         }
146
147         /*
148          * SWP_NOACTIVATE      Do not activate the window
149          * SWP_NOOWNERZORDER   Do not change position in z-order
150          * SWP_NOSENDCHANGING  Supress WM_WINDOWPOSCHANGING message
151          * SWP_NOZORDER        Retains the current Z order (ignore 2nd param)
152          */
153
154         SetWindowPos( window->Window.Handle,
155                       HWND_TOP,
156                       x, y, w, h,
157                       SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING |
158                       SWP_NOZORDER
159         );
160     }
161 #endif
162
163     /*
164      * XXX Should update {window->State.OldWidth, window->State.OldHeight}
165      * XXX to keep in lockstep with POSIX_X11 code.
166      */
167     if( FETCH_WCB( *window, Reshape ) )
168         INVOKE_WCB( *window, Reshape, ( width, height ) );
169     else
170     {
171         fgSetWindow( window );
172         glViewport( 0, 0, width, height );
173     }
174
175     /*
176      * Force a window redraw.  In Windows at least this is only a partial
177      * solution:  if the window is increasing in size in either dimension,
178      * the already-drawn part does not get drawn again and things look funny.
179      * But without this we get this bad behaviour whenever we resize the
180      * window.
181      */
182     window->State.Redisplay = GL_TRUE;
183
184     if( window->IsMenu )
185         fgSetWindow( current_window );
186 }
187
188 /*
189  * Calls a window's redraw method. This is used when
190  * a redraw is forced by the incoming window messages.
191  */
192 static void fghRedrawWindow ( SFG_Window *window )
193 {
194     SFG_Window *current_window = fgStructure.CurrentWindow;
195
196     freeglut_return_if_fail( window );
197     freeglut_return_if_fail( FETCH_WCB ( *window, Display ) );
198
199     window->State.Redisplay = GL_FALSE;
200
201     freeglut_return_if_fail( window->State.Visible );
202
203     fgSetWindow( window );
204
205     if( window->State.NeedToResize )
206     {
207         fghReshapeWindow(
208             window,
209             window->State.Width,
210             window->State.Height
211         );
212
213         window->State.NeedToResize = GL_FALSE;
214     }
215
216     INVOKE_WCB( *window, Display, ( ) );
217
218     fgSetWindow( current_window );
219 }
220
221 /*
222  * A static helper function to execute display callback for a window
223  */
224 static void fghcbDisplayWindow( SFG_Window *window,
225                                 SFG_Enumerator *enumerator )
226 {
227     if( window->State.Redisplay &&
228         window->State.Visible )
229     {
230         window->State.Redisplay = GL_FALSE;
231
232 #if TARGET_HOST_POSIX_X11
233         fghRedrawWindow ( window ) ;
234 #elif TARGET_HOST_MS_WINDOWS
235
236         RedrawWindow(
237             window->Window.Handle, NULL, NULL,
238             RDW_NOERASE | RDW_INTERNALPAINT | RDW_INVALIDATE | RDW_UPDATENOW
239         );
240 #endif
241     }
242
243     fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
244 }
245
246 /*
247  * Make all windows perform a display call
248  */
249 static void fghDisplayAll( void )
250 {
251     SFG_Enumerator enumerator;
252
253     enumerator.found = GL_FALSE;
254     enumerator.data  =  NULL;
255
256     fgEnumWindows( fghcbDisplayWindow, &enumerator );
257 }
258
259 /*
260  * Window enumerator callback to check for the joystick polling code
261  */
262 static void fghcbCheckJoystickPolls( SFG_Window *window,
263                                      SFG_Enumerator *enumerator )
264 {
265     long int checkTime = fgElapsedTime( );
266
267     if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=
268         checkTime )
269     {
270 #if !defined(_WIN32_WCE)
271         fgJoystickPollWindow( window );
272 #endif /* !defined(_WIN32_WCE) */
273         window->State.JoystickLastPoll = checkTime;
274     }
275
276     fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
277 }
278
279 /*
280  * Check all windows for joystick polling
281  */
282 static void fghCheckJoystickPolls( void )
283 {
284     SFG_Enumerator enumerator;
285
286     enumerator.found = GL_FALSE;
287     enumerator.data  =  NULL;
288
289     fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
290 }
291
292 /*
293  * Check the global timers
294  */
295 static void fghCheckTimers( void )
296 {
297     long checkTime = fgElapsedTime( );
298
299     while( fgState.Timers.First )
300     {
301         SFG_Timer *timer = fgState.Timers.First;
302
303         if( timer->TriggerTime > checkTime )
304             break;
305
306         fgListRemove( &fgState.Timers, &timer->Node );
307         fgListAppend( &fgState.FreeTimers, &timer->Node );
308
309         timer->Callback( timer->ID );
310     }
311 }
312
313  
314 /* Platform-dependent time in milliseconds, as an unsigned 32-bit integer.
315  * This value wraps every 49.7 days, but integer overflows cancel
316  * when subtracting an initial start time, unless the total time exceeds
317  * 32-bit, where the GLUT API return value is also overflowed.
318  */  
319 unsigned long fgSystemTime(void) {
320 #if TARGET_HOST_SOLARIS || HAVE_GETTIMEOFDAY
321     struct timeval now;
322     gettimeofday( &now, NULL );
323     return now.tv_usec/1000 + now.tv_sec*1000;
324 #elif TARGET_HOST_MS_WINDOWS
325 #    if defined(_WIN32_WCE)
326     return GetTickCount();
327 #    else
328     return timeGetTime();
329 #    endif
330 #endif
331 }
332   
333 /*
334  * Elapsed Time
335  */
336 long fgElapsedTime( void )
337 {
338     return (long) (fgSystemTime() - fgState.Time);
339 }
340
341 /*
342  * Error Messages.
343  */
344 void fgError( const char *fmt, ... )
345 {
346     va_list ap;
347
348     if (fgState.ErrorFunc) {
349
350         va_start( ap, fmt );
351
352         /* call user set error handler here */
353         fgState.ErrorFunc(fmt, ap);
354
355         va_end( ap );
356
357     } else {
358
359         va_start( ap, fmt );
360
361         fprintf( stderr, "freeglut ");
362         if( fgState.ProgramName )
363             fprintf( stderr, "(%s): ", fgState.ProgramName );
364         VFPRINTF( stderr, fmt, ap );
365         fprintf( stderr, "\n" );
366
367         va_end( ap );
368
369         if ( fgState.Initialised )
370             fgDeinitialize ();
371
372         exit( 1 );
373     }
374 }
375
376 void fgWarning( const char *fmt, ... )
377 {
378     va_list ap;
379
380     if (fgState.WarningFunc) {
381
382         va_start( ap, fmt );
383
384         /* call user set warning handler here */
385         fgState.WarningFunc(fmt, ap);
386
387         va_end( ap );
388
389     } else {
390
391         va_start( ap, fmt );
392
393         fprintf( stderr, "freeglut ");
394         if( fgState.ProgramName )
395             fprintf( stderr, "(%s): ", fgState.ProgramName );
396         VFPRINTF( stderr, fmt, ap );
397         fprintf( stderr, "\n" );
398
399         va_end( ap );
400     }
401 }
402
403
404 /*
405  * Indicates whether Joystick events are being used by ANY window.
406  *
407  * The current mechanism is to walk all of the windows and ask if
408  * there is a joystick callback.  We have a short-circuit early
409  * return if we find any joystick handler registered.
410  *
411  * The real way to do this is to make use of the glutTimer() API
412  * to more cleanly re-implement the joystick API.  Then, this code
413  * and all other "joystick timer" code can be yanked.
414  *
415  */
416 static void fghCheckJoystickCallback( SFG_Window* w, SFG_Enumerator* e)
417 {
418     if( FETCH_WCB( *w, Joystick ) )
419     {
420         e->found = GL_TRUE;
421         e->data = w;
422     }
423     fgEnumSubWindows( w, fghCheckJoystickCallback, e );
424 }
425 static int fghHaveJoystick( void )
426 {
427     SFG_Enumerator enumerator;
428
429     enumerator.found = GL_FALSE;
430     enumerator.data = NULL;
431     fgEnumWindows( fghCheckJoystickCallback, &enumerator );
432     return !!enumerator.data;
433 }
434 static void fghHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)
435 {
436     if( w->State.Redisplay && w->State.Visible )
437     {
438         e->found = GL_TRUE;
439         e->data = w;
440     }
441     fgEnumSubWindows( w, fghHavePendingRedisplaysCallback, e );
442 }
443 static int fghHavePendingRedisplays (void)
444 {
445     SFG_Enumerator enumerator;
446
447     enumerator.found = GL_FALSE;
448     enumerator.data = NULL;
449     fgEnumWindows( fghHavePendingRedisplaysCallback, &enumerator );
450     return !!enumerator.data;
451 }
452 /*
453  * Returns the number of GLUT ticks (milliseconds) till the next timer event.
454  */
455 static long fghNextTimer( void )
456 {
457     long ret = INT_MAX;
458     SFG_Timer *timer = fgState.Timers.First;
459
460     if( timer )
461         ret = timer->TriggerTime - fgElapsedTime();
462     if( ret < 0 )
463         ret = 0;
464
465     return ret;
466 }
467 /*
468  * Does the magic required to relinquish the CPU until something interesting
469  * happens.
470  */
471 static void fghSleepForEvents( void )
472 {
473     long msec;
474
475     if( fgState.IdleCallback || fghHavePendingRedisplays( ) )
476         return;
477
478     msec = fghNextTimer( );
479     /* XXX Use GLUT timers for joysticks... */
480     /* XXX Dumb; forces granularity to .01sec */
481     if( fghHaveJoystick( ) && ( msec > 10 ) )     
482         msec = 10;
483
484 #if TARGET_HOST_POSIX_X11
485     /*
486      * Possibly due to aggressive use of XFlush() and friends,
487      * it is possible to have our socket drained but still have
488      * unprocessed events.  (Or, this may just be normal with
489      * X, anyway?)  We do non-trivial processing of X events
490      * after the event-reading loop, in any case, so we
491      * need to allow that we may have an empty socket but non-
492      * empty event queue.
493      */
494     if( ! XPending( fgDisplay.Display ) )
495     {
496         fd_set fdset;
497         int err;
498         int socket;
499         struct timeval wait;
500
501         socket = ConnectionNumber( fgDisplay.Display );
502         FD_ZERO( &fdset );
503         FD_SET( socket, &fdset );
504         wait.tv_sec = msec / 1000;
505         wait.tv_usec = (msec % 1000) * 1000;
506         err = select( socket+1, &fdset, NULL, NULL, &wait );
507
508 #ifdef HAVE_ERRNO_H
509         if( ( -1 == err ) && ( errno != EINTR ) )
510             fgWarning ( "freeglut select() error: %d", errno );
511 #endif
512     }
513 #elif TARGET_HOST_MS_WINDOWS
514     MsgWaitForMultipleObjects( 0, NULL, FALSE, msec, QS_ALLINPUT );
515 #endif
516 }
517
518 #if TARGET_HOST_POSIX_X11
519 /*
520  * Returns GLUT modifier mask for the state field of an X11 event.
521  */
522 int fghGetXModifiers( int state )
523 {
524     int ret = 0;
525
526     if( state & ( ShiftMask | LockMask ) )
527         ret |= GLUT_ACTIVE_SHIFT;
528     if( state & ControlMask )
529         ret |= GLUT_ACTIVE_CTRL;
530     if( state & Mod1Mask )
531         ret |= GLUT_ACTIVE_ALT;
532
533     return ret;
534 }
535 #endif
536
537
538 #if TARGET_HOST_POSIX_X11 && _DEBUG
539
540 static const char* fghTypeToString( int type )
541 {
542     switch( type ) {
543     case KeyPress: return "KeyPress";
544     case KeyRelease: return "KeyRelease";
545     case ButtonPress: return "ButtonPress";
546     case ButtonRelease: return "ButtonRelease";
547     case MotionNotify: return "MotionNotify";
548     case EnterNotify: return "EnterNotify";
549     case LeaveNotify: return "LeaveNotify";
550     case FocusIn: return "FocusIn";
551     case FocusOut: return "FocusOut";
552     case KeymapNotify: return "KeymapNotify";
553     case Expose: return "Expose";
554     case GraphicsExpose: return "GraphicsExpose";
555     case NoExpose: return "NoExpose";
556     case VisibilityNotify: return "VisibilityNotify";
557     case CreateNotify: return "CreateNotify";
558     case DestroyNotify: return "DestroyNotify";
559     case UnmapNotify: return "UnmapNotify";
560     case MapNotify: return "MapNotify";
561     case MapRequest: return "MapRequest";
562     case ReparentNotify: return "ReparentNotify";
563     case ConfigureNotify: return "ConfigureNotify";
564     case ConfigureRequest: return "ConfigureRequest";
565     case GravityNotify: return "GravityNotify";
566     case ResizeRequest: return "ResizeRequest";
567     case CirculateNotify: return "CirculateNotify";
568     case CirculateRequest: return "CirculateRequest";
569     case PropertyNotify: return "PropertyNotify";
570     case SelectionClear: return "SelectionClear";
571     case SelectionRequest: return "SelectionRequest";
572     case SelectionNotify: return "SelectionNotify";
573     case ColormapNotify: return "ColormapNotify";
574     case ClientMessage: return "ClientMessage";
575     case MappingNotify: return "MappingNotify";
576     default: return "UNKNOWN";
577     }
578 }
579
580 static const char* fghBoolToString( Bool b )
581 {
582     return b == False ? "False" : "True";
583 }
584
585 static const char* fghNotifyHintToString( char is_hint )
586 {
587     switch( is_hint ) {
588     case NotifyNormal: return "NotifyNormal";
589     case NotifyHint: return "NotifyHint";
590     default: return "UNKNOWN";
591     }
592 }
593
594 static const char* fghNotifyModeToString( int mode )
595 {
596     switch( mode ) {
597     case NotifyNormal: return "NotifyNormal";
598     case NotifyGrab: return "NotifyGrab";
599     case NotifyUngrab: return "NotifyUngrab";
600     case NotifyWhileGrabbed: return "NotifyWhileGrabbed";
601     default: return "UNKNOWN";
602     }
603 }
604
605 static const char* fghNotifyDetailToString( int detail )
606 {
607     switch( detail ) {
608     case NotifyAncestor: return "NotifyAncestor";
609     case NotifyVirtual: return "NotifyVirtual";
610     case NotifyInferior: return "NotifyInferior";
611     case NotifyNonlinear: return "NotifyNonlinear";
612     case NotifyNonlinearVirtual: return "NotifyNonlinearVirtual";
613     case NotifyPointer: return "NotifyPointer";
614     case NotifyPointerRoot: return "NotifyPointerRoot";
615     case NotifyDetailNone: return "NotifyDetailNone";
616     default: return "UNKNOWN";
617     }
618 }
619
620 static const char* fghVisibilityToString( int state ) {
621     switch( state ) {
622     case VisibilityUnobscured: return "VisibilityUnobscured";
623     case VisibilityPartiallyObscured: return "VisibilityPartiallyObscured";
624     case VisibilityFullyObscured: return "VisibilityFullyObscured";
625     default: return "UNKNOWN";
626     }
627 }
628
629 static const char* fghConfigureDetailToString( int detail )
630 {
631     switch( detail ) {
632     case Above: return "Above";
633     case Below: return "Below";
634     case TopIf: return "TopIf";
635     case BottomIf: return "BottomIf";
636     case Opposite: return "Opposite";
637     default: return "UNKNOWN";
638     }
639 }
640
641 static const char* fghPlaceToString( int place )
642 {
643     switch( place ) {
644     case PlaceOnTop: return "PlaceOnTop";
645     case PlaceOnBottom: return "PlaceOnBottom";
646     default: return "UNKNOWN";
647     }
648 }
649
650 static const char* fghMappingRequestToString( int request )
651 {
652     switch( request ) {
653     case MappingModifier: return "MappingModifier";
654     case MappingKeyboard: return "MappingKeyboard";
655     case MappingPointer: return "MappingPointer";
656     default: return "UNKNOWN";
657     }
658 }
659
660 static const char* fghPropertyStateToString( int state )
661 {
662     switch( state ) {
663     case PropertyNewValue: return "PropertyNewValue";
664     case PropertyDelete: return "PropertyDelete";
665     default: return "UNKNOWN";
666     }
667 }
668
669 static const char* fghColormapStateToString( int state )
670 {
671     switch( state ) {
672     case ColormapUninstalled: return "ColormapUninstalled";
673     case ColormapInstalled: return "ColormapInstalled";
674     default: return "UNKNOWN";
675     }
676 }
677
678 static void fghPrintEvent( XEvent *event )
679 {
680     switch( event->type ) {
681
682     case KeyPress:
683     case KeyRelease: {
684         XKeyEvent *e = &event->xkey;
685         fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
686                    "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
687                    "keycode=%u, same_screen=%s", fghTypeToString( e->type ),
688                    e->window, e->root, e->subwindow, (unsigned long)e->time,
689                    e->x, e->y, e->x_root, e->y_root, e->state, e->keycode,
690                    fghBoolToString( e->same_screen ) );
691         break;
692     }
693
694     case ButtonPress:
695     case ButtonRelease: {
696         XButtonEvent *e = &event->xbutton;
697         fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
698                    "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
699                    "button=%u, same_screen=%d", fghTypeToString( e->type ),
700                    e->window, e->root, e->subwindow, (unsigned long)e->time,
701                    e->x, e->y, e->x_root, e->y_root, e->state, e->button,
702                    fghBoolToString( e->same_screen ) );
703         break;
704     }
705
706     case MotionNotify: {
707         XMotionEvent *e = &event->xmotion;
708         fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
709                    "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
710                    "is_hint=%s, same_screen=%d", fghTypeToString( e->type ),
711                    e->window, e->root, e->subwindow, (unsigned long)e->time,
712                    e->x, e->y, e->x_root, e->y_root, e->state,
713                    fghNotifyHintToString( e->is_hint ),
714                    fghBoolToString( e->same_screen ) );
715         break;
716     }
717
718     case EnterNotify:
719     case LeaveNotify: {
720         XCrossingEvent *e = &event->xcrossing;
721         fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
722                    "(x,y)=(%d,%d), mode=%s, detail=%s, same_screen=%d, "
723                    "focus=%d, state=0x%x", fghTypeToString( e->type ),
724                    e->window, e->root, e->subwindow, (unsigned long)e->time,
725                    e->x, e->y, fghNotifyModeToString( e->mode ),
726                    fghNotifyDetailToString( e->detail ), (int)e->same_screen,
727                    (int)e->focus, e->state );
728         break;
729     }
730
731     case FocusIn:
732     case FocusOut: {
733         XFocusChangeEvent *e = &event->xfocus;
734         fgWarning( "%s: window=0x%x, mode=%s, detail=%s",
735                    fghTypeToString( e->type ), e->window,
736                    fghNotifyModeToString( e->mode ),
737                    fghNotifyDetailToString( e->detail ) );
738         break;
739     }
740
741     case KeymapNotify: {
742         XKeymapEvent *e = &event->xkeymap;
743         char buf[32 * 2 + 1];
744         int i;
745         for ( i = 0; i < 32; i++ ) {
746             snprintf( &buf[ i * 2 ], sizeof( buf ) - i * 2,
747                       "%02x", e->key_vector[ i ] );
748         }
749         buf[ i ] = '\0';
750         fgWarning( "%s: window=0x%x, %s", fghTypeToString( e->type ), e->window,
751                    buf );
752         break;
753     }
754
755     case Expose: {
756         XExposeEvent *e = &event->xexpose;
757         fgWarning( "%s: window=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), "
758                    "count=%d", fghTypeToString( e->type ), e->window, e->x,
759                    e->y, e->width, e->height, e->count );
760         break;
761     }
762
763     case GraphicsExpose: {
764         XGraphicsExposeEvent *e = &event->xgraphicsexpose;
765         fgWarning( "%s: drawable=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), "
766                    "count=%d, (major_code,minor_code)=(%d,%d)",
767                    fghTypeToString( e->type ), e->drawable, e->x, e->y,
768                    e->width, e->height, e->count, e->major_code,
769                    e->minor_code );
770         break;
771     }
772
773     case NoExpose: {
774         XNoExposeEvent *e = &event->xnoexpose;
775         fgWarning( "%s: drawable=0x%x, (major_code,minor_code)=(%d,%d)",
776                    fghTypeToString( e->type ), e->drawable, e->major_code,
777                    e->minor_code );
778         break;
779     }
780
781     case VisibilityNotify: {
782         XVisibilityEvent *e = &event->xvisibility;
783         fgWarning( "%s: window=0x%x, state=%s", fghTypeToString( e->type ),
784                    e->window, fghVisibilityToString( e->state) );
785         break;
786     }
787
788     case CreateNotify: {
789         XCreateWindowEvent *e = &event->xcreatewindow;
790         fgWarning( "%s: (x,y)=(%d,%d), (width,height)=(%d,%d), border_width=%d, "
791                    "window=0x%x, override_redirect=%s",
792                    fghTypeToString( e->type ), e->x, e->y, e->width, e->height,
793                    e->border_width, e->window,
794                    fghBoolToString( e->override_redirect ) );
795         break;
796     }
797
798     case DestroyNotify: {
799         XDestroyWindowEvent *e = &event->xdestroywindow;
800         fgWarning( "%s: event=0x%x, window=0x%x",
801                    fghTypeToString( e->type ), e->event, e->window );
802         break;
803     }
804
805     case UnmapNotify: {
806         XUnmapEvent *e = &event->xunmap;
807         fgWarning( "%s: event=0x%x, window=0x%x, from_configure=%s",
808                    fghTypeToString( e->type ), e->event, e->window,
809                    fghBoolToString( e->from_configure ) );
810         break;
811     }
812
813     case MapNotify: {
814         XMapEvent *e = &event->xmap;
815         fgWarning( "%s: event=0x%x, window=0x%x, override_redirect=%s",
816                    fghTypeToString( e->type ), e->event, e->window,
817                    fghBoolToString( e->override_redirect ) );
818         break;
819     }
820
821     case MapRequest: {
822         XMapRequestEvent *e = &event->xmaprequest;
823         fgWarning( "%s: parent=0x%x, window=0x%x",
824                    fghTypeToString( event->type ), e->parent, e->window );
825         break;
826     }
827
828     case ReparentNotify: {
829         XReparentEvent *e = &event->xreparent;
830         fgWarning( "%s: event=0x%x, window=0x%x, parent=0x%x, (x,y)=(%d,%d), "
831                    "override_redirect=%s", fghTypeToString( e->type ),
832                    e->event, e->window, e->parent, e->x, e->y,
833                    fghBoolToString( e->override_redirect ) );
834         break;
835     }
836
837     case ConfigureNotify: {
838         XConfigureEvent *e = &event->xconfigure;
839         fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d), "
840                    "(width,height)=(%d,%d), border_width=%d, above=0x%x, "
841                    "override_redirect=%s", fghTypeToString( e->type ), e->event,
842                    e->window, e->x, e->y, e->width, e->height, e->border_width,
843                    e->above, fghBoolToString( e->override_redirect ) );
844         break;
845     }
846
847     case ConfigureRequest: {
848         XConfigureRequestEvent *e = &event->xconfigurerequest;
849         fgWarning( "%s: parent=0x%x, window=0x%x, (x,y)=(%d,%d), "
850                    "(width,height)=(%d,%d), border_width=%d, above=0x%x, "
851                    "detail=%s, value_mask=%lx", fghTypeToString( e->type ),
852                    e->parent, e->window, e->x, e->y, e->width, e->height,
853                    e->border_width, e->above,
854                    fghConfigureDetailToString( e->detail ), e->value_mask );
855         break;
856     }
857
858     case GravityNotify: {
859         XGravityEvent *e = &event->xgravity;
860         fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d)",
861                    fghTypeToString( e->type ), e->event, e->window, e->x, e->y );
862         break;
863     }
864
865     case ResizeRequest: {
866         XResizeRequestEvent *e = &event->xresizerequest;
867         fgWarning( "%s: window=0x%x, (width,height)=(%d,%d)",
868                    fghTypeToString( e->type ), e->window, e->width, e->height );
869         break;
870     }
871
872     case CirculateNotify: {
873         XCirculateEvent *e = &event->xcirculate;
874         fgWarning( "%s: event=0x%x, window=0x%x, place=%s",
875                    fghTypeToString( e->type ), e->event, e->window,
876                    fghPlaceToString( e->place ) );
877         break;
878     }
879
880     case CirculateRequest: {
881         XCirculateRequestEvent *e = &event->xcirculaterequest;
882         fgWarning( "%s: parent=0x%x, window=0x%x, place=%s",
883                    fghTypeToString( e->type ), e->parent, e->window,
884                    fghPlaceToString( e->place ) );
885         break;
886     }
887
888     case PropertyNotify: {
889         XPropertyEvent *e = &event->xproperty;
890         fgWarning( "%s: window=0x%x, atom=%lu, time=%lu, state=%s",
891                    fghTypeToString( e->type ), e->window,
892                    (unsigned long)e->atom, (unsigned long)e->time,
893                    fghPropertyStateToString( e->state ) );
894         break;
895     }
896
897     case SelectionClear: {
898         XSelectionClearEvent *e = &event->xselectionclear;
899         fgWarning( "%s: window=0x%x, selection=%lu, time=%lu",
900                    fghTypeToString( e->type ), e->window,
901                    (unsigned long)e->selection, (unsigned long)e->time );
902         break;
903     }
904
905     case SelectionRequest: {
906         XSelectionRequestEvent *e = &event->xselectionrequest;
907         fgWarning( "%s: owner=0x%x, requestor=0x%x, selection=0x%x, "
908                    "target=0x%x, property=%lu, time=%lu",
909                    fghTypeToString( e->type ), e->owner, e->requestor,
910                    (unsigned long)e->selection, (unsigned long)e->target,
911                    (unsigned long)e->property, (unsigned long)e->time );
912         break;
913     }
914
915     case SelectionNotify: {
916         XSelectionEvent *e = &event->xselection;
917         fgWarning( "%s: requestor=0x%x, selection=0x%x, target=0x%x, "
918                    "property=%lu, time=%lu", fghTypeToString( e->type ),
919                    e->requestor, (unsigned long)e->selection,
920                    (unsigned long)e->target, (unsigned long)e->property,
921                    (unsigned long)e->time );
922         break;
923     }
924
925     case ColormapNotify: {
926         XColormapEvent *e = &event->xcolormap;
927         fgWarning( "%s: window=0x%x, colormap=%lu, new=%s, state=%s",
928                    fghTypeToString( e->type ), e->window,
929                    (unsigned long)e->colormap, fghBoolToString( e->new ),
930                    fghColormapStateToString( e->state ) );
931         break;
932     }
933
934     case ClientMessage: {
935         XClientMessageEvent *e = &event->xclient;
936         char buf[ 61 ];
937         char* p = buf;
938         char* end = buf + sizeof( buf );
939         int i;
940         switch( e->format ) {
941         case 8:
942           for ( i = 0; i < 20; i++, p += 3 ) {
943                 snprintf( p, end - p, " %02x", e->data.b[ i ] );
944             }
945             break;
946         case 16:
947             for ( i = 0; i < 10; i++, p += 5 ) {
948                 snprintf( p, end - p, " %04x", e->data.s[ i ] );
949             }
950             break;
951         case 32:
952             for ( i = 0; i < 5; i++, p += 9 ) {
953                 snprintf( p, end - p, " %08lx", e->data.l[ i ] );
954             }
955             break;
956         }
957         *p = '\0';
958         fgWarning( "%s: window=0x%x, message_type=%lu, format=%d, data=(%s )",
959                    fghTypeToString( e->type ), e->window,
960                    (unsigned long)e->message_type, e->format, buf );
961         break;
962     }
963
964     case MappingNotify: {
965         XMappingEvent *e = &event->xmapping;
966         fgWarning( "%s: window=0x%x, request=%s, first_keycode=%d, count=%d",
967                    fghTypeToString( e->type ), e->window,
968                    fghMappingRequestToString( e->request ), e->first_keycode,
969                    e->count );
970         break;
971     }
972
973     default: {
974         fgWarning( "%s", fghTypeToString( event->type ) );
975         break;
976     }
977     }
978 }
979
980 #endif
981
982 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
983
984 /*
985  * Executes a single iteration in the freeglut processing loop.
986  */
987 void FGAPIENTRY glutMainLoopEvent( void )
988 {
989 #if TARGET_HOST_POSIX_X11
990     SFG_Window* window;
991     XEvent event;
992
993     /* This code was repeated constantly, so here it goes into a definition: */
994 #define GETWINDOW(a)                             \
995     window = fgWindowByHandle( event.a.window ); \
996     if( window == NULL )                         \
997         break;
998
999 #define GETMOUSE(a)                              \
1000     window->State.MouseX = event.a.x;            \
1001     window->State.MouseY = event.a.y;
1002
1003     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
1004
1005     while( XPending( fgDisplay.Display ) )
1006     {
1007         XNextEvent( fgDisplay.Display, &event );
1008 #if _DEBUG
1009         fghPrintEvent( &event );
1010 #endif
1011
1012         switch( event.type )
1013         {
1014         case ClientMessage:
1015             if(fgIsSpaceballXEvent(&event)) {
1016                 fgSpaceballHandleXEvent(&event);
1017                 break;
1018             }
1019             /* Destroy the window when the WM_DELETE_WINDOW message arrives */
1020             if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.DeleteWindow )
1021             {
1022                 GETWINDOW( xclient );
1023
1024                 fgDestroyWindow ( window );
1025
1026                 if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
1027                 {
1028                     fgDeinitialize( );
1029                     exit( 0 );
1030                 }
1031                 else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
1032                     fgState.ExecState = GLUT_EXEC_STATE_STOP;
1033
1034                 return;
1035             }
1036             break;
1037
1038             /*
1039              * CreateNotify causes a configure-event so that sub-windows are
1040              * handled compatibly with GLUT.  Otherwise, your sub-windows
1041              * (in freeglut only) will not get an initial reshape event,
1042              * which can break things.
1043              *
1044              * GLUT presumably does this because it generally tries to treat
1045              * sub-windows the same as windows.
1046              */
1047         case CreateNotify:
1048         case ConfigureNotify:
1049             {
1050                 int width, height;
1051                 if( event.type == CreateNotify ) {
1052                     GETWINDOW( xcreatewindow );
1053                     width = event.xcreatewindow.width;
1054                     height = event.xcreatewindow.height;
1055                 } else {
1056                     GETWINDOW( xconfigure );
1057                     width = event.xconfigure.width;
1058                     height = event.xconfigure.height;
1059                 }
1060
1061                 if( ( width != window->State.OldWidth ) ||
1062                     ( height != window->State.OldHeight ) )
1063                 {
1064                     SFG_Window *current_window = fgStructure.CurrentWindow;
1065
1066                     window->State.OldWidth = width;
1067                     window->State.OldHeight = height;
1068                     if( FETCH_WCB( *window, Reshape ) )
1069                         INVOKE_WCB( *window, Reshape, ( width, height ) );
1070                     else
1071                     {
1072                         fgSetWindow( window );
1073                         glViewport( 0, 0, width, height );
1074                     }
1075                     glutPostRedisplay( );
1076                     if( window->IsMenu )
1077                         fgSetWindow( current_window );
1078                 }
1079             }
1080             break;
1081
1082         case DestroyNotify:
1083             /*
1084              * This is sent to confirm the XDestroyWindow call.
1085              *
1086              * XXX WHY is this commented out?  Should we re-enable it?
1087              */
1088             /* fgAddToWindowDestroyList ( window ); */
1089             break;
1090
1091         case Expose:
1092             /*
1093              * We are too dumb to process partial exposes...
1094              *
1095              * XXX Well, we could do it.  However, it seems to only
1096              * XXX be potentially useful for single-buffered (since
1097              * XXX double-buffered does not respect viewport when we
1098              * XXX do a buffer-swap).
1099              *
1100              */
1101             if( event.xexpose.count == 0 )
1102             {
1103                 GETWINDOW( xexpose );
1104                 window->State.Redisplay = GL_TRUE;
1105             }
1106             break;
1107
1108         case MapNotify:
1109             break;
1110
1111         case UnmapNotify:
1112             /* We get this when iconifying a window. */ 
1113             GETWINDOW( xunmap );
1114             INVOKE_WCB( *window, WindowStatus, ( GLUT_HIDDEN ) );
1115             window->State.Visible = GL_FALSE;
1116             break;
1117
1118         case MappingNotify:
1119             /*
1120              * Have the client's keyboard knowledge updated (xlib.ps,
1121              * page 206, says that's a good thing to do)
1122              */
1123             XRefreshKeyboardMapping( (XMappingEvent *) &event );
1124             break;
1125
1126         case VisibilityNotify:
1127         {
1128             /*
1129              * Sending this event, the X server can notify us that the window
1130              * has just acquired one of the three possible visibility states:
1131              * VisibilityUnobscured, VisibilityPartiallyObscured or
1132              * VisibilityFullyObscured. Note that we DO NOT receive a
1133              * VisibilityNotify event when iconifying a window, we only get an
1134              * UnmapNotify then.
1135              */
1136             GETWINDOW( xvisibility );
1137             switch( event.xvisibility.state )
1138             {
1139             case VisibilityUnobscured:
1140                 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_RETAINED ) );
1141                 window->State.Visible = GL_TRUE;
1142                 break;
1143
1144             case VisibilityPartiallyObscured:
1145                 INVOKE_WCB( *window, WindowStatus,
1146                             ( GLUT_PARTIALLY_RETAINED ) );
1147                 window->State.Visible = GL_TRUE;
1148                 break;
1149
1150             case VisibilityFullyObscured:
1151                 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_COVERED ) );
1152                 window->State.Visible = GL_FALSE;
1153                 break;
1154
1155             default:
1156                 fgWarning( "Unknown X visibility state: %d",
1157                            event.xvisibility.state );
1158                 break;
1159             }
1160         }
1161         break;
1162
1163         case EnterNotify:
1164         case LeaveNotify:
1165             GETWINDOW( xcrossing );
1166             GETMOUSE( xcrossing );
1167             if( ( event.type == LeaveNotify ) && window->IsMenu &&
1168                 window->ActiveMenu && window->ActiveMenu->IsActive )
1169                 fgUpdateMenuHighlight( window->ActiveMenu );
1170
1171             INVOKE_WCB( *window, Entry, ( ( EnterNotify == event.type ) ?
1172                                           GLUT_ENTERED :
1173                                           GLUT_LEFT ) );
1174             break;
1175
1176         case MotionNotify:
1177         {
1178             GETWINDOW( xmotion );
1179             GETMOUSE( xmotion );
1180
1181             if( window->ActiveMenu )
1182             {
1183                 if( window == window->ActiveMenu->ParentWindow )
1184                 {
1185                     window->ActiveMenu->Window->State.MouseX =
1186                         event.xmotion.x_root - window->ActiveMenu->X;
1187                     window->ActiveMenu->Window->State.MouseY =
1188                         event.xmotion.y_root - window->ActiveMenu->Y;
1189                 }
1190
1191                 fgUpdateMenuHighlight( window->ActiveMenu );
1192
1193                 break;
1194             }
1195
1196             /*
1197              * XXX For more than 5 buttons, just check {event.xmotion.state},
1198              * XXX rather than a host of bit-masks?  Or maybe we need to
1199              * XXX track ButtonPress/ButtonRelease events in our own
1200              * XXX bit-mask?
1201              */
1202             fgState.Modifiers = fghGetXModifiers( event.xmotion.state );
1203             if ( event.xmotion.state & ( Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask ) ) {
1204                 INVOKE_WCB( *window, Motion, ( event.xmotion.x,
1205                                                event.xmotion.y ) );
1206             } else {
1207                 INVOKE_WCB( *window, Passive, ( event.xmotion.x,
1208                                                 event.xmotion.y ) );
1209             }
1210             fgState.Modifiers = INVALID_MODIFIERS;
1211         }
1212         break;
1213
1214         case ButtonRelease:
1215         case ButtonPress:
1216         {
1217             GLboolean pressed = GL_TRUE;
1218             int button;
1219
1220             if( event.type == ButtonRelease )
1221                 pressed = GL_FALSE ;
1222
1223             /*
1224              * A mouse button has been pressed or released. Traditionally,
1225              * break if the window was found within the freeglut structures.
1226              */
1227             GETWINDOW( xbutton );
1228             GETMOUSE( xbutton );
1229
1230             /*
1231              * An X button (at least in XFree86) is numbered from 1.
1232              * A GLUT button is numbered from 0.
1233              * Old GLUT passed through buttons other than just the first
1234              * three, though it only gave symbolic names and official
1235              * support to the first three.
1236              */
1237             button = event.xbutton.button - 1;
1238
1239             /*
1240              * Do not execute the application's mouse callback if a menu
1241              * is hooked to this button.  In that case an appropriate
1242              * private call should be generated.
1243              */
1244             if( fgCheckActiveMenu( window, button, pressed,
1245                                    event.xbutton.x_root, event.xbutton.y_root ) )
1246                 break;
1247
1248             /*
1249              * Check if there is a mouse or mouse wheel callback hooked to the
1250              * window
1251              */
1252             if( ! FETCH_WCB( *window, Mouse ) &&
1253                 ! FETCH_WCB( *window, MouseWheel ) )
1254                 break;
1255
1256             fgState.Modifiers = fghGetXModifiers( event.xbutton.state );
1257
1258             /* Finally execute the mouse or mouse wheel callback */
1259             if( ( button < glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS ) ) || ( ! FETCH_WCB( *window, MouseWheel ) ) )
1260                 INVOKE_WCB( *window, Mouse, ( button,
1261                                               pressed ? GLUT_DOWN : GLUT_UP,
1262                                               event.xbutton.x,
1263                                               event.xbutton.y )
1264                 );
1265             else
1266             {
1267                 /*
1268                  * Map 4 and 5 to wheel zero; EVEN to +1, ODD to -1
1269                  *  "  6 and 7 "    "   one; ...
1270                  *
1271                  * XXX This *should* be behind some variables/macros,
1272                  * XXX since the order and numbering isn't certain
1273                  * XXX See XFree86 configuration docs (even back in the
1274                  * XXX 3.x days, and especially with 4.x).
1275                  *
1276                  * XXX Note that {button} has already been decremented
1277                  * XXX in mapping from X button numbering to GLUT.
1278                                  *
1279                                  * XXX Should add support for partial wheel turns as Windows does -- 5/27/11
1280                  */
1281                 int wheel_number = (button - glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS )) / 2;
1282                 int direction = -1;
1283                 if( button % 2 )
1284                     direction = 1;
1285
1286                 if( pressed )
1287                     INVOKE_WCB( *window, MouseWheel, ( wheel_number,
1288                                                        direction,
1289                                                        event.xbutton.x,
1290                                                        event.xbutton.y )
1291                     );
1292             }
1293             fgState.Modifiers = INVALID_MODIFIERS;
1294         }
1295         break;
1296
1297         case KeyRelease:
1298         case KeyPress:
1299         {
1300             FGCBKeyboard keyboard_cb;
1301             FGCBSpecial special_cb;
1302
1303             GETWINDOW( xkey );
1304             GETMOUSE( xkey );
1305
1306             /* Detect auto repeated keys, if configured globally or per-window */
1307
1308             if ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE )
1309             {
1310                 if (event.type==KeyRelease)
1311                 {
1312                     /*
1313                      * Look at X11 keystate to detect repeat mode.
1314                      * While X11 says the key is actually held down, we'll ignore KeyRelease/KeyPress pairs.
1315                      */
1316
1317                     char keys[32];
1318                     XQueryKeymap( fgDisplay.Display, keys ); /* Look at X11 keystate to detect repeat mode */
1319
1320                     if ( event.xkey.keycode<256 )            /* XQueryKeymap is limited to 256 keycodes    */
1321                     {
1322                         if ( keys[event.xkey.keycode>>3] & (1<<(event.xkey.keycode%8)) )
1323                             window->State.KeyRepeating = GL_TRUE;
1324                         else
1325                             window->State.KeyRepeating = GL_FALSE;
1326                     }
1327                 }
1328             }
1329             else
1330                 window->State.KeyRepeating = GL_FALSE;
1331
1332             /* Cease processing this event if it is auto repeated */
1333
1334             if (window->State.KeyRepeating)
1335             {
1336                 if (event.type == KeyPress) window->State.KeyRepeating = GL_FALSE;
1337                 break;
1338             }
1339
1340             if( event.type == KeyPress )
1341             {
1342                 keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, Keyboard ));
1343                 special_cb  = (FGCBSpecial) ( FETCH_WCB( *window, Special  ));
1344             }
1345             else
1346             {
1347                 keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, KeyboardUp ));
1348                 special_cb  = (FGCBSpecial) ( FETCH_WCB( *window, SpecialUp  ));
1349             }
1350
1351             /* Is there a keyboard/special callback hooked for this window? */
1352             if( keyboard_cb || special_cb )
1353             {
1354                 XComposeStatus composeStatus;
1355                 char asciiCode[ 32 ];
1356                 KeySym keySym;
1357                 int len;
1358
1359                 /* Check for the ASCII/KeySym codes associated with the event: */
1360                 len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode),
1361                                      &keySym, &composeStatus
1362                 );
1363
1364                 /* GLUT API tells us to have two separate callbacks... */
1365                 if( len > 0 )
1366                 {
1367                     /* ...one for the ASCII translateable keypresses... */
1368                     if( keyboard_cb )
1369                     {
1370                         fgSetWindow( window );
1371                         fgState.Modifiers = fghGetXModifiers( event.xkey.state );
1372                         keyboard_cb( asciiCode[ 0 ],
1373                                      event.xkey.x, event.xkey.y
1374                         );
1375                         fgState.Modifiers = INVALID_MODIFIERS;
1376                     }
1377                 }
1378                 else
1379                 {
1380                     int special = -1;
1381
1382                     /*
1383                      * ...and one for all the others, which need to be
1384                      * translated to GLUT_KEY_Xs...
1385                      */
1386                     switch( keySym )
1387                     {
1388                     case XK_F1:     special = GLUT_KEY_F1;     break;
1389                     case XK_F2:     special = GLUT_KEY_F2;     break;
1390                     case XK_F3:     special = GLUT_KEY_F3;     break;
1391                     case XK_F4:     special = GLUT_KEY_F4;     break;
1392                     case XK_F5:     special = GLUT_KEY_F5;     break;
1393                     case XK_F6:     special = GLUT_KEY_F6;     break;
1394                     case XK_F7:     special = GLUT_KEY_F7;     break;
1395                     case XK_F8:     special = GLUT_KEY_F8;     break;
1396                     case XK_F9:     special = GLUT_KEY_F9;     break;
1397                     case XK_F10:    special = GLUT_KEY_F10;    break;
1398                     case XK_F11:    special = GLUT_KEY_F11;    break;
1399                     case XK_F12:    special = GLUT_KEY_F12;    break;
1400
1401                     case XK_KP_Left:
1402                     case XK_Left:   special = GLUT_KEY_LEFT;   break;
1403                     case XK_KP_Right:
1404                     case XK_Right:  special = GLUT_KEY_RIGHT;  break;
1405                     case XK_KP_Up:
1406                     case XK_Up:     special = GLUT_KEY_UP;     break;
1407                     case XK_KP_Down:
1408                     case XK_Down:   special = GLUT_KEY_DOWN;   break;
1409
1410                     case XK_KP_Prior:
1411                     case XK_Prior:  special = GLUT_KEY_PAGE_UP; break;
1412                     case XK_KP_Next:
1413                     case XK_Next:   special = GLUT_KEY_PAGE_DOWN; break;
1414                     case XK_KP_Home:
1415                     case XK_Home:   special = GLUT_KEY_HOME;   break;
1416                     case XK_KP_End:
1417                     case XK_End:    special = GLUT_KEY_END;    break;
1418                     case XK_KP_Insert:
1419                     case XK_Insert: special = GLUT_KEY_INSERT; break;
1420
1421                     case XK_Num_Lock :  special = GLUT_KEY_NUM_LOCK;  break;
1422                     case XK_KP_Begin :  special = GLUT_KEY_BEGIN;     break;
1423                     case XK_KP_Delete:  special = GLUT_KEY_DELETE;    break;
1424
1425                     case XK_Shift_L:   special = GLUT_KEY_SHIFT_L;    break;
1426                     case XK_Shift_R:   special = GLUT_KEY_SHIFT_R;    break;
1427                     case XK_Control_L: special = GLUT_KEY_CTRL_L;     break;
1428                     case XK_Control_R: special = GLUT_KEY_CTRL_R;     break;
1429                     case XK_Alt_L:     special = GLUT_KEY_ALT_L;      break;
1430                     case XK_Alt_R:     special = GLUT_KEY_ALT_R;      break;
1431                     }
1432
1433                     /*
1434                      * Execute the callback (if one has been specified),
1435                      * given that the special code seems to be valid...
1436                      */
1437                     if( special_cb && (special != -1) )
1438                     {
1439                         fgSetWindow( window );
1440                         fgState.Modifiers = fghGetXModifiers( event.xkey.state );
1441                         special_cb( special, event.xkey.x, event.xkey.y );
1442                         fgState.Modifiers = INVALID_MODIFIERS;
1443                     }
1444                 }
1445             }
1446         }
1447         break;
1448
1449         case ReparentNotify:
1450             break; /* XXX Should disable this event */
1451
1452         /* Not handled */
1453         case GravityNotify:
1454             break;
1455
1456         default:
1457             /* enter handling of Extension Events here */
1458             #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
1459                 fgHandleExtensionEvents( &event );
1460             #endif
1461             break;
1462         }
1463     }
1464
1465 #elif TARGET_HOST_MS_WINDOWS
1466
1467     MSG stMsg;
1468
1469     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
1470
1471     while( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
1472     {
1473         if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
1474         {
1475             if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
1476             {
1477                 fgDeinitialize( );
1478                 exit( 0 );
1479             }
1480             else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
1481                 fgState.ExecState = GLUT_EXEC_STATE_STOP;
1482
1483             return;
1484         }
1485
1486         TranslateMessage( &stMsg );
1487         DispatchMessage( &stMsg );
1488     }
1489 #endif
1490
1491     if( fgState.Timers.First )
1492         fghCheckTimers( );
1493     fghCheckJoystickPolls( );
1494     fghDisplayAll( );
1495
1496     fgCloseWindows( );
1497 }
1498
1499 /*
1500  * Enters the freeglut processing loop.
1501  * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
1502  */
1503 void FGAPIENTRY glutMainLoop( void )
1504 {
1505     int action;
1506
1507 #if TARGET_HOST_MS_WINDOWS
1508     SFG_Window *window = (SFG_Window *)fgStructure.Windows.First ;
1509 #endif
1510
1511     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );
1512
1513 #if TARGET_HOST_MS_WINDOWS
1514     /*
1515      * Processing before the main loop:  If there is a window which is open and
1516      * which has a visibility callback, call it.  I know this is an ugly hack,
1517      * but I'm not sure what else to do about it.  Ideally we should leave
1518      * something uninitialized in the create window code and initialize it in
1519      * the main loop, and have that initialization create a "WM_ACTIVATE"
1520      * message.  Then we would put the visibility callback code in the
1521      * "case WM_ACTIVATE" block below.         - John Fay -- 10/24/02
1522      */
1523     while( window )
1524     {
1525         if ( FETCH_WCB( *window, Visibility ) )
1526         {
1527             SFG_Window *current_window = fgStructure.CurrentWindow ;
1528
1529             INVOKE_WCB( *window, Visibility, ( window->State.Visible ) );
1530             fgSetWindow( current_window );
1531         }
1532
1533         window = (SFG_Window *)window->Node.Next ;
1534     }
1535 #endif
1536
1537     fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
1538     while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
1539     {
1540         SFG_Window *window;
1541
1542         glutMainLoopEvent( );
1543         /*
1544          * Step through the list of windows, seeing if there are any
1545          * that are not menus
1546          */
1547         for( window = ( SFG_Window * )fgStructure.Windows.First;
1548              window;
1549              window = ( SFG_Window * )window->Node.Next )
1550             if ( ! ( window->IsMenu ) )
1551                 break;
1552
1553         if( ! window )
1554             fgState.ExecState = GLUT_EXEC_STATE_STOP;
1555         else
1556         {
1557             if( fgState.IdleCallback )
1558             {
1559                 if( fgStructure.CurrentWindow &&
1560                     fgStructure.CurrentWindow->IsMenu )
1561                     /* fail safe */
1562                     fgSetWindow( window );
1563                 fgState.IdleCallback( );
1564             }
1565
1566             fghSleepForEvents( );
1567         }
1568     }
1569
1570     /*
1571      * When this loop terminates, destroy the display, state and structure
1572      * of a freeglut session, so that another glutInit() call can happen
1573      *
1574      * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it.
1575      */
1576     action = fgState.ActionOnWindowClose;
1577     fgDeinitialize( );
1578     if( action == GLUT_ACTION_EXIT )
1579         exit( 0 );
1580 }
1581
1582 /*
1583  * Leaves the freeglut processing loop.
1584  */
1585 void FGAPIENTRY glutLeaveMainLoop( void )
1586 {
1587     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" );
1588     fgState.ExecState = GLUT_EXEC_STATE_STOP ;
1589 }
1590
1591
1592 #if TARGET_HOST_MS_WINDOWS
1593 /*
1594  * Determine a GLUT modifer mask based on MS-WINDOWS system info.
1595  */
1596 static int fghGetWin32Modifiers (void)
1597 {
1598     return
1599         ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
1600             ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1601         ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
1602             ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1603         ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
1604             ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1605 }
1606
1607 /*
1608  * The window procedure for handling Win32 events
1609  */
1610 LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
1611                                LPARAM lParam )
1612 {
1613     static unsigned char lControl = 0, rControl = 0, lShift = 0,
1614                          rShift = 0, lAlt = 0, rAlt = 0;
1615
1616     SFG_Window* window;
1617     PAINTSTRUCT ps;
1618     LRESULT lRet = 1;
1619
1620     FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Event Handler" ) ;
1621
1622     window = fgWindowByHandle( hWnd );
1623
1624     if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
1625       return DefWindowProc( hWnd, uMsg, wParam, lParam );
1626
1627     /* printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0,
1628              uMsg, wParam, lParam ); */
1629
1630     if ( window )
1631     {
1632       /* Checking for CTRL, ALT, and SHIFT key positions:  Key Down! */
1633       if ( !lControl && GetAsyncKeyState ( VK_LCONTROL ) )
1634       {
1635           INVOKE_WCB    ( *window, Special,
1636                         ( GLUT_KEY_CTRL_L, window->State.MouseX, window->State.MouseY )
1637                       );
1638
1639           lControl = 1;
1640       }
1641
1642       if ( !rControl && GetAsyncKeyState ( VK_RCONTROL ) )
1643       {
1644           INVOKE_WCB ( *window, Special,
1645                        ( GLUT_KEY_CTRL_R, window->State.MouseX, window->State.MouseY )
1646                      );
1647
1648           rControl = 1;
1649       }
1650
1651       if ( !lShift && GetAsyncKeyState ( VK_LSHIFT ) )
1652       {
1653           INVOKE_WCB ( *window, Special,
1654                        ( GLUT_KEY_SHIFT_L, window->State.MouseX, window->State.MouseY )
1655                      );
1656
1657           lShift = 1;
1658       }
1659
1660       if ( !rShift && GetAsyncKeyState ( VK_RSHIFT ) )
1661       {
1662           INVOKE_WCB ( *window, Special,
1663                        ( GLUT_KEY_SHIFT_R, window->State.MouseX, window->State.MouseY )
1664                      );
1665
1666           rShift = 1;
1667       }
1668
1669       if ( !lAlt && GetAsyncKeyState ( VK_LMENU ) )
1670       {
1671           INVOKE_WCB ( *window, Special,
1672                        ( GLUT_KEY_ALT_L, window->State.MouseX, window->State.MouseY )
1673                      );
1674
1675           lAlt = 1;
1676       }
1677
1678       if ( !rAlt && GetAsyncKeyState ( VK_RMENU ) )
1679       {
1680           INVOKE_WCB ( *window, Special,
1681                        ( GLUT_KEY_ALT_R, window->State.MouseX, window->State.MouseY )
1682                      );
1683
1684           rAlt = 1;
1685       }
1686
1687       /* Checking for CTRL, ALT, and SHIFT key positions:  Key Up! */
1688       if ( lControl && !GetAsyncKeyState ( VK_LCONTROL ) )
1689       {
1690           INVOKE_WCB ( *window, SpecialUp,
1691                        ( GLUT_KEY_CTRL_L, window->State.MouseX, window->State.MouseY )
1692                      );
1693
1694           lControl = 0;
1695       }
1696
1697       if ( rControl && !GetAsyncKeyState ( VK_RCONTROL ) )
1698       {
1699           INVOKE_WCB ( *window, SpecialUp,
1700                        ( GLUT_KEY_CTRL_R, window->State.MouseX, window->State.MouseY )
1701                      );
1702
1703           rControl = 0;
1704       }
1705
1706       if ( lShift && !GetAsyncKeyState ( VK_LSHIFT ) )
1707       {
1708           INVOKE_WCB ( *window, SpecialUp,
1709                        ( GLUT_KEY_SHIFT_L, window->State.MouseX, window->State.MouseY )
1710                      );
1711
1712           lShift = 0;
1713       }
1714
1715       if ( rShift && !GetAsyncKeyState ( VK_RSHIFT ) )
1716       {
1717           INVOKE_WCB ( *window, SpecialUp,
1718                        ( GLUT_KEY_SHIFT_R, window->State.MouseX, window->State.MouseY )
1719                      );
1720
1721           rShift = 0;
1722       }
1723
1724       if ( lAlt && !GetAsyncKeyState ( VK_LMENU ) )
1725       {
1726           INVOKE_WCB ( *window, SpecialUp,
1727                        ( GLUT_KEY_ALT_L, window->State.MouseX, window->State.MouseY )
1728                      );
1729
1730           lAlt = 0;
1731       }
1732
1733       if ( rAlt && !GetAsyncKeyState ( VK_RMENU ) )
1734       {
1735           INVOKE_WCB ( *window, SpecialUp,
1736                        ( GLUT_KEY_ALT_R, window->State.MouseX, window->State.MouseY )
1737                      );
1738
1739           rAlt = 0;
1740       }
1741     }
1742
1743     switch( uMsg )
1744     {
1745     case WM_CREATE:
1746         /* The window structure is passed as the creation structure parameter... */
1747         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
1748         FREEGLUT_INTERNAL_ERROR_EXIT ( ( window != NULL ), "Cannot create window",
1749                                        "fgWindowProc" );
1750
1751         window->Window.Handle = hWnd;
1752         window->Window.Device = GetDC( hWnd );
1753         if( window->IsMenu )
1754         {
1755             unsigned int current_DisplayMode = fgState.DisplayMode;
1756             fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH;
1757 #if !defined(_WIN32_WCE)
1758             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
1759 #endif
1760             fgState.DisplayMode = current_DisplayMode;
1761
1762             if( fgStructure.MenuContext )
1763                 wglMakeCurrent( window->Window.Device,
1764                                 fgStructure.MenuContext->MContext
1765                 );
1766             else
1767             {
1768                 fgStructure.MenuContext =
1769                     (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
1770                 fgStructure.MenuContext->MContext =
1771                     wglCreateContext( window->Window.Device );
1772             }
1773
1774             /* window->Window.Context = wglGetCurrentContext ();   */
1775             window->Window.Context = wglCreateContext( window->Window.Device );
1776         }
1777         else
1778         {
1779 #if !defined(_WIN32_WCE)
1780             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
1781 #endif
1782
1783             if( ! fgState.UseCurrentContext )
1784                 window->Window.Context =
1785                     wglCreateContext( window->Window.Device );
1786             else
1787             {
1788                 window->Window.Context = wglGetCurrentContext( );
1789                 if( ! window->Window.Context )
1790                     window->Window.Context =
1791                         wglCreateContext( window->Window.Device );
1792             }
1793
1794 #if !defined(_WIN32_WCE)
1795             fgNewWGLCreateContext( window );
1796 #endif
1797         }
1798
1799         window->State.NeedToResize = GL_TRUE;
1800         /* if we used CW_USEDEFAULT (thats a negative value) for the size
1801          * of the window, query the window now for the size at which it
1802          * was created.
1803          */
1804         if( ( window->State.Width < 0 ) || ( window->State.Height < 0 ) )
1805         {
1806             SFG_Window *current_window = fgStructure.CurrentWindow;
1807
1808             fgSetWindow( window );
1809             window->State.Width = glutGet( GLUT_WINDOW_WIDTH );
1810             window->State.Height = glutGet( GLUT_WINDOW_HEIGHT );
1811             fgSetWindow( current_window );
1812         }
1813
1814         ReleaseDC( window->Window.Handle, window->Window.Device );
1815
1816 #if defined(_WIN32_WCE)
1817         /* Take over button handling */
1818         {
1819             HINSTANCE dxDllLib=LoadLibrary(_T("gx.dll"));
1820             if (dxDllLib)
1821             {
1822                 GXGetDefaultKeys_=(GXGETDEFAULTKEYS)GetProcAddress(dxDllLib, _T("?GXGetDefaultKeys@@YA?AUGXKeyList@@H@Z"));
1823                 GXOpenInput_=(GXOPENINPUT)GetProcAddress(dxDllLib, _T("?GXOpenInput@@YAHXZ"));
1824             }
1825
1826             if(GXOpenInput_)
1827                 (*GXOpenInput_)();
1828             if(GXGetDefaultKeys_)
1829                 gxKeyList = (*GXGetDefaultKeys_)(GX_LANDSCAPEKEYS);
1830         }
1831
1832 #endif /* defined(_WIN32_WCE) */
1833         break;
1834
1835     case WM_SIZE:
1836         /*
1837          * If the window is visible, then it is the user manually resizing it.
1838          * If it is not, then it is the system sending us a dummy resize with
1839          * zero dimensions on a "glutIconifyWindow" call.
1840          */
1841         if( window->State.Visible )
1842         {
1843             window->State.NeedToResize = GL_TRUE;
1844 #if defined(_WIN32_WCE)
1845             window->State.Width  = HIWORD(lParam);
1846             window->State.Height = LOWORD(lParam);
1847 #else
1848             window->State.Width  = LOWORD(lParam);
1849             window->State.Height = HIWORD(lParam);
1850 #endif /* defined(_WIN32_WCE) */
1851         }
1852
1853         break;
1854
1855     case WM_SETFOCUS:
1856 /*        printf("WM_SETFOCUS: %p\n", window ); */
1857         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1858         INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
1859         break;
1860
1861     case WM_KILLFOCUS:
1862 /*        printf("WM_KILLFOCUS: %p\n", window ); */
1863         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1864         INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
1865
1866         if( window->IsMenu &&
1867             window->ActiveMenu && window->ActiveMenu->IsActive )
1868             fgUpdateMenuHighlight( window->ActiveMenu );
1869
1870         break;
1871
1872 #if 0
1873     case WM_ACTIVATE:
1874         if (LOWORD(wParam) != WA_INACTIVE)
1875         {
1876 /*            printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
1877                    window->State.Cursor ); */
1878             fgSetCursor( window, window->State.Cursor );
1879         }
1880
1881         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1882         break;
1883 #endif
1884
1885     case WM_SETCURSOR:
1886 /*      printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
1887         if( LOWORD( lParam ) == HTCLIENT )
1888             fgSetCursor ( window, window->State.Cursor ) ;
1889         else
1890             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1891         break;
1892
1893     case WM_SHOWWINDOW:
1894         window->State.Visible = GL_TRUE;
1895         window->State.Redisplay = GL_TRUE;
1896         break;
1897
1898     case WM_PAINT:
1899         /* Turn on the visibility in case it was turned off somehow */
1900         window->State.Visible = GL_TRUE;
1901         BeginPaint( hWnd, &ps );
1902         fghRedrawWindow( window );
1903         EndPaint( hWnd, &ps );
1904         break;
1905
1906     case WM_CLOSE:
1907         fgDestroyWindow ( window );
1908         if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
1909             PostQuitMessage(0);
1910         break;
1911
1912     case WM_DESTROY:
1913         /*
1914          * The window already got destroyed, so don't bother with it.
1915          */
1916         return 0;
1917
1918     case WM_MOUSEMOVE:
1919     {
1920 #if defined(_WIN32_WCE)
1921         window->State.MouseX = 320-HIWORD( lParam );
1922         window->State.MouseY = LOWORD( lParam );
1923 #else
1924         window->State.MouseX = LOWORD( lParam );
1925         window->State.MouseY = HIWORD( lParam );
1926 #endif /* defined(_WIN32_WCE) */
1927         /* Restrict to [-32768, 32767] to match X11 behaviour       */
1928         /* See comment in "freeglut_developer" mailing list 10/4/04 */
1929         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
1930         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
1931
1932         if ( window->ActiveMenu )
1933         {
1934             fgUpdateMenuHighlight( window->ActiveMenu );
1935             break;
1936         }
1937         SetFocus(window->Window.Handle);
1938
1939         fgState.Modifiers = fghGetWin32Modifiers( );
1940
1941         if( ( wParam & MK_LBUTTON ) ||
1942             ( wParam & MK_MBUTTON ) ||
1943             ( wParam & MK_RBUTTON ) )
1944             INVOKE_WCB( *window, Motion, ( window->State.MouseX,
1945                                            window->State.MouseY ) );
1946         else
1947             INVOKE_WCB( *window, Passive, ( window->State.MouseX,
1948                                             window->State.MouseY ) );
1949
1950         fgState.Modifiers = INVALID_MODIFIERS;
1951     }
1952     break;
1953
1954     case WM_LBUTTONDOWN:
1955     case WM_MBUTTONDOWN:
1956     case WM_RBUTTONDOWN:
1957     case WM_LBUTTONUP:
1958     case WM_MBUTTONUP:
1959     case WM_RBUTTONUP:
1960     {
1961         GLboolean pressed = GL_TRUE;
1962         int button;
1963
1964 #if defined(_WIN32_WCE)
1965         window->State.MouseX = 320-HIWORD( lParam );
1966         window->State.MouseY = LOWORD( lParam );
1967 #else
1968         window->State.MouseX = LOWORD( lParam );
1969         window->State.MouseY = HIWORD( lParam );
1970 #endif /* defined(_WIN32_WCE) */
1971
1972         /* Restrict to [-32768, 32767] to match X11 behaviour       */
1973         /* See comment in "freeglut_developer" mailing list 10/4/04 */
1974         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
1975         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
1976
1977         switch( uMsg )
1978         {
1979         case WM_LBUTTONDOWN:
1980             pressed = GL_TRUE;
1981             button = GLUT_LEFT_BUTTON;
1982             break;
1983         case WM_MBUTTONDOWN:
1984             pressed = GL_TRUE;
1985             button = GLUT_MIDDLE_BUTTON;
1986             break;
1987         case WM_RBUTTONDOWN:
1988             pressed = GL_TRUE;
1989             button = GLUT_RIGHT_BUTTON;
1990             break;
1991         case WM_LBUTTONUP:
1992             pressed = GL_FALSE;
1993             button = GLUT_LEFT_BUTTON;
1994             break;
1995         case WM_MBUTTONUP:
1996             pressed = GL_FALSE;
1997             button = GLUT_MIDDLE_BUTTON;
1998             break;
1999         case WM_RBUTTONUP:
2000             pressed = GL_FALSE;
2001             button = GLUT_RIGHT_BUTTON;
2002             break;
2003         default:
2004             pressed = GL_FALSE;
2005             button = -1;
2006             break;
2007         }
2008
2009 #if !defined(_WIN32_WCE)
2010         if( GetSystemMetrics( SM_SWAPBUTTON ) )
2011         {
2012             if( button == GLUT_LEFT_BUTTON )
2013                 button = GLUT_RIGHT_BUTTON;
2014             else
2015                 if( button == GLUT_RIGHT_BUTTON )
2016                     button = GLUT_LEFT_BUTTON;
2017         }
2018 #endif /* !defined(_WIN32_WCE) */
2019
2020         if( button == -1 )
2021             return DefWindowProc( hWnd, uMsg, lParam, wParam );
2022
2023         /*
2024          * Do not execute the application's mouse callback if a menu
2025          * is hooked to this button.  In that case an appropriate
2026          * private call should be generated.
2027          */
2028         if( fgCheckActiveMenu( window, button, pressed,
2029                                window->State.MouseX, window->State.MouseY ) )
2030             break;
2031
2032         /* Set capture so that the window captures all the mouse messages */
2033         /*
2034          * XXX - Multiple button support:  Under X11, the mouse is not released
2035          * XXX - from the window until all buttons have been released, even if the
2036          * XXX - user presses a button in another window.  This will take more
2037          * XXX - code changes than I am up to at the moment (10/5/04).  The present
2038          * XXX - is a 90 percent solution.
2039          */
2040         if ( pressed == GL_TRUE )
2041           SetCapture ( window->Window.Handle ) ;
2042         else
2043           ReleaseCapture () ;
2044
2045         if( ! FETCH_WCB( *window, Mouse ) )
2046             break;
2047
2048         fgSetWindow( window );
2049         fgState.Modifiers = fghGetWin32Modifiers( );
2050
2051         INVOKE_WCB(
2052             *window, Mouse,
2053             ( button,
2054               pressed ? GLUT_DOWN : GLUT_UP,
2055               window->State.MouseX,
2056               window->State.MouseY
2057             )
2058         );
2059
2060         fgState.Modifiers = INVALID_MODIFIERS;
2061     }
2062     break;
2063
2064     case 0x020a:
2065         /* Should be WM_MOUSEWHEEL but my compiler doesn't recognize it */
2066     {
2067         int wheel_number = LOWORD( wParam );
2068         short ticks = ( short )HIWORD( wParam );
2069                 fgState.MouseWheelTicks += ticks;
2070
2071         /*
2072          * XXX Should use WHEEL_DELTA instead of 120
2073          */
2074                 if ( abs ( fgState.MouseWheelTicks ) > 120 )
2075                 {
2076                         int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1;
2077
2078             if( ! FETCH_WCB( *window, MouseWheel ) &&
2079                 ! FETCH_WCB( *window, Mouse ) )
2080                 break;
2081
2082             fgSetWindow( window );
2083             fgState.Modifiers = fghGetWin32Modifiers( );
2084
2085             /*
2086              * XXX Should use WHEEL_DELTA instead of 120
2087              */
2088             while( abs ( fgState.MouseWheelTicks ) > 120 )
2089                         {
2090                 if( FETCH_WCB( *window, MouseWheel ) )
2091                     INVOKE_WCB( *window, MouseWheel,
2092                                 ( wheel_number,
2093                                   direction,
2094                                   window->State.MouseX,
2095                                   window->State.MouseY
2096                                 )
2097                     );
2098                 else  /* No mouse wheel, call the mouse button callback twice */
2099                                 {
2100                     /*
2101                      * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
2102                      *  "    "   one                     +1 to 5, -1 to 6, ...
2103                      *
2104                      * XXX The below assumes that you have no more than 3 mouse
2105                      * XXX buttons.  Sorry.
2106                      */
2107                     int button = wheel_number * 2 + 3;
2108                     if( direction < 0 )
2109                         ++button;
2110                     INVOKE_WCB( *window, Mouse,
2111                                 ( button, GLUT_DOWN,
2112                                   window->State.MouseX, window->State.MouseY )
2113                     );
2114                     INVOKE_WCB( *window, Mouse,
2115                                 ( button, GLUT_UP,
2116                                   window->State.MouseX, window->State.MouseY )
2117                     );
2118                                 }
2119
2120                 /*
2121                  * XXX Should use WHEEL_DELTA instead of 120
2122                  */
2123                                 fgState.MouseWheelTicks -= 120 * direction;
2124                         }
2125
2126             fgState.Modifiers = INVALID_MODIFIERS;
2127                 }
2128     }
2129     break ;
2130
2131     case WM_SYSKEYDOWN:
2132     case WM_KEYDOWN:
2133     {
2134         int keypress = -1;
2135         POINT mouse_pos ;
2136
2137         if( ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) && (HIWORD(lParam) & KF_REPEAT) )
2138             break;
2139
2140         /*
2141          * Remember the current modifiers state. This is done here in order
2142          * to make sure the VK_DELETE keyboard callback is executed properly.
2143          */
2144         fgState.Modifiers = fghGetWin32Modifiers( );
2145
2146         GetCursorPos( &mouse_pos );
2147         ScreenToClient( window->Window.Handle, &mouse_pos );
2148
2149         window->State.MouseX = mouse_pos.x;
2150         window->State.MouseY = mouse_pos.y;
2151
2152         /* Convert the Win32 keystroke codes to GLUTtish way */
2153 #       define KEY(a,b) case a: keypress = b; break;
2154
2155         switch( wParam )
2156         {
2157             KEY( VK_F1,     GLUT_KEY_F1        );
2158             KEY( VK_F2,     GLUT_KEY_F2        );
2159             KEY( VK_F3,     GLUT_KEY_F3        );
2160             KEY( VK_F4,     GLUT_KEY_F4        );
2161             KEY( VK_F5,     GLUT_KEY_F5        );
2162             KEY( VK_F6,     GLUT_KEY_F6        );
2163             KEY( VK_F7,     GLUT_KEY_F7        );
2164             KEY( VK_F8,     GLUT_KEY_F8        );
2165             KEY( VK_F9,     GLUT_KEY_F9        );
2166             KEY( VK_F10,    GLUT_KEY_F10       );
2167             KEY( VK_F11,    GLUT_KEY_F11       );
2168             KEY( VK_F12,    GLUT_KEY_F12       );
2169             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
2170             KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
2171             KEY( VK_HOME,   GLUT_KEY_HOME      );
2172             KEY( VK_END,    GLUT_KEY_END       );
2173             KEY( VK_LEFT,   GLUT_KEY_LEFT      );
2174             KEY( VK_UP,     GLUT_KEY_UP        );
2175             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
2176             KEY( VK_DOWN,   GLUT_KEY_DOWN      );
2177             KEY( VK_INSERT, GLUT_KEY_INSERT    );
2178             KEY( VK_LCONTROL, GLUT_KEY_CTRL_L  );
2179             KEY( VK_RCONTROL, GLUT_KEY_CTRL_R  );
2180             KEY( VK_LSHIFT, GLUT_KEY_SHIFT_L   );
2181             KEY( VK_RSHIFT, GLUT_KEY_SHIFT_R   );
2182             KEY( VK_LMENU,  GLUT_KEY_ALT_L     );
2183             KEY( VK_RMENU,  GLUT_KEY_ALT_R     );
2184
2185         case VK_DELETE:
2186             /* The delete key should be treated as an ASCII keypress: */
2187             INVOKE_WCB( *window, Keyboard,
2188                         ( 127, window->State.MouseX, window->State.MouseY )
2189             );
2190         }
2191
2192 #if defined(_WIN32_WCE)
2193         if(!(lParam & 0x40000000)) /* Prevent auto-repeat */
2194         {
2195             if(wParam==(unsigned)gxKeyList.vkRight)
2196                 keypress = GLUT_KEY_RIGHT;
2197             else if(wParam==(unsigned)gxKeyList.vkLeft)
2198                 keypress = GLUT_KEY_LEFT;
2199             else if(wParam==(unsigned)gxKeyList.vkUp)
2200                 keypress = GLUT_KEY_UP;
2201             else if(wParam==(unsigned)gxKeyList.vkDown)
2202                 keypress = GLUT_KEY_DOWN;
2203             else if(wParam==(unsigned)gxKeyList.vkA)
2204                 keypress = GLUT_KEY_F1;
2205             else if(wParam==(unsigned)gxKeyList.vkB)
2206                 keypress = GLUT_KEY_F2;
2207             else if(wParam==(unsigned)gxKeyList.vkC)
2208                 keypress = GLUT_KEY_F3;
2209             else if(wParam==(unsigned)gxKeyList.vkStart)
2210                 keypress = GLUT_KEY_F4;
2211         }
2212 #endif
2213
2214         if( keypress != -1 )
2215             INVOKE_WCB( *window, Special,
2216                         ( keypress,
2217                           window->State.MouseX, window->State.MouseY )
2218             );
2219
2220         fgState.Modifiers = INVALID_MODIFIERS;
2221     }
2222     break;
2223
2224     case WM_SYSKEYUP:
2225     case WM_KEYUP:
2226     {
2227         int keypress = -1;
2228         POINT mouse_pos;
2229
2230         /*
2231          * Remember the current modifiers state. This is done here in order
2232          * to make sure the VK_DELETE keyboard callback is executed properly.
2233          */
2234         fgState.Modifiers = fghGetWin32Modifiers( );
2235
2236         GetCursorPos( &mouse_pos );
2237         ScreenToClient( window->Window.Handle, &mouse_pos );
2238
2239         window->State.MouseX = mouse_pos.x;
2240         window->State.MouseY = mouse_pos.y;
2241
2242         /*
2243          * Convert the Win32 keystroke codes to GLUTtish way.
2244          * "KEY(a,b)" was defined under "WM_KEYDOWN"
2245          */
2246
2247         switch( wParam )
2248         {
2249             KEY( VK_F1,     GLUT_KEY_F1        );
2250             KEY( VK_F2,     GLUT_KEY_F2        );
2251             KEY( VK_F3,     GLUT_KEY_F3        );
2252             KEY( VK_F4,     GLUT_KEY_F4        );
2253             KEY( VK_F5,     GLUT_KEY_F5        );
2254             KEY( VK_F6,     GLUT_KEY_F6        );
2255             KEY( VK_F7,     GLUT_KEY_F7        );
2256             KEY( VK_F8,     GLUT_KEY_F8        );
2257             KEY( VK_F9,     GLUT_KEY_F9        );
2258             KEY( VK_F10,    GLUT_KEY_F10       );
2259             KEY( VK_F11,    GLUT_KEY_F11       );
2260             KEY( VK_F12,    GLUT_KEY_F12       );
2261             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
2262             KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
2263             KEY( VK_HOME,   GLUT_KEY_HOME      );
2264             KEY( VK_END,    GLUT_KEY_END       );
2265             KEY( VK_LEFT,   GLUT_KEY_LEFT      );
2266             KEY( VK_UP,     GLUT_KEY_UP        );
2267             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
2268             KEY( VK_DOWN,   GLUT_KEY_DOWN      );
2269             KEY( VK_INSERT, GLUT_KEY_INSERT    );
2270             KEY( VK_LCONTROL, GLUT_KEY_CTRL_L  );
2271             KEY( VK_RCONTROL, GLUT_KEY_CTRL_R  );
2272             KEY( VK_LSHIFT, GLUT_KEY_SHIFT_L   );
2273             KEY( VK_RSHIFT, GLUT_KEY_SHIFT_R   );
2274             KEY( VK_LMENU,  GLUT_KEY_ALT_L     );
2275             KEY( VK_RMENU,  GLUT_KEY_ALT_R     );
2276
2277           case VK_DELETE:
2278               /* The delete key should be treated as an ASCII keypress: */
2279               INVOKE_WCB( *window, KeyboardUp,
2280                           ( 127, window->State.MouseX, window->State.MouseY )
2281               );
2282               break;
2283
2284         default:
2285         {
2286 #if !defined(_WIN32_WCE)
2287             BYTE state[ 256 ];
2288             WORD code[ 2 ];
2289
2290             GetKeyboardState( state );
2291
2292             if( ToAscii( (UINT)wParam, 0, state, code, 0 ) == 1 )
2293                 wParam=code[ 0 ];
2294
2295             INVOKE_WCB( *window, KeyboardUp,
2296                         ( (char)wParam,
2297                           window->State.MouseX, window->State.MouseY )
2298             );
2299 #endif /* !defined(_WIN32_WCE) */
2300         }
2301         }
2302
2303         if( keypress != -1 )
2304             INVOKE_WCB( *window, SpecialUp,
2305                         ( keypress,
2306                           window->State.MouseX, window->State.MouseY )
2307             );
2308
2309         fgState.Modifiers = INVALID_MODIFIERS;
2310     }
2311     break;
2312
2313     case WM_SYSCHAR:
2314     case WM_CHAR:
2315     {
2316       if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
2317             break;
2318
2319         fgState.Modifiers = fghGetWin32Modifiers( );
2320         INVOKE_WCB( *window, Keyboard,
2321                     ( (char)wParam,
2322                       window->State.MouseX, window->State.MouseY )
2323         );
2324         fgState.Modifiers = INVALID_MODIFIERS;
2325     }
2326     break;
2327
2328     case WM_CAPTURECHANGED:
2329         /* User has finished resizing the window, force a redraw */
2330         INVOKE_WCB( *window, Display, ( ) );
2331
2332         /*lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); */
2333         break;
2334
2335         /* Other messages that I have seen and which are not handled already */
2336     case WM_SETTEXT:  /* 0x000c */
2337         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2338         /* Pass it on to "DefWindowProc" to set the window text */
2339         break;
2340
2341     case WM_GETTEXT:  /* 0x000d */
2342         /* Ideally we would copy the title of the window into "lParam" */
2343         /* strncpy ( (char *)lParam, "Window Title", wParam );
2344            lRet = ( wParam > 12 ) ? 12 : wParam;  */
2345         /* the number of characters copied */
2346         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2347         break;
2348
2349     case WM_GETTEXTLENGTH:  /* 0x000e */
2350         /* Ideally we would get the length of the title of the window */
2351         lRet = 12;
2352         /* the number of characters in "Window Title\0" (see above) */
2353         break;
2354
2355     case WM_ERASEBKGND:  /* 0x0014 */
2356         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2357         break;
2358
2359 #if !defined(_WIN32_WCE)
2360     case WM_SYNCPAINT:  /* 0x0088 */
2361         /* Another window has moved, need to update this one */
2362         window->State.Redisplay = GL_TRUE;
2363         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2364         /* Help screen says this message must be passed to "DefWindowProc" */
2365         break;
2366
2367     case WM_NCPAINT:  /* 0x0085 */
2368       /* Need to update the border of this window */
2369         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2370         /* Pass it on to "DefWindowProc" to repaint a standard border */
2371         break;
2372
2373     case WM_SYSCOMMAND :  /* 0x0112 */
2374         {
2375           /*
2376            * We have received a system command message.  Try to act on it.
2377            * The commands are passed in through the "wParam" parameter:
2378            * The least significant digit seems to be which edge of the window
2379            * is being used for a resize event:
2380            *     4  3  5
2381            *     1     2
2382            *     7  6  8
2383            * Congratulations and thanks to Richard Rauch for figuring this out..
2384            */
2385             switch ( wParam & 0xfff0 )
2386             {
2387             case SC_SIZE       :
2388                 break ;
2389
2390             case SC_MOVE       :
2391                 break ;
2392
2393             case SC_MINIMIZE   :
2394                 /* User has clicked on the "-" to minimize the window */
2395                 /* Turn off the visibility */
2396                 window->State.Visible = GL_FALSE ;
2397
2398                 break ;
2399
2400             case SC_MAXIMIZE   :
2401                 break ;
2402
2403             case SC_NEXTWINDOW :
2404                 break ;
2405
2406             case SC_PREVWINDOW :
2407                 break ;
2408
2409             case SC_CLOSE      :
2410                 /* Followed very closely by a WM_CLOSE message */
2411                 break ;
2412
2413             case SC_VSCROLL    :
2414                 break ;
2415
2416             case SC_HSCROLL    :
2417                 break ;
2418
2419             case SC_MOUSEMENU  :
2420                 break ;
2421
2422             case SC_KEYMENU    :
2423                 break ;
2424
2425             case SC_ARRANGE    :
2426                 break ;
2427
2428             case SC_RESTORE    :
2429                 break ;
2430
2431             case SC_TASKLIST   :
2432                 break ;
2433
2434             case SC_SCREENSAVE :
2435                 break ;
2436
2437             case SC_HOTKEY     :
2438                 break ;
2439
2440 #if(WINVER >= 0x0400)
2441             case SC_DEFAULT    :
2442                 break ;
2443
2444             case SC_MONITORPOWER    :
2445                 break ;
2446
2447             case SC_CONTEXTHELP    :
2448                 break ;
2449 #endif /* WINVER >= 0x0400 */
2450
2451             default:
2452 #if _DEBUG
2453                 fgWarning( "Unknown wParam type 0x%x", wParam );
2454 #endif
2455                 break;
2456             }
2457         }
2458 #endif /* !defined(_WIN32_WCE) */
2459
2460         /* We need to pass the message on to the operating system as well */
2461         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2462         break;
2463
2464 #ifdef WM_TOUCH
2465         /* handle multi-touch messages */
2466         case WM_TOUCH:
2467         {
2468                 unsigned int numInputs = (unsigned int)wParam;
2469                 unsigned int i = 0;
2470                 TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
2471
2472                 if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) {
2473                     fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo");
2474                     fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle");
2475                 }
2476
2477                 if (!fghGetTouchInputInfo) { 
2478                         free( (void*)ti );
2479                         break;
2480                 }
2481
2482                 if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
2483                         /* Handle each contact point */
2484                         for (i = 0; i < numInputs; ++i ) {
2485
2486                                 POINT tp;
2487                                 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
2488                                 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
2489                                 ScreenToClient( hWnd, &tp );
2490
2491                                 ti[i].dwID = ti[i].dwID * 2;
2492
2493                                 if (ti[i].dwFlags & TOUCHEVENTF_DOWN) {
2494                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_ENTERED ) );
2495                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) );
2496                                 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
2497                                         INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) );
2498                                 } else if (ti[i].dwFlags & TOUCHEVENTF_UP)   { 
2499                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) );
2500                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_LEFT ) );
2501                                 }
2502                         }
2503                 }
2504                 fghCloseTouchInputHandle((HTOUCHINPUT)lParam);
2505                 free( (void*)ti );
2506                 lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
2507                 break;
2508         }
2509 #endif
2510     default:
2511         /* Handle unhandled messages */
2512         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2513         break;
2514     }
2515
2516     return lRet;
2517 }
2518 #endif
2519
2520 /*** END OF FILE ***/