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