4 * The X11-specific windows message processing methods.
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
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:
18 * The above copyright notice and this permission notice shall be included
19 * in all copies or substantial portions of the Software.
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.
29 #include <GL/freeglut.h>
30 #include "../fg_internal.h"
38 * Try to get the maximum value allowed for ints, falling back to the minimum
39 * guaranteed by ISO C99 if there is no suitable header.
45 # define INT_MAX 32767
49 # define MIN(a,b) (((a)<(b)) ? (a) : (b))
52 extern void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify);
53 extern void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify);
54 extern void fgPlatformFullScreenToggle( SFG_Window *win );
55 extern void fgPlatformPositionWindow( SFG_Window *window, int x, int y );
56 extern void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height );
57 extern void fgPlatformPushWindow( SFG_Window *window );
58 extern void fgPlatformPopWindow( SFG_Window *window );
59 extern void fgPlatformHideWindow( SFG_Window *window );
60 extern void fgPlatformIconifyWindow( SFG_Window *window );
61 extern void fgPlatformShowWindow( SFG_Window *window );
63 /* used in the event handling code to match and discard stale mouse motion events */
64 static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg);
67 * TODO BEFORE THE STABLE RELEASE:
69 * There are some issues concerning window redrawing under X11, and maybe
70 * some events are not handled.
72 * Need to investigate why the X11 version breaks out with an error when
73 * closing a window (using the window manager, not glutDestroyWindow)...
78 fg_time_t fgPlatformSystemTime ( void )
80 #ifdef CLOCK_MONOTONIC
82 clock_gettime(CLOCK_MONOTONIC, &now);
83 return now.tv_nsec/1000000 + now.tv_sec*1000;
84 #elif defined(HAVE_GETTIMEOFDAY)
86 gettimeofday( &now, NULL );
87 return now.tv_usec/1000 + now.tv_sec*1000;
92 * Does the magic required to relinquish the CPU until something interesting
96 void fgPlatformSleepForEvents( fg_time_t msec )
99 * Possibly due to aggressive use of XFlush() and friends,
100 * it is possible to have our socket drained but still have
101 * unprocessed events. (Or, this may just be normal with
102 * X, anyway?) We do non-trivial processing of X events
103 * after the event-reading loop, in any case, so we
104 * need to allow that we may have an empty socket but non-
107 if( ! XPending( fgDisplay.pDisplay.Display ) )
114 socket = ConnectionNumber( fgDisplay.pDisplay.Display );
116 FD_SET( socket, &fdset );
117 wait.tv_sec = msec / 1000;
118 wait.tv_usec = (msec % 1000) * 1000;
119 err = select( socket+1, &fdset, NULL, NULL, &wait );
122 if( ( -1 == err ) && ( errno != EINTR ) )
123 fgWarning ( "freeglut select() error: %d", errno );
130 * Returns GLUT modifier mask for the state field of an X11 event.
132 int fgPlatformGetModifiers( int state )
136 if( state & ( ShiftMask | LockMask ) )
137 ret |= GLUT_ACTIVE_SHIFT;
138 if( state & ControlMask )
139 ret |= GLUT_ACTIVE_CTRL;
140 if( state & Mod1Mask )
141 ret |= GLUT_ACTIVE_ALT;
146 static const char* fghTypeToString( int type )
149 case KeyPress: return "KeyPress";
150 case KeyRelease: return "KeyRelease";
151 case ButtonPress: return "ButtonPress";
152 case ButtonRelease: return "ButtonRelease";
153 case MotionNotify: return "MotionNotify";
154 case EnterNotify: return "EnterNotify";
155 case LeaveNotify: return "LeaveNotify";
156 case FocusIn: return "FocusIn";
157 case FocusOut: return "FocusOut";
158 case KeymapNotify: return "KeymapNotify";
159 case Expose: return "Expose";
160 case GraphicsExpose: return "GraphicsExpose";
161 case NoExpose: return "NoExpose";
162 case VisibilityNotify: return "VisibilityNotify";
163 case CreateNotify: return "CreateNotify";
164 case DestroyNotify: return "DestroyNotify";
165 case UnmapNotify: return "UnmapNotify";
166 case MapNotify: return "MapNotify";
167 case MapRequest: return "MapRequest";
168 case ReparentNotify: return "ReparentNotify";
169 case ConfigureNotify: return "ConfigureNotify";
170 case ConfigureRequest: return "ConfigureRequest";
171 case GravityNotify: return "GravityNotify";
172 case ResizeRequest: return "ResizeRequest";
173 case CirculateNotify: return "CirculateNotify";
174 case CirculateRequest: return "CirculateRequest";
175 case PropertyNotify: return "PropertyNotify";
176 case SelectionClear: return "SelectionClear";
177 case SelectionRequest: return "SelectionRequest";
178 case SelectionNotify: return "SelectionNotify";
179 case ColormapNotify: return "ColormapNotify";
180 case ClientMessage: return "ClientMessage";
181 case MappingNotify: return "MappingNotify";
182 default: return "UNKNOWN";
186 static const char* fghBoolToString( Bool b )
188 return b == False ? "False" : "True";
191 static const char* fghNotifyHintToString( char is_hint )
194 case NotifyNormal: return "NotifyNormal";
195 case NotifyHint: return "NotifyHint";
196 default: return "UNKNOWN";
200 static const char* fghNotifyModeToString( int mode )
203 case NotifyNormal: return "NotifyNormal";
204 case NotifyGrab: return "NotifyGrab";
205 case NotifyUngrab: return "NotifyUngrab";
206 case NotifyWhileGrabbed: return "NotifyWhileGrabbed";
207 default: return "UNKNOWN";
211 static const char* fghNotifyDetailToString( int detail )
214 case NotifyAncestor: return "NotifyAncestor";
215 case NotifyVirtual: return "NotifyVirtual";
216 case NotifyInferior: return "NotifyInferior";
217 case NotifyNonlinear: return "NotifyNonlinear";
218 case NotifyNonlinearVirtual: return "NotifyNonlinearVirtual";
219 case NotifyPointer: return "NotifyPointer";
220 case NotifyPointerRoot: return "NotifyPointerRoot";
221 case NotifyDetailNone: return "NotifyDetailNone";
222 default: return "UNKNOWN";
226 static const char* fghVisibilityToString( int state ) {
228 case VisibilityUnobscured: return "VisibilityUnobscured";
229 case VisibilityPartiallyObscured: return "VisibilityPartiallyObscured";
230 case VisibilityFullyObscured: return "VisibilityFullyObscured";
231 default: return "UNKNOWN";
235 static const char* fghConfigureDetailToString( int detail )
238 case Above: return "Above";
239 case Below: return "Below";
240 case TopIf: return "TopIf";
241 case BottomIf: return "BottomIf";
242 case Opposite: return "Opposite";
243 default: return "UNKNOWN";
247 static const char* fghPlaceToString( int place )
250 case PlaceOnTop: return "PlaceOnTop";
251 case PlaceOnBottom: return "PlaceOnBottom";
252 default: return "UNKNOWN";
256 static const char* fghMappingRequestToString( int request )
259 case MappingModifier: return "MappingModifier";
260 case MappingKeyboard: return "MappingKeyboard";
261 case MappingPointer: return "MappingPointer";
262 default: return "UNKNOWN";
266 static const char* fghPropertyStateToString( int state )
269 case PropertyNewValue: return "PropertyNewValue";
270 case PropertyDelete: return "PropertyDelete";
271 default: return "UNKNOWN";
275 static const char* fghColormapStateToString( int state )
278 case ColormapUninstalled: return "ColormapUninstalled";
279 case ColormapInstalled: return "ColormapInstalled";
280 default: return "UNKNOWN";
284 __fg_unused static void fghPrintEvent( XEvent *event )
286 switch( event->type ) {
290 XKeyEvent *e = &event->xkey;
291 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
292 "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
293 "keycode=%u, same_screen=%s", fghTypeToString( e->type ),
294 e->window, e->root, e->subwindow, (unsigned long)e->time,
295 e->x, e->y, e->x_root, e->y_root, e->state, e->keycode,
296 fghBoolToString( e->same_screen ) );
301 case ButtonRelease: {
302 XButtonEvent *e = &event->xbutton;
303 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
304 "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
305 "button=%u, same_screen=%d", fghTypeToString( e->type ),
306 e->window, e->root, e->subwindow, (unsigned long)e->time,
307 e->x, e->y, e->x_root, e->y_root, e->state, e->button,
308 fghBoolToString( e->same_screen ) );
313 XMotionEvent *e = &event->xmotion;
314 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
315 "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
316 "is_hint=%s, same_screen=%d", fghTypeToString( e->type ),
317 e->window, e->root, e->subwindow, (unsigned long)e->time,
318 e->x, e->y, e->x_root, e->y_root, e->state,
319 fghNotifyHintToString( e->is_hint ),
320 fghBoolToString( e->same_screen ) );
326 XCrossingEvent *e = &event->xcrossing;
327 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
328 "(x,y)=(%d,%d), mode=%s, detail=%s, same_screen=%d, "
329 "focus=%d, state=0x%x", fghTypeToString( e->type ),
330 e->window, e->root, e->subwindow, (unsigned long)e->time,
331 e->x, e->y, fghNotifyModeToString( e->mode ),
332 fghNotifyDetailToString( e->detail ), (int)e->same_screen,
333 (int)e->focus, e->state );
339 XFocusChangeEvent *e = &event->xfocus;
340 fgWarning( "%s: window=0x%x, mode=%s, detail=%s",
341 fghTypeToString( e->type ), e->window,
342 fghNotifyModeToString( e->mode ),
343 fghNotifyDetailToString( e->detail ) );
348 XKeymapEvent *e = &event->xkeymap;
349 char buf[32 * 2 + 1];
351 for ( i = 0; i < 32; i++ ) {
352 snprintf( &buf[ i * 2 ], sizeof( buf ) - i * 2,
353 "%02x", e->key_vector[ i ] );
356 fgWarning( "%s: window=0x%x, %s", fghTypeToString( e->type ), e->window,
362 XExposeEvent *e = &event->xexpose;
363 fgWarning( "%s: window=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), "
364 "count=%d", fghTypeToString( e->type ), e->window, e->x,
365 e->y, e->width, e->height, e->count );
369 case GraphicsExpose: {
370 XGraphicsExposeEvent *e = &event->xgraphicsexpose;
371 fgWarning( "%s: drawable=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), "
372 "count=%d, (major_code,minor_code)=(%d,%d)",
373 fghTypeToString( e->type ), e->drawable, e->x, e->y,
374 e->width, e->height, e->count, e->major_code,
380 XNoExposeEvent *e = &event->xnoexpose;
381 fgWarning( "%s: drawable=0x%x, (major_code,minor_code)=(%d,%d)",
382 fghTypeToString( e->type ), e->drawable, e->major_code,
387 case VisibilityNotify: {
388 XVisibilityEvent *e = &event->xvisibility;
389 fgWarning( "%s: window=0x%x, state=%s", fghTypeToString( e->type ),
390 e->window, fghVisibilityToString( e->state) );
395 XCreateWindowEvent *e = &event->xcreatewindow;
396 fgWarning( "%s: (x,y)=(%d,%d), (width,height)=(%d,%d), border_width=%d, "
397 "window=0x%x, override_redirect=%s",
398 fghTypeToString( e->type ), e->x, e->y, e->width, e->height,
399 e->border_width, e->window,
400 fghBoolToString( e->override_redirect ) );
404 case DestroyNotify: {
405 XDestroyWindowEvent *e = &event->xdestroywindow;
406 fgWarning( "%s: event=0x%x, window=0x%x",
407 fghTypeToString( e->type ), e->event, e->window );
412 XUnmapEvent *e = &event->xunmap;
413 fgWarning( "%s: event=0x%x, window=0x%x, from_configure=%s",
414 fghTypeToString( e->type ), e->event, e->window,
415 fghBoolToString( e->from_configure ) );
420 XMapEvent *e = &event->xmap;
421 fgWarning( "%s: event=0x%x, window=0x%x, override_redirect=%s",
422 fghTypeToString( e->type ), e->event, e->window,
423 fghBoolToString( e->override_redirect ) );
428 XMapRequestEvent *e = &event->xmaprequest;
429 fgWarning( "%s: parent=0x%x, window=0x%x",
430 fghTypeToString( event->type ), e->parent, e->window );
434 case ReparentNotify: {
435 XReparentEvent *e = &event->xreparent;
436 fgWarning( "%s: event=0x%x, window=0x%x, parent=0x%x, (x,y)=(%d,%d), "
437 "override_redirect=%s", fghTypeToString( e->type ),
438 e->event, e->window, e->parent, e->x, e->y,
439 fghBoolToString( e->override_redirect ) );
443 case ConfigureNotify: {
444 XConfigureEvent *e = &event->xconfigure;
445 fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d), "
446 "(width,height)=(%d,%d), border_width=%d, above=0x%x, "
447 "override_redirect=%s", fghTypeToString( e->type ), e->event,
448 e->window, e->x, e->y, e->width, e->height, e->border_width,
449 e->above, fghBoolToString( e->override_redirect ) );
453 case ConfigureRequest: {
454 XConfigureRequestEvent *e = &event->xconfigurerequest;
455 fgWarning( "%s: parent=0x%x, window=0x%x, (x,y)=(%d,%d), "
456 "(width,height)=(%d,%d), border_width=%d, above=0x%x, "
457 "detail=%s, value_mask=%lx", fghTypeToString( e->type ),
458 e->parent, e->window, e->x, e->y, e->width, e->height,
459 e->border_width, e->above,
460 fghConfigureDetailToString( e->detail ), e->value_mask );
464 case GravityNotify: {
465 XGravityEvent *e = &event->xgravity;
466 fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d)",
467 fghTypeToString( e->type ), e->event, e->window, e->x, e->y );
471 case ResizeRequest: {
472 XResizeRequestEvent *e = &event->xresizerequest;
473 fgWarning( "%s: window=0x%x, (width,height)=(%d,%d)",
474 fghTypeToString( e->type ), e->window, e->width, e->height );
478 case CirculateNotify: {
479 XCirculateEvent *e = &event->xcirculate;
480 fgWarning( "%s: event=0x%x, window=0x%x, place=%s",
481 fghTypeToString( e->type ), e->event, e->window,
482 fghPlaceToString( e->place ) );
486 case CirculateRequest: {
487 XCirculateRequestEvent *e = &event->xcirculaterequest;
488 fgWarning( "%s: parent=0x%x, window=0x%x, place=%s",
489 fghTypeToString( e->type ), e->parent, e->window,
490 fghPlaceToString( e->place ) );
494 case PropertyNotify: {
495 XPropertyEvent *e = &event->xproperty;
496 fgWarning( "%s: window=0x%x, atom=%lu, time=%lu, state=%s",
497 fghTypeToString( e->type ), e->window,
498 (unsigned long)e->atom, (unsigned long)e->time,
499 fghPropertyStateToString( e->state ) );
503 case SelectionClear: {
504 XSelectionClearEvent *e = &event->xselectionclear;
505 fgWarning( "%s: window=0x%x, selection=%lu, time=%lu",
506 fghTypeToString( e->type ), e->window,
507 (unsigned long)e->selection, (unsigned long)e->time );
511 case SelectionRequest: {
512 XSelectionRequestEvent *e = &event->xselectionrequest;
513 fgWarning( "%s: owner=0x%x, requestor=0x%x, selection=0x%x, "
514 "target=0x%x, property=%lu, time=%lu",
515 fghTypeToString( e->type ), e->owner, e->requestor,
516 (unsigned long)e->selection, (unsigned long)e->target,
517 (unsigned long)e->property, (unsigned long)e->time );
521 case SelectionNotify: {
522 XSelectionEvent *e = &event->xselection;
523 fgWarning( "%s: requestor=0x%x, selection=0x%x, target=0x%x, "
524 "property=%lu, time=%lu", fghTypeToString( e->type ),
525 e->requestor, (unsigned long)e->selection,
526 (unsigned long)e->target, (unsigned long)e->property,
527 (unsigned long)e->time );
531 case ColormapNotify: {
532 XColormapEvent *e = &event->xcolormap;
533 fgWarning( "%s: window=0x%x, colormap=%lu, new=%s, state=%s",
534 fghTypeToString( e->type ), e->window,
535 (unsigned long)e->colormap, fghBoolToString( e->new ),
536 fghColormapStateToString( e->state ) );
540 case ClientMessage: {
541 XClientMessageEvent *e = &event->xclient;
544 char* end = buf + sizeof( buf );
546 switch( e->format ) {
548 for ( i = 0; i < 20; i++, p += 3 ) {
549 snprintf( p, end - p, " %02x", e->data.b[ i ] );
553 for ( i = 0; i < 10; i++, p += 5 ) {
554 snprintf( p, end - p, " %04x", e->data.s[ i ] );
558 for ( i = 0; i < 5; i++, p += 9 ) {
559 snprintf( p, end - p, " %08lx", e->data.l[ i ] );
564 fgWarning( "%s: window=0x%x, message_type=%lu, format=%d, data=(%s )",
565 fghTypeToString( e->type ), e->window,
566 (unsigned long)e->message_type, e->format, buf );
570 case MappingNotify: {
571 XMappingEvent *e = &event->xmapping;
572 fgWarning( "%s: window=0x%x, request=%s, first_keycode=%d, count=%d",
573 fghTypeToString( e->type ), e->window,
574 fghMappingRequestToString( e->request ), e->first_keycode,
580 fgWarning( "%s", fghTypeToString( event->type ) );
587 void fgPlatformProcessSingleEvent ( void )
592 /* This code was repeated constantly, so here it goes into a definition: */
593 #define GETWINDOW(a) \
594 window = fgWindowByHandle( event.a.window ); \
595 if( window == NULL ) \
598 #define GETMOUSE(a) \
599 window->State.MouseX = event.a.x; \
600 window->State.MouseY = event.a.y;
602 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
604 while( XPending( fgDisplay.pDisplay.Display ) )
606 XNextEvent( fgDisplay.pDisplay.Display, &event );
608 fghPrintEvent( &event );
614 if (fgStructure.CurrentWindow)
615 if(fgIsSpaceballXEvent(&event)) {
616 fgSpaceballHandleXEvent(&event);
619 /* Destroy the window when the WM_DELETE_WINDOW message arrives */
620 if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.pDisplay.DeleteWindow )
622 GETWINDOW( xclient );
624 fgDestroyWindow ( window );
626 if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
631 else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
632 fgState.ExecState = GLUT_EXEC_STATE_STOP;
639 * CreateNotify causes a configure-event so that sub-windows are
640 * handled compatibly with GLUT. Otherwise, your sub-windows
641 * (in freeglut only) will not get an initial reshape event,
642 * which can break things.
644 * GLUT presumably does this because it generally tries to treat
645 * sub-windows the same as windows.
648 case ConfigureNotify:
650 int width, height, x, y;
651 if( event.type == CreateNotify ) {
652 GETWINDOW( xcreatewindow );
653 width = event.xcreatewindow.width;
654 height = event.xcreatewindow.height;
655 x = event.xcreatewindow.x;
656 y = event.xcreatewindow.y;
658 GETWINDOW( xconfigure );
659 width = event.xconfigure.width;
660 height = event.xconfigure.height;
661 x = event.xconfigure.x;
662 y = event.xconfigure.y;
665 /* Update state and call callback, if there was a change */
666 fghOnPositionNotify(window, x, y, GL_FALSE);
667 /* Update state and call callback, if there was a change */
668 fghOnReshapeNotify(window, width, height, GL_FALSE);
674 * This is sent to confirm the XDestroyWindow call.
676 * XXX WHY is this commented out? Should we re-enable it?
678 /* fgAddToWindowDestroyList ( window ); */
683 * We are too dumb to process partial exposes...
685 * XXX Well, we could do it. However, it seems to only
686 * XXX be potentially useful for single-buffered (since
687 * XXX double-buffered does not respect viewport when we
688 * XXX do a buffer-swap).
691 if( event.xexpose.count == 0 )
693 GETWINDOW( xexpose );
694 window->State.WorkMask |= GLUT_DISPLAY_WORK;
702 /* We get this when iconifying a window. */
704 INVOKE_WCB( *window, WindowStatus, ( GLUT_HIDDEN ) );
705 window->State.Visible = GL_FALSE;
710 * Have the client's keyboard knowledge updated (xlib.ps,
711 * page 206, says that's a good thing to do)
713 XRefreshKeyboardMapping( (XMappingEvent *) &event );
716 case VisibilityNotify:
719 * Sending this event, the X server can notify us that the window
720 * has just acquired one of the three possible visibility states:
721 * VisibilityUnobscured, VisibilityPartiallyObscured or
722 * VisibilityFullyObscured. Note that we DO NOT receive a
723 * VisibilityNotify event when iconifying a window, we only get an
726 GETWINDOW( xvisibility );
727 switch( event.xvisibility.state )
729 case VisibilityUnobscured:
730 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_RETAINED ) );
731 window->State.Visible = GL_TRUE;
734 case VisibilityPartiallyObscured:
735 INVOKE_WCB( *window, WindowStatus,
736 ( GLUT_PARTIALLY_RETAINED ) );
737 window->State.Visible = GL_TRUE;
740 case VisibilityFullyObscured:
741 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_COVERED ) );
742 window->State.Visible = GL_FALSE;
746 fgWarning( "Unknown X visibility state: %d",
747 event.xvisibility.state );
755 GETWINDOW( xcrossing );
756 GETMOUSE( xcrossing );
757 if( ( event.type == LeaveNotify ) && window->IsMenu &&
758 window->ActiveMenu && window->ActiveMenu->IsActive )
759 fgUpdateMenuHighlight( window->ActiveMenu );
761 INVOKE_WCB( *window, Entry, ( ( EnterNotify == event.type ) ?
768 /* if GLUT_SKIP_STALE_MOTION_EVENTS is true, then discard all but
769 * the last motion event from the queue
771 if(fgState.SkipStaleMotion) {
772 while(XCheckIfEvent(fgDisplay.pDisplay.Display, &event, match_motion, 0));
775 GETWINDOW( xmotion );
778 if( window->ActiveMenu )
780 if( window == window->ActiveMenu->ParentWindow )
782 window->ActiveMenu->Window->State.MouseX =
783 event.xmotion.x_root - window->ActiveMenu->X;
784 window->ActiveMenu->Window->State.MouseY =
785 event.xmotion.y_root - window->ActiveMenu->Y;
788 fgUpdateMenuHighlight( window->ActiveMenu );
794 * XXX For more than 5 buttons, just check {event.xmotion.state},
795 * XXX rather than a host of bit-masks? Or maybe we need to
796 * XXX track ButtonPress/ButtonRelease events in our own
799 fgState.Modifiers = fgPlatformGetModifiers( event.xmotion.state );
800 if ( event.xmotion.state & ( Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask ) ) {
801 INVOKE_WCB( *window, Motion, ( event.xmotion.x,
804 INVOKE_WCB( *window, Passive, ( event.xmotion.x,
807 fgState.Modifiers = INVALID_MODIFIERS;
814 GLboolean pressed = GL_TRUE;
817 if( event.type == ButtonRelease )
821 * A mouse button has been pressed or released. Traditionally,
822 * break if the window was found within the freeglut structures.
824 GETWINDOW( xbutton );
828 * An X button (at least in XFree86) is numbered from 1.
829 * A GLUT button is numbered from 0.
830 * Old GLUT passed through buttons other than just the first
831 * three, though it only gave symbolic names and official
832 * support to the first three.
834 button = event.xbutton.button - 1;
837 * Do not execute the application's mouse callback if a menu
838 * is hooked to this button. In that case an appropriate
839 * private call should be generated.
841 if( fgCheckActiveMenu( window, button, pressed,
842 event.xbutton.x, event.xbutton.y ) )
846 * Check if there is a mouse or mouse wheel callback hooked to the
849 if( ! FETCH_WCB( *window, Mouse ) &&
850 ! FETCH_WCB( *window, MouseWheel ) )
853 fgState.Modifiers = fgPlatformGetModifiers( event.xbutton.state );
855 /* Finally execute the mouse or mouse wheel callback */
856 if( ( button < glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS ) ) || ( ! FETCH_WCB( *window, MouseWheel ) ) )
857 INVOKE_WCB( *window, Mouse, ( button,
858 pressed ? GLUT_DOWN : GLUT_UP,
865 * Map 4 and 5 to wheel zero; EVEN to +1, ODD to -1
866 * " 6 and 7 " " one; ...
868 * XXX This *should* be behind some variables/macros,
869 * XXX since the order and numbering isn't certain
870 * XXX See XFree86 configuration docs (even back in the
871 * XXX 3.x days, and especially with 4.x).
873 * XXX Note that {button} has already been decremented
874 * XXX in mapping from X button numbering to GLUT.
876 * XXX Should add support for partial wheel turns as Windows does -- 5/27/11
878 int wheel_number = (button - glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS )) / 2;
884 INVOKE_WCB( *window, MouseWheel, ( wheel_number,
890 fgState.Modifiers = INVALID_MODIFIERS;
897 FGCBKeyboard keyboard_cb;
898 FGCBSpecial special_cb;
903 /* Detect auto repeated keys, if configured globally or per-window */
905 if ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE )
907 if (event.type==KeyRelease)
910 * Look at X11 keystate to detect repeat mode.
911 * While X11 says the key is actually held down, we'll ignore KeyRelease/KeyPress pairs.
915 XQueryKeymap( fgDisplay.pDisplay.Display, keys ); /* Look at X11 keystate to detect repeat mode */
917 if ( event.xkey.keycode<256 ) /* XQueryKeymap is limited to 256 keycodes */
919 if ( keys[event.xkey.keycode>>3] & (1<<(event.xkey.keycode%8)) )
920 window->State.pWState.KeyRepeating = GL_TRUE;
922 window->State.pWState.KeyRepeating = GL_FALSE;
927 window->State.pWState.KeyRepeating = GL_FALSE;
929 /* Cease processing this event if it is auto repeated */
931 if (window->State.pWState.KeyRepeating)
933 if (event.type == KeyPress) window->State.pWState.KeyRepeating = GL_FALSE;
937 if( event.type == KeyPress )
939 keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, Keyboard ));
940 special_cb = (FGCBSpecial) ( FETCH_WCB( *window, Special ));
944 keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, KeyboardUp ));
945 special_cb = (FGCBSpecial) ( FETCH_WCB( *window, SpecialUp ));
948 /* Is there a keyboard/special callback hooked for this window? */
949 if( keyboard_cb || special_cb )
951 XComposeStatus composeStatus;
952 char asciiCode[ 32 ];
956 /* Check for the ASCII/KeySym codes associated with the event: */
957 len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode),
958 &keySym, &composeStatus
961 /* GLUT API tells us to have two separate callbacks... */
964 /* ...one for the ASCII translateable keypresses... */
967 fgSetWindow( window );
968 fgState.Modifiers = fgPlatformGetModifiers( event.xkey.state );
969 keyboard_cb( asciiCode[ 0 ],
970 event.xkey.x, event.xkey.y
972 fgState.Modifiers = INVALID_MODIFIERS;
980 * ...and one for all the others, which need to be
981 * translated to GLUT_KEY_Xs...
985 case XK_F1: special = GLUT_KEY_F1; break;
986 case XK_F2: special = GLUT_KEY_F2; break;
987 case XK_F3: special = GLUT_KEY_F3; break;
988 case XK_F4: special = GLUT_KEY_F4; break;
989 case XK_F5: special = GLUT_KEY_F5; break;
990 case XK_F6: special = GLUT_KEY_F6; break;
991 case XK_F7: special = GLUT_KEY_F7; break;
992 case XK_F8: special = GLUT_KEY_F8; break;
993 case XK_F9: special = GLUT_KEY_F9; break;
994 case XK_F10: special = GLUT_KEY_F10; break;
995 case XK_F11: special = GLUT_KEY_F11; break;
996 case XK_F12: special = GLUT_KEY_F12; break;
999 case XK_Left: special = GLUT_KEY_LEFT; break;
1001 case XK_Right: special = GLUT_KEY_RIGHT; break;
1003 case XK_Up: special = GLUT_KEY_UP; break;
1005 case XK_Down: special = GLUT_KEY_DOWN; break;
1008 case XK_Prior: special = GLUT_KEY_PAGE_UP; break;
1010 case XK_Next: special = GLUT_KEY_PAGE_DOWN; break;
1012 case XK_Home: special = GLUT_KEY_HOME; break;
1014 case XK_End: special = GLUT_KEY_END; break;
1016 case XK_Insert: special = GLUT_KEY_INSERT; break;
1018 case XK_Num_Lock : special = GLUT_KEY_NUM_LOCK; break;
1019 case XK_KP_Begin : special = GLUT_KEY_BEGIN; break;
1020 case XK_KP_Delete: special = GLUT_KEY_DELETE; break;
1022 case XK_Shift_L: special = GLUT_KEY_SHIFT_L; break;
1023 case XK_Shift_R: special = GLUT_KEY_SHIFT_R; break;
1024 case XK_Control_L: special = GLUT_KEY_CTRL_L; break;
1025 case XK_Control_R: special = GLUT_KEY_CTRL_R; break;
1026 case XK_Alt_L: special = GLUT_KEY_ALT_L; break;
1027 case XK_Alt_R: special = GLUT_KEY_ALT_R; break;
1031 * Execute the callback (if one has been specified),
1032 * given that the special code seems to be valid...
1034 if( special_cb && (special != -1) )
1036 fgSetWindow( window );
1037 fgState.Modifiers = fgPlatformGetModifiers( event.xkey.state );
1038 special_cb( special, event.xkey.x, event.xkey.y );
1039 fgState.Modifiers = INVALID_MODIFIERS;
1046 case ReparentNotify:
1047 break; /* XXX Should disable this event */
1054 /* enter handling of Extension Events here */
1055 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
1056 fgHandleExtensionEvents( &event );
1064 static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg)
1066 return xev->type == MotionNotify;
1069 void fgPlatformMainLoopPreliminaryWork ( void )
1074 /* deal with work list items */
1075 void fgPlatformInitWork(SFG_Window* window)
1077 /* Notify windowStatus/visibility, position and size get notified on window creation with message handlers above
1078 * XXX CHECK: do the messages happen too early like on windows, so client code cannot have registered
1079 * a callback yet and the message is thus never received by client?
1080 * -> this is a no-op
1085 void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
1087 if (workMask & GLUT_FULL_SCREEN_WORK)
1088 fgPlatformFullScreenToggle( window );
1089 if (workMask & GLUT_POSITION_WORK)
1090 fgPlatformPositionWindow( window, window->State.DesiredXpos, window->State.DesiredYpos );
1091 if (workMask & GLUT_SIZE_WORK)
1092 fgPlatformReshapeWindow ( window, window->State.DesiredWidth, window->State.DesiredHeight );
1093 if (workMask & GLUT_ZORDER_WORK)
1095 if (window->State.DesiredZOrder < 0)
1096 fgPlatformPushWindow( window );
1098 fgPlatformPopWindow( window );
1102 void fgPlatformVisibilityWork(SFG_Window* window)
1104 /* Visibility status of window gets updated in the window message handlers above
1105 * XXX: is this really the case? check
1107 SFG_Window *win = window;
1108 switch (window->State.DesiredVisibility)
1110 case DesireHiddenState:
1111 fgPlatformHideWindow( window );
1113 case DesireIconicState:
1114 /* Call on top-level window */
1117 fgPlatformIconifyWindow( win );
1119 case DesireNormalState:
1120 fgPlatformShowWindow( window );