to check for menu opening/closing upon button press, should pass cursor position...
[freeglut] / src / x11 / fg_main_x11.c
1 /*
2  * freeglut_main_x11.c
3  *
4  * The X11-specific 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  * Copied for Platform code by Evan Felix <karcaw at gmail.com>
9  * Creation date: Thur Feb 2 2012
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
24  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
25  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27  */
28
29 #include <GL/freeglut.h>
30 #include "../fg_internal.h"
31 #ifdef HAVE_ERRNO_H
32 #    include <errno.h>
33 #endif
34 #include <stdarg.h>
35 #ifdef  HAVE_VFPRINTF
36 #    define VFPRINTF(s,f,a) vfprintf((s),(f),(a))
37 #elif defined(HAVE__DOPRNT)
38 #    define VFPRINTF(s,f,a) _doprnt((f),(a),(s))
39 #else
40 #    define VFPRINTF(s,f,a)
41 #endif
42
43
44 /*
45  * Try to get the maximum value allowed for ints, falling back to the minimum
46  * guaranteed by ISO C99 if there is no suitable header.
47  */
48 #ifdef HAVE_LIMITS_H
49 #    include <limits.h>
50 #endif
51 #ifndef INT_MAX
52 #    define INT_MAX 32767
53 #endif
54
55 #ifndef MIN
56 #    define MIN(a,b) (((a)<(b)) ? (a) : (b))
57 #endif
58
59 /* used in the event handling code to match and discard stale mouse motion events */
60 static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg);
61
62 /*
63  * TODO BEFORE THE STABLE RELEASE:
64  *
65  * There are some issues concerning window redrawing under X11, and maybe
66  * some events are not handled. The Win32 version lacks some more features,
67  * but seems acceptable for not demanding purposes.
68  *
69  * Need to investigate why the X11 version breaks out with an error when
70  * closing a window (using the window manager, not glutDestroyWindow)...
71  */
72  
73  
74
75 fg_time_t fgPlatformSystemTime ( void )
76 {
77 #ifdef CLOCK_MONOTONIC
78     struct timespec now;
79     clock_gettime(CLOCK_MONOTONIC, &now);
80     return now.tv_nsec/1000000 + now.tv_sec*1000;
81 #elif defined(HAVE_GETTIMEOFDAY)
82     struct timeval now;
83     gettimeofday( &now, NULL );
84     return now.tv_usec/1000 + now.tv_sec*1000;
85 #endif
86 }
87
88 /*
89  * Does the magic required to relinquish the CPU until something interesting
90  * happens.
91  */
92
93 void fgPlatformSleepForEvents( fg_time_t msec )
94 {
95     /*
96      * Possibly due to aggressive use of XFlush() and friends,
97      * it is possible to have our socket drained but still have
98      * unprocessed events.  (Or, this may just be normal with
99      * X, anyway?)  We do non-trivial processing of X events
100      * after the event-reading loop, in any case, so we
101      * need to allow that we may have an empty socket but non-
102      * empty event queue.
103      */
104     if( ! XPending( fgDisplay.pDisplay.Display ) )
105     {
106         fd_set fdset;
107         int err;
108         int socket;
109         struct timeval wait;
110
111         socket = ConnectionNumber( fgDisplay.pDisplay.Display );
112         FD_ZERO( &fdset );
113         FD_SET( socket, &fdset );
114         wait.tv_sec = msec / 1000;
115         wait.tv_usec = (msec % 1000) * 1000;
116         err = select( socket+1, &fdset, NULL, NULL, &wait );
117
118 #ifdef HAVE_ERRNO_H
119         if( ( -1 == err ) && ( errno != EINTR ) )
120             fgWarning ( "freeglut select() error: %d", errno );
121 #endif
122     }
123 }
124
125
126 /*
127  * Returns GLUT modifier mask for the state field of an X11 event.
128  */
129 int fgPlatformGetModifiers( int state )
130 {
131     int ret = 0;
132
133     if( state & ( ShiftMask | LockMask ) )
134         ret |= GLUT_ACTIVE_SHIFT;
135     if( state & ControlMask )
136         ret |= GLUT_ACTIVE_CTRL;
137     if( state & Mod1Mask )
138         ret |= GLUT_ACTIVE_ALT;
139
140     return ret;
141 }
142
143 static const char* fghTypeToString( int type )
144 {
145     switch( type ) {
146     case KeyPress: return "KeyPress";
147     case KeyRelease: return "KeyRelease";
148     case ButtonPress: return "ButtonPress";
149     case ButtonRelease: return "ButtonRelease";
150     case MotionNotify: return "MotionNotify";
151     case EnterNotify: return "EnterNotify";
152     case LeaveNotify: return "LeaveNotify";
153     case FocusIn: return "FocusIn";
154     case FocusOut: return "FocusOut";
155     case KeymapNotify: return "KeymapNotify";
156     case Expose: return "Expose";
157     case GraphicsExpose: return "GraphicsExpose";
158     case NoExpose: return "NoExpose";
159     case VisibilityNotify: return "VisibilityNotify";
160     case CreateNotify: return "CreateNotify";
161     case DestroyNotify: return "DestroyNotify";
162     case UnmapNotify: return "UnmapNotify";
163     case MapNotify: return "MapNotify";
164     case MapRequest: return "MapRequest";
165     case ReparentNotify: return "ReparentNotify";
166     case ConfigureNotify: return "ConfigureNotify";
167     case ConfigureRequest: return "ConfigureRequest";
168     case GravityNotify: return "GravityNotify";
169     case ResizeRequest: return "ResizeRequest";
170     case CirculateNotify: return "CirculateNotify";
171     case CirculateRequest: return "CirculateRequest";
172     case PropertyNotify: return "PropertyNotify";
173     case SelectionClear: return "SelectionClear";
174     case SelectionRequest: return "SelectionRequest";
175     case SelectionNotify: return "SelectionNotify";
176     case ColormapNotify: return "ColormapNotify";
177     case ClientMessage: return "ClientMessage";
178     case MappingNotify: return "MappingNotify";
179     default: return "UNKNOWN";
180     }
181 }
182
183 static const char* fghBoolToString( Bool b )
184 {
185     return b == False ? "False" : "True";
186 }
187
188 static const char* fghNotifyHintToString( char is_hint )
189 {
190     switch( is_hint ) {
191     case NotifyNormal: return "NotifyNormal";
192     case NotifyHint: return "NotifyHint";
193     default: return "UNKNOWN";
194     }
195 }
196
197 static const char* fghNotifyModeToString( int mode )
198 {
199     switch( mode ) {
200     case NotifyNormal: return "NotifyNormal";
201     case NotifyGrab: return "NotifyGrab";
202     case NotifyUngrab: return "NotifyUngrab";
203     case NotifyWhileGrabbed: return "NotifyWhileGrabbed";
204     default: return "UNKNOWN";
205     }
206 }
207
208 static const char* fghNotifyDetailToString( int detail )
209 {
210     switch( detail ) {
211     case NotifyAncestor: return "NotifyAncestor";
212     case NotifyVirtual: return "NotifyVirtual";
213     case NotifyInferior: return "NotifyInferior";
214     case NotifyNonlinear: return "NotifyNonlinear";
215     case NotifyNonlinearVirtual: return "NotifyNonlinearVirtual";
216     case NotifyPointer: return "NotifyPointer";
217     case NotifyPointerRoot: return "NotifyPointerRoot";
218     case NotifyDetailNone: return "NotifyDetailNone";
219     default: return "UNKNOWN";
220     }
221 }
222
223 static const char* fghVisibilityToString( int state ) {
224     switch( state ) {
225     case VisibilityUnobscured: return "VisibilityUnobscured";
226     case VisibilityPartiallyObscured: return "VisibilityPartiallyObscured";
227     case VisibilityFullyObscured: return "VisibilityFullyObscured";
228     default: return "UNKNOWN";
229     }
230 }
231
232 static const char* fghConfigureDetailToString( int detail )
233 {
234     switch( detail ) {
235     case Above: return "Above";
236     case Below: return "Below";
237     case TopIf: return "TopIf";
238     case BottomIf: return "BottomIf";
239     case Opposite: return "Opposite";
240     default: return "UNKNOWN";
241     }
242 }
243
244 static const char* fghPlaceToString( int place )
245 {
246     switch( place ) {
247     case PlaceOnTop: return "PlaceOnTop";
248     case PlaceOnBottom: return "PlaceOnBottom";
249     default: return "UNKNOWN";
250     }
251 }
252
253 static const char* fghMappingRequestToString( int request )
254 {
255     switch( request ) {
256     case MappingModifier: return "MappingModifier";
257     case MappingKeyboard: return "MappingKeyboard";
258     case MappingPointer: return "MappingPointer";
259     default: return "UNKNOWN";
260     }
261 }
262
263 static const char* fghPropertyStateToString( int state )
264 {
265     switch( state ) {
266     case PropertyNewValue: return "PropertyNewValue";
267     case PropertyDelete: return "PropertyDelete";
268     default: return "UNKNOWN";
269     }
270 }
271
272 static const char* fghColormapStateToString( int state )
273 {
274     switch( state ) {
275     case ColormapUninstalled: return "ColormapUninstalled";
276     case ColormapInstalled: return "ColormapInstalled";
277     default: return "UNKNOWN";
278     }
279 }
280
281 __fg_unused static void fghPrintEvent( XEvent *event )
282 {
283     switch( event->type ) {
284
285     case KeyPress:
286     case KeyRelease: {
287         XKeyEvent *e = &event->xkey;
288         fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
289                    "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
290                    "keycode=%u, same_screen=%s", fghTypeToString( e->type ),
291                    e->window, e->root, e->subwindow, (unsigned long)e->time,
292                    e->x, e->y, e->x_root, e->y_root, e->state, e->keycode,
293                    fghBoolToString( e->same_screen ) );
294         break;
295     }
296
297     case ButtonPress:
298     case ButtonRelease: {
299         XButtonEvent *e = &event->xbutton;
300         fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
301                    "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
302                    "button=%u, same_screen=%d", fghTypeToString( e->type ),
303                    e->window, e->root, e->subwindow, (unsigned long)e->time,
304                    e->x, e->y, e->x_root, e->y_root, e->state, e->button,
305                    fghBoolToString( e->same_screen ) );
306         break;
307     }
308
309     case MotionNotify: {
310         XMotionEvent *e = &event->xmotion;
311         fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
312                    "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
313                    "is_hint=%s, same_screen=%d", fghTypeToString( e->type ),
314                    e->window, e->root, e->subwindow, (unsigned long)e->time,
315                    e->x, e->y, e->x_root, e->y_root, e->state,
316                    fghNotifyHintToString( e->is_hint ),
317                    fghBoolToString( e->same_screen ) );
318         break;
319     }
320
321     case EnterNotify:
322     case LeaveNotify: {
323         XCrossingEvent *e = &event->xcrossing;
324         fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
325                    "(x,y)=(%d,%d), mode=%s, detail=%s, same_screen=%d, "
326                    "focus=%d, state=0x%x", fghTypeToString( e->type ),
327                    e->window, e->root, e->subwindow, (unsigned long)e->time,
328                    e->x, e->y, fghNotifyModeToString( e->mode ),
329                    fghNotifyDetailToString( e->detail ), (int)e->same_screen,
330                    (int)e->focus, e->state );
331         break;
332     }
333
334     case FocusIn:
335     case FocusOut: {
336         XFocusChangeEvent *e = &event->xfocus;
337         fgWarning( "%s: window=0x%x, mode=%s, detail=%s",
338                    fghTypeToString( e->type ), e->window,
339                    fghNotifyModeToString( e->mode ),
340                    fghNotifyDetailToString( e->detail ) );
341         break;
342     }
343
344     case KeymapNotify: {
345         XKeymapEvent *e = &event->xkeymap;
346         char buf[32 * 2 + 1];
347         int i;
348         for ( i = 0; i < 32; i++ ) {
349             snprintf( &buf[ i * 2 ], sizeof( buf ) - i * 2,
350                       "%02x", e->key_vector[ i ] );
351         }
352         buf[ i ] = '\0';
353         fgWarning( "%s: window=0x%x, %s", fghTypeToString( e->type ), e->window,
354                    buf );
355         break;
356     }
357
358     case Expose: {
359         XExposeEvent *e = &event->xexpose;
360         fgWarning( "%s: window=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), "
361                    "count=%d", fghTypeToString( e->type ), e->window, e->x,
362                    e->y, e->width, e->height, e->count );
363         break;
364     }
365
366     case GraphicsExpose: {
367         XGraphicsExposeEvent *e = &event->xgraphicsexpose;
368         fgWarning( "%s: drawable=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), "
369                    "count=%d, (major_code,minor_code)=(%d,%d)",
370                    fghTypeToString( e->type ), e->drawable, e->x, e->y,
371                    e->width, e->height, e->count, e->major_code,
372                    e->minor_code );
373         break;
374     }
375
376     case NoExpose: {
377         XNoExposeEvent *e = &event->xnoexpose;
378         fgWarning( "%s: drawable=0x%x, (major_code,minor_code)=(%d,%d)",
379                    fghTypeToString( e->type ), e->drawable, e->major_code,
380                    e->minor_code );
381         break;
382     }
383
384     case VisibilityNotify: {
385         XVisibilityEvent *e = &event->xvisibility;
386         fgWarning( "%s: window=0x%x, state=%s", fghTypeToString( e->type ),
387                    e->window, fghVisibilityToString( e->state) );
388         break;
389     }
390
391     case CreateNotify: {
392         XCreateWindowEvent *e = &event->xcreatewindow;
393         fgWarning( "%s: (x,y)=(%d,%d), (width,height)=(%d,%d), border_width=%d, "
394                    "window=0x%x, override_redirect=%s",
395                    fghTypeToString( e->type ), e->x, e->y, e->width, e->height,
396                    e->border_width, e->window,
397                    fghBoolToString( e->override_redirect ) );
398         break;
399     }
400
401     case DestroyNotify: {
402         XDestroyWindowEvent *e = &event->xdestroywindow;
403         fgWarning( "%s: event=0x%x, window=0x%x",
404                    fghTypeToString( e->type ), e->event, e->window );
405         break;
406     }
407
408     case UnmapNotify: {
409         XUnmapEvent *e = &event->xunmap;
410         fgWarning( "%s: event=0x%x, window=0x%x, from_configure=%s",
411                    fghTypeToString( e->type ), e->event, e->window,
412                    fghBoolToString( e->from_configure ) );
413         break;
414     }
415
416     case MapNotify: {
417         XMapEvent *e = &event->xmap;
418         fgWarning( "%s: event=0x%x, window=0x%x, override_redirect=%s",
419                    fghTypeToString( e->type ), e->event, e->window,
420                    fghBoolToString( e->override_redirect ) );
421         break;
422     }
423
424     case MapRequest: {
425         XMapRequestEvent *e = &event->xmaprequest;
426         fgWarning( "%s: parent=0x%x, window=0x%x",
427                    fghTypeToString( event->type ), e->parent, e->window );
428         break;
429     }
430
431     case ReparentNotify: {
432         XReparentEvent *e = &event->xreparent;
433         fgWarning( "%s: event=0x%x, window=0x%x, parent=0x%x, (x,y)=(%d,%d), "
434                    "override_redirect=%s", fghTypeToString( e->type ),
435                    e->event, e->window, e->parent, e->x, e->y,
436                    fghBoolToString( e->override_redirect ) );
437         break;
438     }
439
440     case ConfigureNotify: {
441         XConfigureEvent *e = &event->xconfigure;
442         fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d), "
443                    "(width,height)=(%d,%d), border_width=%d, above=0x%x, "
444                    "override_redirect=%s", fghTypeToString( e->type ), e->event,
445                    e->window, e->x, e->y, e->width, e->height, e->border_width,
446                    e->above, fghBoolToString( e->override_redirect ) );
447         break;
448     }
449
450     case ConfigureRequest: {
451         XConfigureRequestEvent *e = &event->xconfigurerequest;
452         fgWarning( "%s: parent=0x%x, window=0x%x, (x,y)=(%d,%d), "
453                    "(width,height)=(%d,%d), border_width=%d, above=0x%x, "
454                    "detail=%s, value_mask=%lx", fghTypeToString( e->type ),
455                    e->parent, e->window, e->x, e->y, e->width, e->height,
456                    e->border_width, e->above,
457                    fghConfigureDetailToString( e->detail ), e->value_mask );
458         break;
459     }
460
461     case GravityNotify: {
462         XGravityEvent *e = &event->xgravity;
463         fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d)",
464                    fghTypeToString( e->type ), e->event, e->window, e->x, e->y );
465         break;
466     }
467
468     case ResizeRequest: {
469         XResizeRequestEvent *e = &event->xresizerequest;
470         fgWarning( "%s: window=0x%x, (width,height)=(%d,%d)",
471                    fghTypeToString( e->type ), e->window, e->width, e->height );
472         break;
473     }
474
475     case CirculateNotify: {
476         XCirculateEvent *e = &event->xcirculate;
477         fgWarning( "%s: event=0x%x, window=0x%x, place=%s",
478                    fghTypeToString( e->type ), e->event, e->window,
479                    fghPlaceToString( e->place ) );
480         break;
481     }
482
483     case CirculateRequest: {
484         XCirculateRequestEvent *e = &event->xcirculaterequest;
485         fgWarning( "%s: parent=0x%x, window=0x%x, place=%s",
486                    fghTypeToString( e->type ), e->parent, e->window,
487                    fghPlaceToString( e->place ) );
488         break;
489     }
490
491     case PropertyNotify: {
492         XPropertyEvent *e = &event->xproperty;
493         fgWarning( "%s: window=0x%x, atom=%lu, time=%lu, state=%s",
494                    fghTypeToString( e->type ), e->window,
495                    (unsigned long)e->atom, (unsigned long)e->time,
496                    fghPropertyStateToString( e->state ) );
497         break;
498     }
499
500     case SelectionClear: {
501         XSelectionClearEvent *e = &event->xselectionclear;
502         fgWarning( "%s: window=0x%x, selection=%lu, time=%lu",
503                    fghTypeToString( e->type ), e->window,
504                    (unsigned long)e->selection, (unsigned long)e->time );
505         break;
506     }
507
508     case SelectionRequest: {
509         XSelectionRequestEvent *e = &event->xselectionrequest;
510         fgWarning( "%s: owner=0x%x, requestor=0x%x, selection=0x%x, "
511                    "target=0x%x, property=%lu, time=%lu",
512                    fghTypeToString( e->type ), e->owner, e->requestor,
513                    (unsigned long)e->selection, (unsigned long)e->target,
514                    (unsigned long)e->property, (unsigned long)e->time );
515         break;
516     }
517
518     case SelectionNotify: {
519         XSelectionEvent *e = &event->xselection;
520         fgWarning( "%s: requestor=0x%x, selection=0x%x, target=0x%x, "
521                    "property=%lu, time=%lu", fghTypeToString( e->type ),
522                    e->requestor, (unsigned long)e->selection,
523                    (unsigned long)e->target, (unsigned long)e->property,
524                    (unsigned long)e->time );
525         break;
526     }
527
528     case ColormapNotify: {
529         XColormapEvent *e = &event->xcolormap;
530         fgWarning( "%s: window=0x%x, colormap=%lu, new=%s, state=%s",
531                    fghTypeToString( e->type ), e->window,
532                    (unsigned long)e->colormap, fghBoolToString( e->new ),
533                    fghColormapStateToString( e->state ) );
534         break;
535     }
536
537     case ClientMessage: {
538         XClientMessageEvent *e = &event->xclient;
539         char buf[ 61 ];
540         char* p = buf;
541         char* end = buf + sizeof( buf );
542         int i;
543         switch( e->format ) {
544         case 8:
545           for ( i = 0; i < 20; i++, p += 3 ) {
546                 snprintf( p, end - p, " %02x", e->data.b[ i ] );
547             }
548             break;
549         case 16:
550             for ( i = 0; i < 10; i++, p += 5 ) {
551                 snprintf( p, end - p, " %04x", e->data.s[ i ] );
552             }
553             break;
554         case 32:
555             for ( i = 0; i < 5; i++, p += 9 ) {
556                 snprintf( p, end - p, " %08lx", e->data.l[ i ] );
557             }
558             break;
559         }
560         *p = '\0';
561         fgWarning( "%s: window=0x%x, message_type=%lu, format=%d, data=(%s )",
562                    fghTypeToString( e->type ), e->window,
563                    (unsigned long)e->message_type, e->format, buf );
564         break;
565     }
566
567     case MappingNotify: {
568         XMappingEvent *e = &event->xmapping;
569         fgWarning( "%s: window=0x%x, request=%s, first_keycode=%d, count=%d",
570                    fghTypeToString( e->type ), e->window,
571                    fghMappingRequestToString( e->request ), e->first_keycode,
572                    e->count );
573         break;
574     }
575
576     default: {
577         fgWarning( "%s", fghTypeToString( event->type ) );
578         break;
579     }
580     }
581 }
582
583
584 void fgPlatformProcessSingleEvent ( void )
585 {
586     SFG_Window* window;
587     XEvent event;
588
589     /* This code was repeated constantly, so here it goes into a definition: */
590 #define GETWINDOW(a)                             \
591     window = fgWindowByHandle( event.a.window ); \
592     if( window == NULL )                         \
593         break;
594
595 #define GETMOUSE(a)                              \
596     window->State.MouseX = event.a.x;            \
597     window->State.MouseY = event.a.y;
598
599     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
600
601     while( XPending( fgDisplay.pDisplay.Display ) )
602     {
603         XNextEvent( fgDisplay.pDisplay.Display, &event );
604 #if _DEBUG
605         fghPrintEvent( &event );
606 #endif
607
608         switch( event.type )
609         {
610         case ClientMessage:
611             if (fgStructure.CurrentWindow)
612                 if(fgIsSpaceballXEvent(&event)) {
613                     fgSpaceballHandleXEvent(&event);
614                     break;
615                 }
616             /* Destroy the window when the WM_DELETE_WINDOW message arrives */
617             if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.pDisplay.DeleteWindow )
618             {
619                 GETWINDOW( xclient );
620
621                 fgDestroyWindow ( window );
622
623                 if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
624                 {
625                     fgDeinitialize( );
626                     exit( 0 );
627                 }
628                 else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
629                     fgState.ExecState = GLUT_EXEC_STATE_STOP;
630
631                 return;
632             }
633             break;
634
635             /*
636              * CreateNotify causes a configure-event so that sub-windows are
637              * handled compatibly with GLUT.  Otherwise, your sub-windows
638              * (in freeglut only) will not get an initial reshape event,
639              * which can break things.
640              *
641              * GLUT presumably does this because it generally tries to treat
642              * sub-windows the same as windows.
643              */
644         case CreateNotify:
645         case ConfigureNotify:
646             {
647                 int width, height;
648                 if( event.type == CreateNotify ) {
649                     GETWINDOW( xcreatewindow );
650                     width = event.xcreatewindow.width;
651                     height = event.xcreatewindow.height;
652                 } else {
653                     GETWINDOW( xconfigure );
654                     width = event.xconfigure.width;
655                     height = event.xconfigure.height;
656                 }
657
658                 if( ( width != window->State.pWState.OldWidth ) ||
659                     ( height != window->State.pWState.OldHeight ) )
660                 {
661                     SFG_Window *current_window = fgStructure.CurrentWindow;
662
663                     window->State.pWState.OldWidth = width;
664                     window->State.pWState.OldHeight = height;
665                     INVOKE_WCB( *window, Reshape, ( width, height ) );
666                     glutPostRedisplay( );
667                     if( window->IsMenu )
668                         fgSetWindow( current_window );
669                 }
670             }
671             break;
672
673         case DestroyNotify:
674             /*
675              * This is sent to confirm the XDestroyWindow call.
676              *
677              * XXX WHY is this commented out?  Should we re-enable it?
678              */
679             /* fgAddToWindowDestroyList ( window ); */
680             break;
681
682         case Expose:
683             /*
684              * We are too dumb to process partial exposes...
685              *
686              * XXX Well, we could do it.  However, it seems to only
687              * XXX be potentially useful for single-buffered (since
688              * XXX double-buffered does not respect viewport when we
689              * XXX do a buffer-swap).
690              *
691              */
692             if( event.xexpose.count == 0 )
693             {
694                 GETWINDOW( xexpose );
695                 window->State.Redisplay = GL_TRUE;
696             }
697             break;
698
699         case MapNotify:
700             break;
701
702         case UnmapNotify:
703             /* We get this when iconifying a window. */ 
704             GETWINDOW( xunmap );
705             INVOKE_WCB( *window, WindowStatus, ( GLUT_HIDDEN ) );
706             window->State.Visible = GL_FALSE;
707             break;
708
709         case MappingNotify:
710             /*
711              * Have the client's keyboard knowledge updated (xlib.ps,
712              * page 206, says that's a good thing to do)
713              */
714             XRefreshKeyboardMapping( (XMappingEvent *) &event );
715             break;
716
717         case VisibilityNotify:
718         {
719             /*
720              * Sending this event, the X server can notify us that the window
721              * has just acquired one of the three possible visibility states:
722              * VisibilityUnobscured, VisibilityPartiallyObscured or
723              * VisibilityFullyObscured. Note that we DO NOT receive a
724              * VisibilityNotify event when iconifying a window, we only get an
725              * UnmapNotify then.
726              */
727             GETWINDOW( xvisibility );
728             switch( event.xvisibility.state )
729             {
730             case VisibilityUnobscured:
731                 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_RETAINED ) );
732                 window->State.Visible = GL_TRUE;
733                 break;
734
735             case VisibilityPartiallyObscured:
736                 INVOKE_WCB( *window, WindowStatus,
737                             ( GLUT_PARTIALLY_RETAINED ) );
738                 window->State.Visible = GL_TRUE;
739                 break;
740
741             case VisibilityFullyObscured:
742                 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_COVERED ) );
743                 window->State.Visible = GL_FALSE;
744                 break;
745
746             default:
747                 fgWarning( "Unknown X visibility state: %d",
748                            event.xvisibility.state );
749                 break;
750             }
751         }
752         break;
753
754         case EnterNotify:
755         case LeaveNotify:
756             GETWINDOW( xcrossing );
757             GETMOUSE( xcrossing );
758             if( ( event.type == LeaveNotify ) && window->IsMenu &&
759                 window->ActiveMenu && window->ActiveMenu->IsActive )
760                 fgUpdateMenuHighlight( window->ActiveMenu );
761
762             INVOKE_WCB( *window, Entry, ( ( EnterNotify == event.type ) ?
763                                           GLUT_ENTERED :
764                                           GLUT_LEFT ) );
765             break;
766
767         case MotionNotify:
768         {
769             /* if GLUT_SKIP_STALE_MOTION_EVENTS is true, then discard all but
770              * the last motion event from the queue
771              */
772             if(fgState.SkipStaleMotion) {
773                 while(XCheckIfEvent(fgDisplay.pDisplay.Display, &event, match_motion, 0));
774             }
775
776             GETWINDOW( xmotion );
777             GETMOUSE( xmotion );
778
779             if( window->ActiveMenu )
780             {
781                 if( window == window->ActiveMenu->ParentWindow )
782                 {
783                     window->ActiveMenu->Window->State.MouseX =
784                         event.xmotion.x_root - window->ActiveMenu->X;
785                     window->ActiveMenu->Window->State.MouseY =
786                         event.xmotion.y_root - window->ActiveMenu->Y;
787                 }
788
789                 fgUpdateMenuHighlight( window->ActiveMenu );
790
791                 break;
792             }
793
794             /*
795              * XXX For more than 5 buttons, just check {event.xmotion.state},
796              * XXX rather than a host of bit-masks?  Or maybe we need to
797              * XXX track ButtonPress/ButtonRelease events in our own
798              * XXX bit-mask?
799              */
800             fgState.Modifiers = fgPlatformGetModifiers( event.xmotion.state );
801             if ( event.xmotion.state & ( Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask ) ) {
802                 INVOKE_WCB( *window, Motion, ( event.xmotion.x,
803                                                event.xmotion.y ) );
804             } else {
805                 INVOKE_WCB( *window, Passive, ( event.xmotion.x,
806                                                 event.xmotion.y ) );
807             }
808             fgState.Modifiers = INVALID_MODIFIERS;
809         }
810         break;
811
812         case ButtonRelease:
813         case ButtonPress:
814         {
815             GLboolean pressed = GL_TRUE;
816             int button;
817
818             if( event.type == ButtonRelease )
819                 pressed = GL_FALSE ;
820
821             /*
822              * A mouse button has been pressed or released. Traditionally,
823              * break if the window was found within the freeglut structures.
824              */
825             GETWINDOW( xbutton );
826             GETMOUSE( xbutton );
827
828             /*
829              * An X button (at least in XFree86) is numbered from 1.
830              * A GLUT button is numbered from 0.
831              * Old GLUT passed through buttons other than just the first
832              * three, though it only gave symbolic names and official
833              * support to the first three.
834              */
835             button = event.xbutton.button - 1;
836
837             /*
838              * Do not execute the application's mouse callback if a menu
839              * is hooked to this button.  In that case an appropriate
840              * private call should be generated.
841              */
842             if( fgCheckActiveMenu( window, button, pressed,
843                                    event.xbutton.x, event.xbutton.y ) )
844                 break;
845
846             /*
847              * Check if there is a mouse or mouse wheel callback hooked to the
848              * window
849              */
850             if( ! FETCH_WCB( *window, Mouse ) &&
851                 ! FETCH_WCB( *window, MouseWheel ) )
852                 break;
853
854             fgState.Modifiers = fgPlatformGetModifiers( event.xbutton.state );
855
856             /* Finally execute the mouse or mouse wheel callback */
857             if( ( button < glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS ) ) || ( ! FETCH_WCB( *window, MouseWheel ) ) )
858                 INVOKE_WCB( *window, Mouse, ( button,
859                                               pressed ? GLUT_DOWN : GLUT_UP,
860                                               event.xbutton.x,
861                                               event.xbutton.y )
862                 );
863             else
864             {
865                 /*
866                  * Map 4 and 5 to wheel zero; EVEN to +1, ODD to -1
867                  *  "  6 and 7 "    "   one; ...
868                  *
869                  * XXX This *should* be behind some variables/macros,
870                  * XXX since the order and numbering isn't certain
871                  * XXX See XFree86 configuration docs (even back in the
872                  * XXX 3.x days, and especially with 4.x).
873                  *
874                  * XXX Note that {button} has already been decremented
875                  * XXX in mapping from X button numbering to GLUT.
876                                  *
877                                  * XXX Should add support for partial wheel turns as Windows does -- 5/27/11
878                  */
879                 int wheel_number = (button - glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS )) / 2;
880                 int direction = -1;
881                 if( button % 2 )
882                     direction = 1;
883
884                 if( pressed )
885                     INVOKE_WCB( *window, MouseWheel, ( wheel_number,
886                                                        direction,
887                                                        event.xbutton.x,
888                                                        event.xbutton.y )
889                     );
890             }
891             fgState.Modifiers = INVALID_MODIFIERS;
892         }
893         break;
894
895         case KeyRelease:
896         case KeyPress:
897         {
898             FGCBKeyboard keyboard_cb;
899             FGCBSpecial special_cb;
900
901             GETWINDOW( xkey );
902             GETMOUSE( xkey );
903
904             /* Detect auto repeated keys, if configured globally or per-window */
905
906             if ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE )
907             {
908                 if (event.type==KeyRelease)
909                 {
910                     /*
911                      * Look at X11 keystate to detect repeat mode.
912                      * While X11 says the key is actually held down, we'll ignore KeyRelease/KeyPress pairs.
913                      */
914
915                     char keys[32];
916                     XQueryKeymap( fgDisplay.pDisplay.Display, keys ); /* Look at X11 keystate to detect repeat mode */
917
918                     if ( event.xkey.keycode<256 )            /* XQueryKeymap is limited to 256 keycodes    */
919                     {
920                         if ( keys[event.xkey.keycode>>3] & (1<<(event.xkey.keycode%8)) )
921                             window->State.KeyRepeating = GL_TRUE;
922                         else
923                             window->State.KeyRepeating = GL_FALSE;
924                     }
925                 }
926             }
927             else
928                 window->State.KeyRepeating = GL_FALSE;
929
930             /* Cease processing this event if it is auto repeated */
931
932             if (window->State.KeyRepeating)
933             {
934                 if (event.type == KeyPress) window->State.KeyRepeating = GL_FALSE;
935                 break;
936             }
937
938             if( event.type == KeyPress )
939             {
940                 keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, Keyboard ));
941                 special_cb  = (FGCBSpecial) ( FETCH_WCB( *window, Special  ));
942             }
943             else
944             {
945                 keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, KeyboardUp ));
946                 special_cb  = (FGCBSpecial) ( FETCH_WCB( *window, SpecialUp  ));
947             }
948
949             /* Is there a keyboard/special callback hooked for this window? */
950             if( keyboard_cb || special_cb )
951             {
952                 XComposeStatus composeStatus;
953                 char asciiCode[ 32 ];
954                 KeySym keySym;
955                 int len;
956
957                 /* Check for the ASCII/KeySym codes associated with the event: */
958                 len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode),
959                                      &keySym, &composeStatus
960                 );
961
962                 /* GLUT API tells us to have two separate callbacks... */
963                 if( len > 0 )
964                 {
965                     /* ...one for the ASCII translateable keypresses... */
966                     if( keyboard_cb )
967                     {
968                         fgSetWindow( window );
969                         fgState.Modifiers = fgPlatformGetModifiers( event.xkey.state );
970                         keyboard_cb( asciiCode[ 0 ],
971                                      event.xkey.x, event.xkey.y
972                         );
973                         fgState.Modifiers = INVALID_MODIFIERS;
974                     }
975                 }
976                 else
977                 {
978                     int special = -1;
979
980                     /*
981                      * ...and one for all the others, which need to be
982                      * translated to GLUT_KEY_Xs...
983                      */
984                     switch( keySym )
985                     {
986                     case XK_F1:     special = GLUT_KEY_F1;     break;
987                     case XK_F2:     special = GLUT_KEY_F2;     break;
988                     case XK_F3:     special = GLUT_KEY_F3;     break;
989                     case XK_F4:     special = GLUT_KEY_F4;     break;
990                     case XK_F5:     special = GLUT_KEY_F5;     break;
991                     case XK_F6:     special = GLUT_KEY_F6;     break;
992                     case XK_F7:     special = GLUT_KEY_F7;     break;
993                     case XK_F8:     special = GLUT_KEY_F8;     break;
994                     case XK_F9:     special = GLUT_KEY_F9;     break;
995                     case XK_F10:    special = GLUT_KEY_F10;    break;
996                     case XK_F11:    special = GLUT_KEY_F11;    break;
997                     case XK_F12:    special = GLUT_KEY_F12;    break;
998
999                     case XK_KP_Left:
1000                     case XK_Left:   special = GLUT_KEY_LEFT;   break;
1001                     case XK_KP_Right:
1002                     case XK_Right:  special = GLUT_KEY_RIGHT;  break;
1003                     case XK_KP_Up:
1004                     case XK_Up:     special = GLUT_KEY_UP;     break;
1005                     case XK_KP_Down:
1006                     case XK_Down:   special = GLUT_KEY_DOWN;   break;
1007
1008                     case XK_KP_Prior:
1009                     case XK_Prior:  special = GLUT_KEY_PAGE_UP; break;
1010                     case XK_KP_Next:
1011                     case XK_Next:   special = GLUT_KEY_PAGE_DOWN; break;
1012                     case XK_KP_Home:
1013                     case XK_Home:   special = GLUT_KEY_HOME;   break;
1014                     case XK_KP_End:
1015                     case XK_End:    special = GLUT_KEY_END;    break;
1016                     case XK_KP_Insert:
1017                     case XK_Insert: special = GLUT_KEY_INSERT; break;
1018
1019                     case XK_Num_Lock :  special = GLUT_KEY_NUM_LOCK;  break;
1020                     case XK_KP_Begin :  special = GLUT_KEY_BEGIN;     break;
1021                     case XK_KP_Delete:  special = GLUT_KEY_DELETE;    break;
1022
1023                     case XK_Shift_L:   special = GLUT_KEY_SHIFT_L;    break;
1024                     case XK_Shift_R:   special = GLUT_KEY_SHIFT_R;    break;
1025                     case XK_Control_L: special = GLUT_KEY_CTRL_L;     break;
1026                     case XK_Control_R: special = GLUT_KEY_CTRL_R;     break;
1027                     case XK_Alt_L:     special = GLUT_KEY_ALT_L;      break;
1028                     case XK_Alt_R:     special = GLUT_KEY_ALT_R;      break;
1029                     }
1030
1031                     /*
1032                      * Execute the callback (if one has been specified),
1033                      * given that the special code seems to be valid...
1034                      */
1035                     if( special_cb && (special != -1) )
1036                     {
1037                         fgSetWindow( window );
1038                         fgState.Modifiers = fgPlatformGetModifiers( event.xkey.state );
1039                         special_cb( special, event.xkey.x, event.xkey.y );
1040                         fgState.Modifiers = INVALID_MODIFIERS;
1041                     }
1042                 }
1043             }
1044         }
1045         break;
1046
1047         case ReparentNotify:
1048             break; /* XXX Should disable this event */
1049
1050         /* Not handled */
1051         case GravityNotify:
1052             break;
1053
1054         default:
1055             /* enter handling of Extension Events here */
1056             #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
1057                 fgHandleExtensionEvents( &event );
1058             #endif
1059             break;
1060         }
1061     }
1062 }
1063
1064
1065 static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg)
1066 {
1067     return xev->type == MotionNotify;
1068 }
1069
1070 void fgPlatformMainLoopPreliminaryWork ( void )
1071 {
1072 }
1073