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