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