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