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