Adding "MouseWheelTicks" to the "fgState" structure as a first step towards supportin...
[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 (*pGetTouchInputInfo)(HTOUCHINPUT,UINT,PTOUCHINPUT,int);
75     typedef BOOL (*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
105 #if TARGET_HOST_POSIX_X11
106
107     XResizeWindow( fgDisplay.Display, window->Window.Handle,
108                    width, height );
109     XFlush( fgDisplay.Display ); /* XXX Shouldn't need this */
110
111 #elif TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
112     {
113         RECT winRect;
114         int x, y, w, h;
115
116         /*
117          * For windowed mode, get the current position of the
118          * window and resize taking the size of the frame
119          * decorations into account.
120          */
121
122         /* "GetWindowRect" returns the pixel coordinates of the outside of the window */
123         GetWindowRect( window->Window.Handle, &winRect );
124         x = winRect.left;
125         y = winRect.top;
126         w = width;
127         h = height;
128
129         if ( window->Parent == NULL )
130         {
131            if ( ! window->IsMenu && (window != fgStructure.GameModeWindow) &&
132                !( fgState.DisplayMode & GLUT_BORDERLESS ))
133             {
134                 w += GetSystemMetrics( SM_CXSIZEFRAME ) * 2;
135                 h += GetSystemMetrics( SM_CYSIZEFRAME ) * 2 +
136                      GetSystemMetrics( SM_CYCAPTION );
137             }
138         }
139         else
140         {
141             RECT parentRect;
142             GetWindowRect( window->Parent->Window.Handle, &parentRect );
143             x -= parentRect.left + GetSystemMetrics( SM_CXSIZEFRAME ) * 2;
144             y -= parentRect.top  + GetSystemMetrics( SM_CYSIZEFRAME ) * 2 +
145                                    GetSystemMetrics( SM_CYCAPTION );
146         }
147
148         /*
149          * SWP_NOACTIVATE      Do not activate the window
150          * SWP_NOOWNERZORDER   Do not change position in z-order
151          * SWP_NOSENDCHANGING  Supress WM_WINDOWPOSCHANGING message
152          * SWP_NOZORDER        Retains the current Z order (ignore 2nd param)
153          */
154
155         SetWindowPos( window->Window.Handle,
156                       HWND_TOP,
157                       x, y, w, h,
158                       SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING |
159                       SWP_NOZORDER
160         );
161     }
162 #endif
163
164     /*
165      * XXX Should update {window->State.OldWidth, window->State.OldHeight}
166      * XXX to keep in lockstep with POSIX_X11 code.
167      */
168     if( FETCH_WCB( *window, Reshape ) )
169         INVOKE_WCB( *window, Reshape, ( width, height ) );
170     else
171     {
172         fgSetWindow( window );
173         glViewport( 0, 0, width, height );
174     }
175
176     /*
177      * Force a window redraw.  In Windows at least this is only a partial
178      * solution:  if the window is increasing in size in either dimension,
179      * the already-drawn part does not get drawn again and things look funny.
180      * But without this we get this bad behaviour whenever we resize the
181      * window.
182      */
183     window->State.Redisplay = GL_TRUE;
184
185     if( window->IsMenu )
186         fgSetWindow( current_window );
187 }
188
189 /*
190  * Calls a window's redraw method. This is used when
191  * a redraw is forced by the incoming window messages.
192  */
193 static void fghRedrawWindow ( SFG_Window *window )
194 {
195     SFG_Window *current_window = fgStructure.CurrentWindow;
196
197     freeglut_return_if_fail( window );
198     freeglut_return_if_fail( FETCH_WCB ( *window, Display ) );
199
200     window->State.Redisplay = GL_FALSE;
201
202     freeglut_return_if_fail( window->State.Visible );
203
204     fgSetWindow( window );
205
206     if( window->State.NeedToResize )
207     {
208         fghReshapeWindow(
209             window,
210             window->State.Width,
211             window->State.Height
212         );
213
214         window->State.NeedToResize = GL_FALSE;
215     }
216
217     INVOKE_WCB( *window, Display, ( ) );
218
219     fgSetWindow( current_window );
220 }
221
222 /*
223  * A static helper function to execute display callback for a window
224  */
225 static void fghcbDisplayWindow( SFG_Window *window,
226                                 SFG_Enumerator *enumerator )
227 {
228     if( window->State.Redisplay &&
229         window->State.Visible )
230     {
231         window->State.Redisplay = GL_FALSE;
232
233 #if TARGET_HOST_POSIX_X11
234         fghRedrawWindow ( window ) ;
235 #elif TARGET_HOST_MS_WINDOWS
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 decremeted
1277                  * XXX in mapping from X button numbering to GLUT.
1278                  */
1279                 int wheel_number = (button - glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS )) / 2;
1280                 int direction = -1;
1281                 if( button % 2 )
1282                     direction = 1;
1283
1284                 if( pressed )
1285                     INVOKE_WCB( *window, MouseWheel, ( wheel_number,
1286                                                        direction,
1287                                                        event.xbutton.x,
1288                                                        event.xbutton.y )
1289                     );
1290             }
1291             fgState.Modifiers = INVALID_MODIFIERS;
1292         }
1293         break;
1294
1295         case KeyRelease:
1296         case KeyPress:
1297         {
1298             FGCBKeyboard keyboard_cb;
1299             FGCBSpecial special_cb;
1300
1301             GETWINDOW( xkey );
1302             GETMOUSE( xkey );
1303
1304             /* Detect auto repeated keys, if configured globally or per-window */
1305
1306             if ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE )
1307             {
1308                 if (event.type==KeyRelease)
1309                 {
1310                     /*
1311                      * Look at X11 keystate to detect repeat mode.
1312                      * While X11 says the key is actually held down, we'll ignore KeyRelease/KeyPress pairs.
1313                      */
1314
1315                     char keys[32];
1316                     XQueryKeymap( fgDisplay.Display, keys ); /* Look at X11 keystate to detect repeat mode */
1317
1318                     if ( event.xkey.keycode<256 )            /* XQueryKeymap is limited to 256 keycodes    */
1319                     {
1320                         if ( keys[event.xkey.keycode>>3] & (1<<(event.xkey.keycode%8)) )
1321                             window->State.KeyRepeating = GL_TRUE;
1322                         else
1323                             window->State.KeyRepeating = GL_FALSE;
1324                     }
1325                 }
1326             }
1327             else
1328                 window->State.KeyRepeating = GL_FALSE;
1329
1330             /* Cease processing this event if it is auto repeated */
1331
1332             if (window->State.KeyRepeating)
1333             {
1334                 if (event.type == KeyPress) window->State.KeyRepeating = GL_FALSE;
1335                 break;
1336             }
1337
1338             if( event.type == KeyPress )
1339             {
1340                 keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, Keyboard ));
1341                 special_cb  = (FGCBSpecial) ( FETCH_WCB( *window, Special  ));
1342             }
1343             else
1344             {
1345                 keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, KeyboardUp ));
1346                 special_cb  = (FGCBSpecial) ( FETCH_WCB( *window, SpecialUp  ));
1347             }
1348
1349             /* Is there a keyboard/special callback hooked for this window? */
1350             if( keyboard_cb || special_cb )
1351             {
1352                 XComposeStatus composeStatus;
1353                 char asciiCode[ 32 ];
1354                 KeySym keySym;
1355                 int len;
1356
1357                 /* Check for the ASCII/KeySym codes associated with the event: */
1358                 len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode),
1359                                      &keySym, &composeStatus
1360                 );
1361
1362                 /* GLUT API tells us to have two separate callbacks... */
1363                 if( len > 0 )
1364                 {
1365                     /* ...one for the ASCII translateable keypresses... */
1366                     if( keyboard_cb )
1367                     {
1368                         fgSetWindow( window );
1369                         fgState.Modifiers = fghGetXModifiers( event.xkey.state );
1370                         keyboard_cb( asciiCode[ 0 ],
1371                                      event.xkey.x, event.xkey.y
1372                         );
1373                         fgState.Modifiers = INVALID_MODIFIERS;
1374                     }
1375                 }
1376                 else
1377                 {
1378                     int special = -1;
1379
1380                     /*
1381                      * ...and one for all the others, which need to be
1382                      * translated to GLUT_KEY_Xs...
1383                      */
1384                     switch( keySym )
1385                     {
1386                     case XK_F1:     special = GLUT_KEY_F1;     break;
1387                     case XK_F2:     special = GLUT_KEY_F2;     break;
1388                     case XK_F3:     special = GLUT_KEY_F3;     break;
1389                     case XK_F4:     special = GLUT_KEY_F4;     break;
1390                     case XK_F5:     special = GLUT_KEY_F5;     break;
1391                     case XK_F6:     special = GLUT_KEY_F6;     break;
1392                     case XK_F7:     special = GLUT_KEY_F7;     break;
1393                     case XK_F8:     special = GLUT_KEY_F8;     break;
1394                     case XK_F9:     special = GLUT_KEY_F9;     break;
1395                     case XK_F10:    special = GLUT_KEY_F10;    break;
1396                     case XK_F11:    special = GLUT_KEY_F11;    break;
1397                     case XK_F12:    special = GLUT_KEY_F12;    break;
1398
1399                     case XK_KP_Left:
1400                     case XK_Left:   special = GLUT_KEY_LEFT;   break;
1401                     case XK_KP_Right:
1402                     case XK_Right:  special = GLUT_KEY_RIGHT;  break;
1403                     case XK_KP_Up:
1404                     case XK_Up:     special = GLUT_KEY_UP;     break;
1405                     case XK_KP_Down:
1406                     case XK_Down:   special = GLUT_KEY_DOWN;   break;
1407
1408                     case XK_KP_Prior:
1409                     case XK_Prior:  special = GLUT_KEY_PAGE_UP; break;
1410                     case XK_KP_Next:
1411                     case XK_Next:   special = GLUT_KEY_PAGE_DOWN; break;
1412                     case XK_KP_Home:
1413                     case XK_Home:   special = GLUT_KEY_HOME;   break;
1414                     case XK_KP_End:
1415                     case XK_End:    special = GLUT_KEY_END;    break;
1416                     case XK_KP_Insert:
1417                     case XK_Insert: special = GLUT_KEY_INSERT; break;
1418
1419                     case XK_Num_Lock :  special = GLUT_KEY_NUM_LOCK;  break;
1420                     case XK_KP_Begin :  special = GLUT_KEY_BEGIN;     break;
1421                     case XK_KP_Delete:  special = GLUT_KEY_DELETE;    break;
1422
1423                     case XK_Shift_L:   special = GLUT_KEY_SHIFT_L;    break;
1424                     case XK_Shift_R:   special = GLUT_KEY_SHIFT_R;    break;
1425                     case XK_Control_L: special = GLUT_KEY_CTRL_L;     break;
1426                     case XK_Control_R: special = GLUT_KEY_CTRL_R;     break;
1427                     case XK_Alt_L:     special = GLUT_KEY_ALT_L;      break;
1428                     case XK_Alt_R:     special = GLUT_KEY_ALT_R;      break;
1429                     }
1430
1431                     /*
1432                      * Execute the callback (if one has been specified),
1433                      * given that the special code seems to be valid...
1434                      */
1435                     if( special_cb && (special != -1) )
1436                     {
1437                         fgSetWindow( window );
1438                         fgState.Modifiers = fghGetXModifiers( event.xkey.state );
1439                         special_cb( special, event.xkey.x, event.xkey.y );
1440                         fgState.Modifiers = INVALID_MODIFIERS;
1441                     }
1442                 }
1443             }
1444         }
1445         break;
1446
1447         case ReparentNotify:
1448             break; /* XXX Should disable this event */
1449
1450         /* Not handled */
1451         case GravityNotify:
1452             break;
1453
1454         default:
1455             /* enter handling of Extension Events here */
1456             #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
1457                 fgHandleExtensionEvents( &event );
1458             #endif
1459             break;
1460         }
1461     }
1462
1463 #elif TARGET_HOST_MS_WINDOWS
1464
1465     MSG stMsg;
1466
1467     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
1468
1469     while( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
1470     {
1471         if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
1472         {
1473             if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
1474             {
1475                 fgDeinitialize( );
1476                 exit( 0 );
1477             }
1478             else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
1479                 fgState.ExecState = GLUT_EXEC_STATE_STOP;
1480
1481             return;
1482         }
1483
1484         TranslateMessage( &stMsg );
1485         DispatchMessage( &stMsg );
1486     }
1487 #endif
1488
1489     if( fgState.Timers.First )
1490         fghCheckTimers( );
1491     fghCheckJoystickPolls( );
1492     fghDisplayAll( );
1493
1494     fgCloseWindows( );
1495 }
1496
1497 /*
1498  * Enters the freeglut processing loop.
1499  * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP".
1500  */
1501 void FGAPIENTRY glutMainLoop( void )
1502 {
1503     int action;
1504
1505 #if TARGET_HOST_MS_WINDOWS
1506     SFG_Window *window = (SFG_Window *)fgStructure.Windows.First ;
1507 #endif
1508
1509     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );
1510
1511 #if TARGET_HOST_MS_WINDOWS
1512     /*
1513      * Processing before the main loop:  If there is a window which is open and
1514      * which has a visibility callback, call it.  I know this is an ugly hack,
1515      * but I'm not sure what else to do about it.  Ideally we should leave
1516      * something uninitialized in the create window code and initialize it in
1517      * the main loop, and have that initialization create a "WM_ACTIVATE"
1518      * message.  Then we would put the visibility callback code in the
1519      * "case WM_ACTIVATE" block below.         - John Fay -- 10/24/02
1520      */
1521     while( window )
1522     {
1523         if ( FETCH_WCB( *window, Visibility ) )
1524         {
1525             SFG_Window *current_window = fgStructure.CurrentWindow ;
1526
1527             INVOKE_WCB( *window, Visibility, ( window->State.Visible ) );
1528             fgSetWindow( current_window );
1529         }
1530
1531         window = (SFG_Window *)window->Node.Next ;
1532     }
1533 #endif
1534
1535     fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
1536     while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
1537     {
1538         SFG_Window *window;
1539
1540         glutMainLoopEvent( );
1541         /*
1542          * Step through the list of windows, seeing if there are any
1543          * that are not menus
1544          */
1545         for( window = ( SFG_Window * )fgStructure.Windows.First;
1546              window;
1547              window = ( SFG_Window * )window->Node.Next )
1548             if ( ! ( window->IsMenu ) )
1549                 break;
1550
1551         if( ! window )
1552             fgState.ExecState = GLUT_EXEC_STATE_STOP;
1553         else
1554         {
1555             if( fgState.IdleCallback )
1556             {
1557                 if( fgStructure.CurrentWindow &&
1558                     fgStructure.CurrentWindow->IsMenu )
1559                     /* fail safe */
1560                     fgSetWindow( window );
1561                 fgState.IdleCallback( );
1562             }
1563
1564             fghSleepForEvents( );
1565         }
1566     }
1567
1568     /*
1569      * When this loop terminates, destroy the display, state and structure
1570      * of a freeglut session, so that another glutInit() call can happen
1571      *
1572      * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it.
1573      */
1574     action = fgState.ActionOnWindowClose;
1575     fgDeinitialize( );
1576     if( action == GLUT_ACTION_EXIT )
1577         exit( 0 );
1578 }
1579
1580 /*
1581  * Leaves the freeglut processing loop.
1582  */
1583 void FGAPIENTRY glutLeaveMainLoop( void )
1584 {
1585     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" );
1586     fgState.ExecState = GLUT_EXEC_STATE_STOP ;
1587 }
1588
1589
1590 #if TARGET_HOST_MS_WINDOWS
1591 /*
1592  * Determine a GLUT modifer mask based on MS-WINDOWS system info.
1593  */
1594 static int fghGetWin32Modifiers (void)
1595 {
1596     return
1597         ( ( ( GetKeyState( VK_LSHIFT   ) < 0 ) ||
1598             ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
1599         ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
1600             ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
1601         ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
1602             ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
1603 }
1604
1605 /*
1606  * The window procedure for handling Win32 events
1607  */
1608 LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
1609                                LPARAM lParam )
1610 {
1611     static unsigned char lControl = 0, rControl = 0, lShift = 0,
1612                          rShift = 0, lAlt = 0, rAlt = 0;
1613
1614     SFG_Window* window;
1615     PAINTSTRUCT ps;
1616     LRESULT lRet = 1;
1617
1618     FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Event Handler" ) ;
1619
1620     window = fgWindowByHandle( hWnd );
1621
1622     if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
1623       return DefWindowProc( hWnd, uMsg, wParam, lParam );
1624
1625     /* printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0,
1626              uMsg, wParam, lParam ); */
1627
1628     if ( window )
1629     {
1630       /* Checking for CTRL, ALT, and SHIFT key positions:  Key Down! */
1631       if ( !lControl && GetAsyncKeyState ( VK_LCONTROL ) )
1632       {
1633           INVOKE_WCB    ( *window, Special,
1634                         ( GLUT_KEY_CTRL_L, window->State.MouseX, window->State.MouseY )
1635                       );
1636
1637           lControl = 1;
1638       }
1639
1640       if ( !rControl && GetAsyncKeyState ( VK_RCONTROL ) )
1641       {
1642           INVOKE_WCB ( *window, Special,
1643                        ( GLUT_KEY_CTRL_R, window->State.MouseX, window->State.MouseY )
1644                      );
1645
1646           rControl = 1;
1647       }
1648
1649       if ( !lShift && GetAsyncKeyState ( VK_LSHIFT ) )
1650       {
1651           INVOKE_WCB ( *window, Special,
1652                        ( GLUT_KEY_SHIFT_L, window->State.MouseX, window->State.MouseY )
1653                      );
1654
1655           lShift = 1;
1656       }
1657
1658       if ( !rShift && GetAsyncKeyState ( VK_RSHIFT ) )
1659       {
1660           INVOKE_WCB ( *window, Special,
1661                        ( GLUT_KEY_SHIFT_R, window->State.MouseX, window->State.MouseY )
1662                      );
1663
1664           rShift = 1;
1665       }
1666
1667       if ( !lAlt && GetAsyncKeyState ( VK_LMENU ) )
1668       {
1669           INVOKE_WCB ( *window, Special,
1670                        ( GLUT_KEY_ALT_L, window->State.MouseX, window->State.MouseY )
1671                      );
1672
1673           lAlt = 1;
1674       }
1675
1676       if ( !rAlt && GetAsyncKeyState ( VK_RMENU ) )
1677       {
1678           INVOKE_WCB ( *window, Special,
1679                        ( GLUT_KEY_ALT_R, window->State.MouseX, window->State.MouseY )
1680                      );
1681
1682           rAlt = 1;
1683       }
1684
1685       /* Checking for CTRL, ALT, and SHIFT key positions:  Key Up! */
1686       if ( lControl && !GetAsyncKeyState ( VK_LCONTROL ) )
1687       {
1688           INVOKE_WCB ( *window, SpecialUp,
1689                        ( GLUT_KEY_CTRL_L, window->State.MouseX, window->State.MouseY )
1690                      );
1691
1692           lControl = 0;
1693       }
1694
1695       if ( rControl && !GetAsyncKeyState ( VK_RCONTROL ) )
1696       {
1697           INVOKE_WCB ( *window, SpecialUp,
1698                        ( GLUT_KEY_CTRL_R, window->State.MouseX, window->State.MouseY )
1699                      );
1700
1701           rControl = 0;
1702       }
1703
1704       if ( lShift && !GetAsyncKeyState ( VK_LSHIFT ) )
1705       {
1706           INVOKE_WCB ( *window, SpecialUp,
1707                        ( GLUT_KEY_SHIFT_L, window->State.MouseX, window->State.MouseY )
1708                      );
1709
1710           lShift = 0;
1711       }
1712
1713       if ( rShift && !GetAsyncKeyState ( VK_RSHIFT ) )
1714       {
1715           INVOKE_WCB ( *window, SpecialUp,
1716                        ( GLUT_KEY_SHIFT_R, window->State.MouseX, window->State.MouseY )
1717                      );
1718
1719           rShift = 0;
1720       }
1721
1722       if ( lAlt && !GetAsyncKeyState ( VK_LMENU ) )
1723       {
1724           INVOKE_WCB ( *window, SpecialUp,
1725                        ( GLUT_KEY_ALT_L, window->State.MouseX, window->State.MouseY )
1726                      );
1727
1728           lAlt = 0;
1729       }
1730
1731       if ( rAlt && !GetAsyncKeyState ( VK_RMENU ) )
1732       {
1733           INVOKE_WCB ( *window, SpecialUp,
1734                        ( GLUT_KEY_ALT_R, window->State.MouseX, window->State.MouseY )
1735                      );
1736
1737           rAlt = 0;
1738       }
1739     }
1740
1741     switch( uMsg )
1742     {
1743     case WM_CREATE:
1744         /* The window structure is passed as the creation structure paramter... */
1745         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
1746         FREEGLUT_INTERNAL_ERROR_EXIT ( ( window != NULL ), "Cannot create window",
1747                                        "fgWindowProc" );
1748
1749         window->Window.Handle = hWnd;
1750         window->Window.Device = GetDC( hWnd );
1751         if( window->IsMenu )
1752         {
1753             unsigned int current_DisplayMode = fgState.DisplayMode;
1754             fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH;
1755 #if !defined(_WIN32_WCE)
1756             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
1757 #endif
1758             fgState.DisplayMode = current_DisplayMode;
1759
1760             if( fgStructure.MenuContext )
1761                 wglMakeCurrent( window->Window.Device,
1762                                 fgStructure.MenuContext->MContext
1763                 );
1764             else
1765             {
1766                 fgStructure.MenuContext =
1767                     (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
1768                 fgStructure.MenuContext->MContext =
1769                     wglCreateContext( window->Window.Device );
1770             }
1771
1772             /* window->Window.Context = wglGetCurrentContext ();   */
1773             window->Window.Context = wglCreateContext( window->Window.Device );
1774         }
1775         else
1776         {
1777 #if !defined(_WIN32_WCE)
1778             fgSetupPixelFormat( window, GL_FALSE, PFD_MAIN_PLANE );
1779 #endif
1780
1781             if( ! fgState.UseCurrentContext )
1782                 window->Window.Context =
1783                     wglCreateContext( window->Window.Device );
1784             else
1785             {
1786                 window->Window.Context = wglGetCurrentContext( );
1787                 if( ! window->Window.Context )
1788                     window->Window.Context =
1789                         wglCreateContext( window->Window.Device );
1790             }
1791
1792 #if !defined(_WIN32_WCE)
1793             fgNewWGLCreateContext( window );
1794 #endif
1795         }
1796
1797         window->State.NeedToResize = GL_TRUE;
1798         if( ( window->State.Width < 0 ) || ( window->State.Height < 0 ) )
1799         {
1800             SFG_Window *current_window = fgStructure.CurrentWindow;
1801
1802             fgSetWindow( window );
1803             window->State.Width = glutGet( GLUT_WINDOW_WIDTH );
1804             window->State.Height = glutGet( GLUT_WINDOW_HEIGHT );
1805             fgSetWindow( current_window );
1806         }
1807
1808         ReleaseDC( window->Window.Handle, window->Window.Device );
1809
1810 #if defined(_WIN32_WCE)
1811         /* Take over button handling */
1812         {
1813             HINSTANCE dxDllLib=LoadLibrary(_T("gx.dll"));
1814             if (dxDllLib)
1815             {
1816                 GXGetDefaultKeys_=(GXGETDEFAULTKEYS)GetProcAddress(dxDllLib, _T("?GXGetDefaultKeys@@YA?AUGXKeyList@@H@Z"));
1817                 GXOpenInput_=(GXOPENINPUT)GetProcAddress(dxDllLib, _T("?GXOpenInput@@YAHXZ"));
1818             }
1819
1820             if(GXOpenInput_)
1821                 (*GXOpenInput_)();
1822             if(GXGetDefaultKeys_)
1823                 gxKeyList = (*GXGetDefaultKeys_)(GX_LANDSCAPEKEYS);
1824         }
1825
1826 #endif /* defined(_WIN32_WCE) */
1827         break;
1828
1829     case WM_SIZE:
1830         /*
1831          * If the window is visible, then it is the user manually resizing it.
1832          * If it is not, then it is the system sending us a dummy resize with
1833          * zero dimensions on a "glutIconifyWindow" call.
1834          */
1835         if( window->State.Visible )
1836         {
1837             window->State.NeedToResize = GL_TRUE;
1838 #if defined(_WIN32_WCE)
1839             window->State.Width  = HIWORD(lParam);
1840             window->State.Height = LOWORD(lParam);
1841 #else
1842             window->State.Width  = LOWORD(lParam);
1843             window->State.Height = HIWORD(lParam);
1844 #endif /* defined(_WIN32_WCE) */
1845         }
1846
1847         break;
1848
1849     case WM_SETFOCUS:
1850 /*        printf("WM_SETFOCUS: %p\n", window ); */
1851         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1852         INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
1853         break;
1854
1855     case WM_KILLFOCUS:
1856 /*        printf("WM_KILLFOCUS: %p\n", window ); */
1857         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1858         INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
1859
1860         if( window->IsMenu &&
1861             window->ActiveMenu && window->ActiveMenu->IsActive )
1862             fgUpdateMenuHighlight( window->ActiveMenu );
1863
1864         break;
1865
1866 #if 0
1867     case WM_ACTIVATE:
1868         if (LOWORD(wParam) != WA_INACTIVE)
1869         {
1870 /*            printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
1871                    window->State.Cursor ); */
1872             fgSetCursor( window, window->State.Cursor );
1873         }
1874
1875         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1876         break;
1877 #endif
1878
1879     case WM_SETCURSOR:
1880 /*      printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
1881         if( LOWORD( lParam ) == HTCLIENT )
1882             fgSetCursor ( window, window->State.Cursor ) ;
1883         else
1884             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1885         break;
1886
1887     case WM_SHOWWINDOW:
1888         window->State.Visible = GL_TRUE;
1889         window->State.Redisplay = GL_TRUE;
1890         break;
1891
1892     case WM_PAINT:
1893         /* Turn on the visibility in case it was turned off somehow */
1894         window->State.Visible = GL_TRUE;
1895         BeginPaint( hWnd, &ps );
1896         fghRedrawWindow( window );
1897         EndPaint( hWnd, &ps );
1898         break;
1899
1900     case WM_CLOSE:
1901         fgDestroyWindow ( window );
1902         if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
1903             PostQuitMessage(0);
1904         break;
1905
1906     case WM_DESTROY:
1907         /*
1908          * The window already got destroyed, so don't bother with it.
1909          */
1910         return 0;
1911
1912     case WM_MOUSEMOVE:
1913     {
1914 #if defined(_WIN32_WCE)
1915         window->State.MouseX = 320-HIWORD( lParam );
1916         window->State.MouseY = LOWORD( lParam );
1917 #else
1918         window->State.MouseX = LOWORD( lParam );
1919         window->State.MouseY = HIWORD( lParam );
1920 #endif /* defined(_WIN32_WCE) */
1921         /* Restrict to [-32768, 32767] to match X11 behaviour       */
1922         /* See comment in "freeglut_developer" mailing list 10/4/04 */
1923         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
1924         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
1925
1926         if ( window->ActiveMenu )
1927         {
1928             fgUpdateMenuHighlight( window->ActiveMenu );
1929             break;
1930         }
1931         SetFocus(window->Window.Handle);
1932
1933         fgState.Modifiers = fghGetWin32Modifiers( );
1934
1935         if( ( wParam & MK_LBUTTON ) ||
1936             ( wParam & MK_MBUTTON ) ||
1937             ( wParam & MK_RBUTTON ) )
1938             INVOKE_WCB( *window, Motion, ( window->State.MouseX,
1939                                            window->State.MouseY ) );
1940         else
1941             INVOKE_WCB( *window, Passive, ( window->State.MouseX,
1942                                             window->State.MouseY ) );
1943
1944         fgState.Modifiers = INVALID_MODIFIERS;
1945     }
1946     break;
1947
1948     case WM_LBUTTONDOWN:
1949     case WM_MBUTTONDOWN:
1950     case WM_RBUTTONDOWN:
1951     case WM_LBUTTONUP:
1952     case WM_MBUTTONUP:
1953     case WM_RBUTTONUP:
1954     {
1955         GLboolean pressed = GL_TRUE;
1956         int button;
1957
1958 #if defined(_WIN32_WCE)
1959         window->State.MouseX = 320-HIWORD( lParam );
1960         window->State.MouseY = LOWORD( lParam );
1961 #else
1962         window->State.MouseX = LOWORD( lParam );
1963         window->State.MouseY = HIWORD( lParam );
1964 #endif /* defined(_WIN32_WCE) */
1965
1966         /* Restrict to [-32768, 32767] to match X11 behaviour       */
1967         /* See comment in "freeglut_developer" mailing list 10/4/04 */
1968         if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
1969         if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
1970
1971         switch( uMsg )
1972         {
1973         case WM_LBUTTONDOWN:
1974             pressed = GL_TRUE;
1975             button = GLUT_LEFT_BUTTON;
1976             break;
1977         case WM_MBUTTONDOWN:
1978             pressed = GL_TRUE;
1979             button = GLUT_MIDDLE_BUTTON;
1980             break;
1981         case WM_RBUTTONDOWN:
1982             pressed = GL_TRUE;
1983             button = GLUT_RIGHT_BUTTON;
1984             break;
1985         case WM_LBUTTONUP:
1986             pressed = GL_FALSE;
1987             button = GLUT_LEFT_BUTTON;
1988             break;
1989         case WM_MBUTTONUP:
1990             pressed = GL_FALSE;
1991             button = GLUT_MIDDLE_BUTTON;
1992             break;
1993         case WM_RBUTTONUP:
1994             pressed = GL_FALSE;
1995             button = GLUT_RIGHT_BUTTON;
1996             break;
1997         default:
1998             pressed = GL_FALSE;
1999             button = -1;
2000             break;
2001         }
2002
2003 #if !defined(_WIN32_WCE)
2004         if( GetSystemMetrics( SM_SWAPBUTTON ) )
2005         {
2006             if( button == GLUT_LEFT_BUTTON )
2007                 button = GLUT_RIGHT_BUTTON;
2008             else
2009                 if( button == GLUT_RIGHT_BUTTON )
2010                     button = GLUT_LEFT_BUTTON;
2011         }
2012 #endif /* !defined(_WIN32_WCE) */
2013
2014         if( button == -1 )
2015             return DefWindowProc( hWnd, uMsg, lParam, wParam );
2016
2017         /*
2018          * Do not execute the application's mouse callback if a menu
2019          * is hooked to this button.  In that case an appropriate
2020          * private call should be generated.
2021          */
2022         if( fgCheckActiveMenu( window, button, pressed,
2023                                window->State.MouseX, window->State.MouseY ) )
2024             break;
2025
2026         /* Set capture so that the window captures all the mouse messages */
2027         /*
2028          * XXX - Multiple button support:  Under X11, the mouse is not released
2029          * XXX - from the window until all buttons have been released, even if the
2030          * XXX - user presses a button in another window.  This will take more
2031          * XXX - code changes than I am up to at the moment (10/5/04).  The present
2032          * XXX - is a 90 percent solution.
2033          */
2034         if ( pressed == GL_TRUE )
2035           SetCapture ( window->Window.Handle ) ;
2036         else
2037           ReleaseCapture () ;
2038
2039         if( ! FETCH_WCB( *window, Mouse ) )
2040             break;
2041
2042         fgSetWindow( window );
2043         fgState.Modifiers = fghGetWin32Modifiers( );
2044
2045         INVOKE_WCB(
2046             *window, Mouse,
2047             ( button,
2048               pressed ? GLUT_DOWN : GLUT_UP,
2049               window->State.MouseX,
2050               window->State.MouseY
2051             )
2052         );
2053
2054         fgState.Modifiers = INVALID_MODIFIERS;
2055     }
2056     break;
2057
2058     case 0x020a:
2059         /* Should be WM_MOUSEWHEEL but my compiler doesn't recognize it */
2060     {
2061         /*
2062          * XXX THIS IS SPECULATIVE -- John Fay, 10/2/03
2063          * XXX Should use WHEEL_DELTA instead of 120
2064          */
2065         int wheel_number = LOWORD( wParam );
2066         short ticks = ( short )HIWORD( wParam ) / 120;
2067         int direction = 1;
2068
2069         if( ticks < 0 )
2070         {
2071             direction = -1;
2072             ticks = -ticks;
2073         }
2074
2075         /*
2076          * The mouse cursor has moved. Remember the new mouse cursor's position
2077          */
2078         /*        window->State.MouseX = LOWORD( lParam ); */
2079         /* Need to adjust by window position, */
2080         /*        window->State.MouseY = HIWORD( lParam ); */
2081         /* change "lParam" to other parameter */
2082
2083         if( ! FETCH_WCB( *window, MouseWheel ) &&
2084             ! FETCH_WCB( *window, Mouse ) )
2085             break;
2086
2087         fgSetWindow( window );
2088         fgState.Modifiers = fghGetWin32Modifiers( );
2089
2090         while( ticks-- )
2091             if( FETCH_WCB( *window, MouseWheel ) )
2092                 INVOKE_WCB( *window, MouseWheel,
2093                             ( wheel_number,
2094                               direction,
2095                               window->State.MouseX,
2096                               window->State.MouseY
2097                             )
2098                 );
2099             else  /* No mouse wheel, call the mouse button callback twice */
2100             {
2101                 /*
2102                  * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
2103                  *  "    "   one                     +1 to 5, -1 to 6, ...
2104                  *
2105                  * XXX The below assumes that you have no more than 3 mouse
2106                  * XXX buttons.  Sorry.
2107                  */
2108                 int button = wheel_number * 2 + 3;
2109                 if( direction < 0 )
2110                     ++button;
2111                 INVOKE_WCB( *window, Mouse,
2112                             ( button, GLUT_DOWN,
2113                               window->State.MouseX, window->State.MouseY )
2114                 );
2115                 INVOKE_WCB( *window, Mouse,
2116                             ( button, GLUT_UP,
2117                               window->State.MouseX, window->State.MouseY )
2118                 );
2119             }
2120
2121         fgState.Modifiers = INVALID_MODIFIERS;
2122     }
2123     break ;
2124
2125     case WM_SYSKEYDOWN:
2126     case WM_KEYDOWN:
2127     {
2128         int keypress = -1;
2129         POINT mouse_pos ;
2130
2131         if( ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) && (HIWORD(lParam) & KF_REPEAT) )
2132             break;
2133
2134         /*
2135          * Remember the current modifiers state. This is done here in order
2136          * to make sure the VK_DELETE keyboard callback is executed properly.
2137          */
2138         fgState.Modifiers = fghGetWin32Modifiers( );
2139
2140         GetCursorPos( &mouse_pos );
2141         ScreenToClient( window->Window.Handle, &mouse_pos );
2142
2143         window->State.MouseX = mouse_pos.x;
2144         window->State.MouseY = mouse_pos.y;
2145
2146         /* Convert the Win32 keystroke codes to GLUTtish way */
2147 #       define KEY(a,b) case a: keypress = b; break;
2148
2149         switch( wParam )
2150         {
2151             KEY( VK_F1,     GLUT_KEY_F1        );
2152             KEY( VK_F2,     GLUT_KEY_F2        );
2153             KEY( VK_F3,     GLUT_KEY_F3        );
2154             KEY( VK_F4,     GLUT_KEY_F4        );
2155             KEY( VK_F5,     GLUT_KEY_F5        );
2156             KEY( VK_F6,     GLUT_KEY_F6        );
2157             KEY( VK_F7,     GLUT_KEY_F7        );
2158             KEY( VK_F8,     GLUT_KEY_F8        );
2159             KEY( VK_F9,     GLUT_KEY_F9        );
2160             KEY( VK_F10,    GLUT_KEY_F10       );
2161             KEY( VK_F11,    GLUT_KEY_F11       );
2162             KEY( VK_F12,    GLUT_KEY_F12       );
2163             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
2164             KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
2165             KEY( VK_HOME,   GLUT_KEY_HOME      );
2166             KEY( VK_END,    GLUT_KEY_END       );
2167             KEY( VK_LEFT,   GLUT_KEY_LEFT      );
2168             KEY( VK_UP,     GLUT_KEY_UP        );
2169             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
2170             KEY( VK_DOWN,   GLUT_KEY_DOWN      );
2171             KEY( VK_INSERT, GLUT_KEY_INSERT    );
2172             KEY( VK_LCONTROL, GLUT_KEY_CTRL_L  );
2173             KEY( VK_RCONTROL, GLUT_KEY_CTRL_R  );
2174             KEY( VK_LSHIFT, GLUT_KEY_SHIFT_L   );
2175             KEY( VK_RSHIFT, GLUT_KEY_SHIFT_R   );
2176             KEY( VK_LMENU,  GLUT_KEY_ALT_L     );
2177             KEY( VK_RMENU,  GLUT_KEY_ALT_R     );
2178
2179         case VK_DELETE:
2180             /* The delete key should be treated as an ASCII keypress: */
2181             INVOKE_WCB( *window, Keyboard,
2182                         ( 127, window->State.MouseX, window->State.MouseY )
2183             );
2184         }
2185
2186 #if defined(_WIN32_WCE)
2187         if(!(lParam & 0x40000000)) /* Prevent auto-repeat */
2188         {
2189             if(wParam==(unsigned)gxKeyList.vkRight)
2190                 keypress = GLUT_KEY_RIGHT;
2191             else if(wParam==(unsigned)gxKeyList.vkLeft)
2192                 keypress = GLUT_KEY_LEFT;
2193             else if(wParam==(unsigned)gxKeyList.vkUp)
2194                 keypress = GLUT_KEY_UP;
2195             else if(wParam==(unsigned)gxKeyList.vkDown)
2196                 keypress = GLUT_KEY_DOWN;
2197             else if(wParam==(unsigned)gxKeyList.vkA)
2198                 keypress = GLUT_KEY_F1;
2199             else if(wParam==(unsigned)gxKeyList.vkB)
2200                 keypress = GLUT_KEY_F2;
2201             else if(wParam==(unsigned)gxKeyList.vkC)
2202                 keypress = GLUT_KEY_F3;
2203             else if(wParam==(unsigned)gxKeyList.vkStart)
2204                 keypress = GLUT_KEY_F4;
2205         }
2206 #endif
2207
2208         if( keypress != -1 )
2209             INVOKE_WCB( *window, Special,
2210                         ( keypress,
2211                           window->State.MouseX, window->State.MouseY )
2212             );
2213
2214         fgState.Modifiers = INVALID_MODIFIERS;
2215     }
2216     break;
2217
2218     case WM_SYSKEYUP:
2219     case WM_KEYUP:
2220     {
2221         int keypress = -1;
2222         POINT mouse_pos;
2223
2224         /*
2225          * Remember the current modifiers state. This is done here in order
2226          * to make sure the VK_DELETE keyboard callback is executed properly.
2227          */
2228         fgState.Modifiers = fghGetWin32Modifiers( );
2229
2230         GetCursorPos( &mouse_pos );
2231         ScreenToClient( window->Window.Handle, &mouse_pos );
2232
2233         window->State.MouseX = mouse_pos.x;
2234         window->State.MouseY = mouse_pos.y;
2235
2236         /*
2237          * Convert the Win32 keystroke codes to GLUTtish way.
2238          * "KEY(a,b)" was defined under "WM_KEYDOWN"
2239          */
2240
2241         switch( wParam )
2242         {
2243             KEY( VK_F1,     GLUT_KEY_F1        );
2244             KEY( VK_F2,     GLUT_KEY_F2        );
2245             KEY( VK_F3,     GLUT_KEY_F3        );
2246             KEY( VK_F4,     GLUT_KEY_F4        );
2247             KEY( VK_F5,     GLUT_KEY_F5        );
2248             KEY( VK_F6,     GLUT_KEY_F6        );
2249             KEY( VK_F7,     GLUT_KEY_F7        );
2250             KEY( VK_F8,     GLUT_KEY_F8        );
2251             KEY( VK_F9,     GLUT_KEY_F9        );
2252             KEY( VK_F10,    GLUT_KEY_F10       );
2253             KEY( VK_F11,    GLUT_KEY_F11       );
2254             KEY( VK_F12,    GLUT_KEY_F12       );
2255             KEY( VK_PRIOR,  GLUT_KEY_PAGE_UP   );
2256             KEY( VK_NEXT,   GLUT_KEY_PAGE_DOWN );
2257             KEY( VK_HOME,   GLUT_KEY_HOME      );
2258             KEY( VK_END,    GLUT_KEY_END       );
2259             KEY( VK_LEFT,   GLUT_KEY_LEFT      );
2260             KEY( VK_UP,     GLUT_KEY_UP        );
2261             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     );
2262             KEY( VK_DOWN,   GLUT_KEY_DOWN      );
2263             KEY( VK_INSERT, GLUT_KEY_INSERT    );
2264             KEY( VK_LCONTROL, GLUT_KEY_CTRL_L  );
2265             KEY( VK_RCONTROL, GLUT_KEY_CTRL_R  );
2266             KEY( VK_LSHIFT, GLUT_KEY_SHIFT_L   );
2267             KEY( VK_RSHIFT, GLUT_KEY_SHIFT_R   );
2268             KEY( VK_LMENU,  GLUT_KEY_ALT_L     );
2269             KEY( VK_RMENU,  GLUT_KEY_ALT_R     );
2270
2271           case VK_DELETE:
2272               /* The delete key should be treated as an ASCII keypress: */
2273               INVOKE_WCB( *window, KeyboardUp,
2274                           ( 127, window->State.MouseX, window->State.MouseY )
2275               );
2276               break;
2277
2278         default:
2279         {
2280 #if !defined(_WIN32_WCE)
2281             BYTE state[ 256 ];
2282             WORD code[ 2 ];
2283
2284             GetKeyboardState( state );
2285
2286             if( ToAscii( (UINT)wParam, 0, state, code, 0 ) == 1 )
2287                 wParam=code[ 0 ];
2288
2289             INVOKE_WCB( *window, KeyboardUp,
2290                         ( (char)wParam,
2291                           window->State.MouseX, window->State.MouseY )
2292             );
2293 #endif /* !defined(_WIN32_WCE) */
2294         }
2295         }
2296
2297         if( keypress != -1 )
2298             INVOKE_WCB( *window, SpecialUp,
2299                         ( keypress,
2300                           window->State.MouseX, window->State.MouseY )
2301             );
2302
2303         fgState.Modifiers = INVALID_MODIFIERS;
2304     }
2305     break;
2306
2307     case WM_SYSCHAR:
2308     case WM_CHAR:
2309     {
2310       if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
2311             break;
2312
2313         fgState.Modifiers = fghGetWin32Modifiers( );
2314         INVOKE_WCB( *window, Keyboard,
2315                     ( (char)wParam,
2316                       window->State.MouseX, window->State.MouseY )
2317         );
2318         fgState.Modifiers = INVALID_MODIFIERS;
2319     }
2320     break;
2321
2322     case WM_CAPTURECHANGED:
2323         /* User has finished resizing the window, force a redraw */
2324         INVOKE_WCB( *window, Display, ( ) );
2325
2326         /*lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); */
2327         break;
2328
2329         /* Other messages that I have seen and which are not handled already */
2330     case WM_SETTEXT:  /* 0x000c */
2331         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2332         /* Pass it on to "DefWindowProc" to set the window text */
2333         break;
2334
2335     case WM_GETTEXT:  /* 0x000d */
2336         /* Ideally we would copy the title of the window into "lParam" */
2337         /* strncpy ( (char *)lParam, "Window Title", wParam );
2338            lRet = ( wParam > 12 ) ? 12 : wParam;  */
2339         /* the number of characters copied */
2340         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2341         break;
2342
2343     case WM_GETTEXTLENGTH:  /* 0x000e */
2344         /* Ideally we would get the length of the title of the window */
2345         lRet = 12;
2346         /* the number of characters in "Window Title\0" (see above) */
2347         break;
2348
2349     case WM_ERASEBKGND:  /* 0x0014 */
2350         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2351         break;
2352
2353 #if !defined(_WIN32_WCE)
2354     case WM_SYNCPAINT:  /* 0x0088 */
2355         /* Another window has moved, need to update this one */
2356         window->State.Redisplay = GL_TRUE;
2357         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2358         /* Help screen says this message must be passed to "DefWindowProc" */
2359         break;
2360
2361     case WM_NCPAINT:  /* 0x0085 */
2362       /* Need to update the border of this window */
2363         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2364         /* Pass it on to "DefWindowProc" to repaint a standard border */
2365         break;
2366
2367     case WM_SYSCOMMAND :  /* 0x0112 */
2368         {
2369           /*
2370            * We have received a system command message.  Try to act on it.
2371            * The commands are passed in through the "wParam" parameter:
2372            * The least significant digit seems to be which edge of the window
2373            * is being used for a resize event:
2374            *     4  3  5
2375            *     1     2
2376            *     7  6  8
2377            * Congratulations and thanks to Richard Rauch for figuring this out..
2378            */
2379             switch ( wParam & 0xfff0 )
2380             {
2381             case SC_SIZE       :
2382                 break ;
2383
2384             case SC_MOVE       :
2385                 break ;
2386
2387             case SC_MINIMIZE   :
2388                 /* User has clicked on the "-" to minimize the window */
2389                 /* Turn off the visibility */
2390                 window->State.Visible = GL_FALSE ;
2391
2392                 break ;
2393
2394             case SC_MAXIMIZE   :
2395                 break ;
2396
2397             case SC_NEXTWINDOW :
2398                 break ;
2399
2400             case SC_PREVWINDOW :
2401                 break ;
2402
2403             case SC_CLOSE      :
2404                 /* Followed very closely by a WM_CLOSE message */
2405                 break ;
2406
2407             case SC_VSCROLL    :
2408                 break ;
2409
2410             case SC_HSCROLL    :
2411                 break ;
2412
2413             case SC_MOUSEMENU  :
2414                 break ;
2415
2416             case SC_KEYMENU    :
2417                 break ;
2418
2419             case SC_ARRANGE    :
2420                 break ;
2421
2422             case SC_RESTORE    :
2423                 break ;
2424
2425             case SC_TASKLIST   :
2426                 break ;
2427
2428             case SC_SCREENSAVE :
2429                 break ;
2430
2431             case SC_HOTKEY     :
2432                 break ;
2433
2434 #if(WINVER >= 0x0400)
2435             case SC_DEFAULT    :
2436                 break ;
2437
2438             case SC_MONITORPOWER    :
2439                 break ;
2440
2441             case SC_CONTEXTHELP    :
2442                 break ;
2443 #endif /* WINVER >= 0x0400 */
2444
2445             default:
2446 #if _DEBUG
2447                 fgWarning( "Unknown wParam type 0x%x", wParam );
2448 #endif
2449                 break;
2450             }
2451         }
2452 #endif /* !defined(_WIN32_WCE) */
2453
2454         /* We need to pass the message on to the operating system as well */
2455         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2456         break;
2457
2458 #ifdef WM_TOUCH
2459         /* handle multi-touch messages */
2460         case WM_TOUCH:
2461         {
2462                 unsigned int numInputs = (unsigned int)wParam;
2463                 unsigned int i = 0;
2464                 TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs);
2465
2466                 if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) {
2467                     fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo");
2468                     fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle");
2469                 }
2470
2471                 if (!fghGetTouchInputInfo) { 
2472                         free( (void*)ti );
2473                         break;
2474                 }
2475
2476                 if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) {
2477                         /* Handle each contact point */
2478                         for (i = 0; i < numInputs; ++i ) {
2479
2480                                 POINT tp;
2481                                 tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
2482                                 tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
2483                                 ScreenToClient( hWnd, &tp );
2484
2485                                 ti[i].dwID = ti[i].dwID * 2;
2486
2487                                 if (ti[i].dwFlags & TOUCHEVENTF_DOWN) {
2488                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_ENTERED ) );
2489                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) );
2490                                 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
2491                                         INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) );
2492                                 } else if (ti[i].dwFlags & TOUCHEVENTF_UP)   { 
2493                                         INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) );
2494                                         INVOKE_WCB( *window, MultiEntry,  ( ti[i].dwID, GLUT_LEFT ) );
2495                                 }
2496                         }
2497                 }
2498                 fghCloseTouchInputHandle((HTOUCHINPUT)lParam);
2499                 free( (void*)ti );
2500                 lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/
2501                 break;
2502         }
2503 #endif
2504     default:
2505         /* Handle unhandled messages */
2506         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
2507         break;
2508     }
2509
2510     return lRet;
2511 }
2512 #endif
2513
2514 /*** END OF FILE ***/