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