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"
36 * Try to get the maximum value allowed for ints, falling back to the minimum
37 * guaranteed by ISO C99 if there is no suitable header.
43 # define INT_MAX 32767
47 # define MIN(a,b) (((a)<(b)) ? (a) : (b))
50 extern void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify);
51 extern void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify);
52 extern void fgPlatformFullScreenToggle( SFG_Window *win );
53 extern void fgPlatformPositionWindow( SFG_Window *window, int x, int y );
54 extern void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height );
55 extern void fgPlatformPushWindow( SFG_Window *window );
56 extern void fgPlatformPopWindow( SFG_Window *window );
57 extern void fgPlatformHideWindow( SFG_Window *window );
58 extern void fgPlatformIconifyWindow( SFG_Window *window );
59 extern void fgPlatformShowWindow( SFG_Window *window );
61 /* used in the event handling code to match and discard stale mouse motion events */
62 static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg);
65 * TODO BEFORE THE STABLE RELEASE:
67 * There are some issues concerning window redrawing under X11, and maybe
68 * some events are not handled.
70 * Need to investigate why the X11 version breaks out with an error when
71 * closing a window (using the window manager, not glutDestroyWindow)...
76 fg_time_t fgPlatformSystemTime ( void )
78 #ifdef CLOCK_MONOTONIC
80 clock_gettime(CLOCK_MONOTONIC, &now);
81 return now.tv_nsec/1000000 + now.tv_sec*1000;
82 #elif defined(HAVE_GETTIMEOFDAY)
84 gettimeofday( &now, NULL );
85 return now.tv_usec/1000 + now.tv_sec*1000;
90 * Does the magic required to relinquish the CPU until something interesting
94 void fgPlatformSleepForEvents( fg_time_t msec )
97 * Possibly due to aggressive use of XFlush() and friends,
98 * it is possible to have our socket drained but still have
99 * unprocessed events. (Or, this may just be normal with
100 * X, anyway?) We do non-trivial processing of X events
101 * after the event-reading loop, in any case, so we
102 * need to allow that we may have an empty socket but non-
105 if( ! XPending( fgDisplay.pDisplay.Display ) )
112 socket = ConnectionNumber( fgDisplay.pDisplay.Display );
114 FD_SET( socket, &fdset );
115 wait.tv_sec = msec / 1000;
116 wait.tv_usec = (msec % 1000) * 1000;
117 err = select( socket+1, &fdset, NULL, NULL, &wait );
119 if( ( -1 == err ) && ( errno != EINTR ) )
120 fgWarning ( "freeglut select() error: %d", errno );
126 * Returns GLUT modifier mask for the state field of an X11 event.
128 int fgPlatformGetModifiers( int state )
132 if( state & ( ShiftMask | LockMask ) )
133 ret |= GLUT_ACTIVE_SHIFT;
134 if( state & ControlMask )
135 ret |= GLUT_ACTIVE_CTRL;
136 if( state & Mod1Mask )
137 ret |= GLUT_ACTIVE_ALT;
142 static const char* fghTypeToString( int type )
145 case KeyPress: return "KeyPress";
146 case KeyRelease: return "KeyRelease";
147 case ButtonPress: return "ButtonPress";
148 case ButtonRelease: return "ButtonRelease";
149 case MotionNotify: return "MotionNotify";
150 case EnterNotify: return "EnterNotify";
151 case LeaveNotify: return "LeaveNotify";
152 case FocusIn: return "FocusIn";
153 case FocusOut: return "FocusOut";
154 case KeymapNotify: return "KeymapNotify";
155 case Expose: return "Expose";
156 case GraphicsExpose: return "GraphicsExpose";
157 case NoExpose: return "NoExpose";
158 case VisibilityNotify: return "VisibilityNotify";
159 case CreateNotify: return "CreateNotify";
160 case DestroyNotify: return "DestroyNotify";
161 case UnmapNotify: return "UnmapNotify";
162 case MapNotify: return "MapNotify";
163 case MapRequest: return "MapRequest";
164 case ReparentNotify: return "ReparentNotify";
165 case ConfigureNotify: return "ConfigureNotify";
166 case ConfigureRequest: return "ConfigureRequest";
167 case GravityNotify: return "GravityNotify";
168 case ResizeRequest: return "ResizeRequest";
169 case CirculateNotify: return "CirculateNotify";
170 case CirculateRequest: return "CirculateRequest";
171 case PropertyNotify: return "PropertyNotify";
172 case SelectionClear: return "SelectionClear";
173 case SelectionRequest: return "SelectionRequest";
174 case SelectionNotify: return "SelectionNotify";
175 case ColormapNotify: return "ColormapNotify";
176 case ClientMessage: return "ClientMessage";
177 case MappingNotify: return "MappingNotify";
178 default: return "UNKNOWN";
182 static const char* fghBoolToString( Bool b )
184 return b == False ? "False" : "True";
187 static const char* fghNotifyHintToString( char is_hint )
190 case NotifyNormal: return "NotifyNormal";
191 case NotifyHint: return "NotifyHint";
192 default: return "UNKNOWN";
196 static const char* fghNotifyModeToString( int mode )
199 case NotifyNormal: return "NotifyNormal";
200 case NotifyGrab: return "NotifyGrab";
201 case NotifyUngrab: return "NotifyUngrab";
202 case NotifyWhileGrabbed: return "NotifyWhileGrabbed";
203 default: return "UNKNOWN";
207 static const char* fghNotifyDetailToString( int detail )
210 case NotifyAncestor: return "NotifyAncestor";
211 case NotifyVirtual: return "NotifyVirtual";
212 case NotifyInferior: return "NotifyInferior";
213 case NotifyNonlinear: return "NotifyNonlinear";
214 case NotifyNonlinearVirtual: return "NotifyNonlinearVirtual";
215 case NotifyPointer: return "NotifyPointer";
216 case NotifyPointerRoot: return "NotifyPointerRoot";
217 case NotifyDetailNone: return "NotifyDetailNone";
218 default: return "UNKNOWN";
222 static const char* fghVisibilityToString( int state ) {
224 case VisibilityUnobscured: return "VisibilityUnobscured";
225 case VisibilityPartiallyObscured: return "VisibilityPartiallyObscured";
226 case VisibilityFullyObscured: return "VisibilityFullyObscured";
227 default: return "UNKNOWN";
231 static const char* fghConfigureDetailToString( int detail )
234 case Above: return "Above";
235 case Below: return "Below";
236 case TopIf: return "TopIf";
237 case BottomIf: return "BottomIf";
238 case Opposite: return "Opposite";
239 default: return "UNKNOWN";
243 static const char* fghPlaceToString( int place )
246 case PlaceOnTop: return "PlaceOnTop";
247 case PlaceOnBottom: return "PlaceOnBottom";
248 default: return "UNKNOWN";
252 static const char* fghMappingRequestToString( int request )
255 case MappingModifier: return "MappingModifier";
256 case MappingKeyboard: return "MappingKeyboard";
257 case MappingPointer: return "MappingPointer";
258 default: return "UNKNOWN";
262 static const char* fghPropertyStateToString( int state )
265 case PropertyNewValue: return "PropertyNewValue";
266 case PropertyDelete: return "PropertyDelete";
267 default: return "UNKNOWN";
271 static const char* fghColormapStateToString( int state )
274 case ColormapUninstalled: return "ColormapUninstalled";
275 case ColormapInstalled: return "ColormapInstalled";
276 default: return "UNKNOWN";
280 __fg_unused static void fghPrintEvent( XEvent *event )
282 switch( event->type ) {
286 XKeyEvent *e = &event->xkey;
287 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
288 "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
289 "keycode=%u, same_screen=%s", fghTypeToString( e->type ),
290 e->window, e->root, e->subwindow, (unsigned long)e->time,
291 e->x, e->y, e->x_root, e->y_root, e->state, e->keycode,
292 fghBoolToString( e->same_screen ) );
297 case ButtonRelease: {
298 XButtonEvent *e = &event->xbutton;
299 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
300 "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
301 "button=%u, same_screen=%d", fghTypeToString( e->type ),
302 e->window, e->root, e->subwindow, (unsigned long)e->time,
303 e->x, e->y, e->x_root, e->y_root, e->state, e->button,
304 fghBoolToString( e->same_screen ) );
309 XMotionEvent *e = &event->xmotion;
310 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
311 "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
312 "is_hint=%s, same_screen=%d", fghTypeToString( e->type ),
313 e->window, e->root, e->subwindow, (unsigned long)e->time,
314 e->x, e->y, e->x_root, e->y_root, e->state,
315 fghNotifyHintToString( e->is_hint ),
316 fghBoolToString( e->same_screen ) );
322 XCrossingEvent *e = &event->xcrossing;
323 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
324 "(x,y)=(%d,%d), mode=%s, detail=%s, same_screen=%d, "
325 "focus=%d, state=0x%x", fghTypeToString( e->type ),
326 e->window, e->root, e->subwindow, (unsigned long)e->time,
327 e->x, e->y, fghNotifyModeToString( e->mode ),
328 fghNotifyDetailToString( e->detail ), (int)e->same_screen,
329 (int)e->focus, e->state );
335 XFocusChangeEvent *e = &event->xfocus;
336 fgWarning( "%s: window=0x%x, mode=%s, detail=%s",
337 fghTypeToString( e->type ), e->window,
338 fghNotifyModeToString( e->mode ),
339 fghNotifyDetailToString( e->detail ) );
344 XKeymapEvent *e = &event->xkeymap;
345 char buf[32 * 2 + 1];
347 for ( i = 0; i < 32; i++ ) {
348 snprintf( &buf[ i * 2 ], sizeof( buf ) - i * 2,
349 "%02x", e->key_vector[ i ] );
352 fgWarning( "%s: window=0x%x, %s", fghTypeToString( e->type ), e->window,
358 XExposeEvent *e = &event->xexpose;
359 fgWarning( "%s: window=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), "
360 "count=%d", fghTypeToString( e->type ), e->window, e->x,
361 e->y, e->width, e->height, e->count );
365 case GraphicsExpose: {
366 XGraphicsExposeEvent *e = &event->xgraphicsexpose;
367 fgWarning( "%s: drawable=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), "
368 "count=%d, (major_code,minor_code)=(%d,%d)",
369 fghTypeToString( e->type ), e->drawable, e->x, e->y,
370 e->width, e->height, e->count, e->major_code,
376 XNoExposeEvent *e = &event->xnoexpose;
377 fgWarning( "%s: drawable=0x%x, (major_code,minor_code)=(%d,%d)",
378 fghTypeToString( e->type ), e->drawable, e->major_code,
383 case VisibilityNotify: {
384 XVisibilityEvent *e = &event->xvisibility;
385 fgWarning( "%s: window=0x%x, state=%s", fghTypeToString( e->type ),
386 e->window, fghVisibilityToString( e->state) );
391 XCreateWindowEvent *e = &event->xcreatewindow;
392 fgWarning( "%s: (x,y)=(%d,%d), (width,height)=(%d,%d), border_width=%d, "
393 "window=0x%x, override_redirect=%s",
394 fghTypeToString( e->type ), e->x, e->y, e->width, e->height,
395 e->border_width, e->window,
396 fghBoolToString( e->override_redirect ) );
400 case DestroyNotify: {
401 XDestroyWindowEvent *e = &event->xdestroywindow;
402 fgWarning( "%s: event=0x%x, window=0x%x",
403 fghTypeToString( e->type ), e->event, e->window );
408 XUnmapEvent *e = &event->xunmap;
409 fgWarning( "%s: event=0x%x, window=0x%x, from_configure=%s",
410 fghTypeToString( e->type ), e->event, e->window,
411 fghBoolToString( e->from_configure ) );
416 XMapEvent *e = &event->xmap;
417 fgWarning( "%s: event=0x%x, window=0x%x, override_redirect=%s",
418 fghTypeToString( e->type ), e->event, e->window,
419 fghBoolToString( e->override_redirect ) );
424 XMapRequestEvent *e = &event->xmaprequest;
425 fgWarning( "%s: parent=0x%x, window=0x%x",
426 fghTypeToString( event->type ), e->parent, e->window );
430 case ReparentNotify: {
431 XReparentEvent *e = &event->xreparent;
432 fgWarning( "%s: event=0x%x, window=0x%x, parent=0x%x, (x,y)=(%d,%d), "
433 "override_redirect=%s", fghTypeToString( e->type ),
434 e->event, e->window, e->parent, e->x, e->y,
435 fghBoolToString( e->override_redirect ) );
439 case ConfigureNotify: {
440 XConfigureEvent *e = &event->xconfigure;
441 fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d), "
442 "(width,height)=(%d,%d), border_width=%d, above=0x%x, "
443 "override_redirect=%s", fghTypeToString( e->type ), e->event,
444 e->window, e->x, e->y, e->width, e->height, e->border_width,
445 e->above, fghBoolToString( e->override_redirect ) );
449 case ConfigureRequest: {
450 XConfigureRequestEvent *e = &event->xconfigurerequest;
451 fgWarning( "%s: parent=0x%x, window=0x%x, (x,y)=(%d,%d), "
452 "(width,height)=(%d,%d), border_width=%d, above=0x%x, "
453 "detail=%s, value_mask=%lx", fghTypeToString( e->type ),
454 e->parent, e->window, e->x, e->y, e->width, e->height,
455 e->border_width, e->above,
456 fghConfigureDetailToString( e->detail ), e->value_mask );
460 case GravityNotify: {
461 XGravityEvent *e = &event->xgravity;
462 fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d)",
463 fghTypeToString( e->type ), e->event, e->window, e->x, e->y );
467 case ResizeRequest: {
468 XResizeRequestEvent *e = &event->xresizerequest;
469 fgWarning( "%s: window=0x%x, (width,height)=(%d,%d)",
470 fghTypeToString( e->type ), e->window, e->width, e->height );
474 case CirculateNotify: {
475 XCirculateEvent *e = &event->xcirculate;
476 fgWarning( "%s: event=0x%x, window=0x%x, place=%s",
477 fghTypeToString( e->type ), e->event, e->window,
478 fghPlaceToString( e->place ) );
482 case CirculateRequest: {
483 XCirculateRequestEvent *e = &event->xcirculaterequest;
484 fgWarning( "%s: parent=0x%x, window=0x%x, place=%s",
485 fghTypeToString( e->type ), e->parent, e->window,
486 fghPlaceToString( e->place ) );
490 case PropertyNotify: {
491 XPropertyEvent *e = &event->xproperty;
492 fgWarning( "%s: window=0x%x, atom=%lu, time=%lu, state=%s",
493 fghTypeToString( e->type ), e->window,
494 (unsigned long)e->atom, (unsigned long)e->time,
495 fghPropertyStateToString( e->state ) );
499 case SelectionClear: {
500 XSelectionClearEvent *e = &event->xselectionclear;
501 fgWarning( "%s: window=0x%x, selection=%lu, time=%lu",
502 fghTypeToString( e->type ), e->window,
503 (unsigned long)e->selection, (unsigned long)e->time );
507 case SelectionRequest: {
508 XSelectionRequestEvent *e = &event->xselectionrequest;
509 fgWarning( "%s: owner=0x%x, requestor=0x%x, selection=0x%x, "
510 "target=0x%x, property=%lu, time=%lu",
511 fghTypeToString( e->type ), e->owner, e->requestor,
512 (unsigned long)e->selection, (unsigned long)e->target,
513 (unsigned long)e->property, (unsigned long)e->time );
517 case SelectionNotify: {
518 XSelectionEvent *e = &event->xselection;
519 fgWarning( "%s: requestor=0x%x, selection=0x%x, target=0x%x, "
520 "property=%lu, time=%lu", fghTypeToString( e->type ),
521 e->requestor, (unsigned long)e->selection,
522 (unsigned long)e->target, (unsigned long)e->property,
523 (unsigned long)e->time );
527 case ColormapNotify: {
528 XColormapEvent *e = &event->xcolormap;
529 fgWarning( "%s: window=0x%x, colormap=%lu, new=%s, state=%s",
530 fghTypeToString( e->type ), e->window,
531 (unsigned long)e->colormap, fghBoolToString( e->new ),
532 fghColormapStateToString( e->state ) );
536 case ClientMessage: {
537 XClientMessageEvent *e = &event->xclient;
540 char* end = buf + sizeof( buf );
542 switch( e->format ) {
544 for ( i = 0; i < 20; i++, p += 3 ) {
545 snprintf( p, end - p, " %02x", e->data.b[ i ] );
549 for ( i = 0; i < 10; i++, p += 5 ) {
550 snprintf( p, end - p, " %04x", e->data.s[ i ] );
554 for ( i = 0; i < 5; i++, p += 9 ) {
555 snprintf( p, end - p, " %08lx", e->data.l[ i ] );
560 fgWarning( "%s: window=0x%x, message_type=%lu, format=%d, data=(%s )",
561 fghTypeToString( e->type ), e->window,
562 (unsigned long)e->message_type, e->format, buf );
566 case MappingNotify: {
567 XMappingEvent *e = &event->xmapping;
568 fgWarning( "%s: window=0x%x, request=%s, first_keycode=%d, count=%d",
569 fghTypeToString( e->type ), e->window,
570 fghMappingRequestToString( e->request ), e->first_keycode,
576 fgWarning( "%s", fghTypeToString( event->type ) );
583 void fgPlatformProcessSingleEvent ( void )
588 /* This code was repeated constantly, so here it goes into a definition: */
589 #define GETWINDOW(a) \
590 window = fgWindowByHandle( event.a.window ); \
591 if( window == NULL ) \
594 #define GETMOUSE(a) \
595 window->State.MouseX = event.a.x; \
596 window->State.MouseY = event.a.y;
598 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
600 while( XPending( fgDisplay.pDisplay.Display ) )
602 XNextEvent( fgDisplay.pDisplay.Display, &event );
604 fghPrintEvent( &event );
610 if (fgStructure.CurrentWindow)
611 if(fgIsSpaceballXEvent(&event)) {
612 fgSpaceballHandleXEvent(&event);
615 /* Destroy the window when the WM_DELETE_WINDOW message arrives */
616 if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.pDisplay.DeleteWindow )
618 GETWINDOW( xclient );
620 fgDestroyWindow ( window );
622 if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
627 else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
628 fgState.ExecState = GLUT_EXEC_STATE_STOP;
635 * CreateNotify causes a configure-event so that sub-windows are
636 * handled compatibly with GLUT. Otherwise, your sub-windows
637 * (in freeglut only) will not get an initial reshape event,
638 * which can break things.
640 * GLUT presumably does this because it generally tries to treat
641 * sub-windows the same as windows.
644 case ConfigureNotify:
646 int width, height, x, y;
647 if( event.type == CreateNotify ) {
648 GETWINDOW( xcreatewindow );
649 width = event.xcreatewindow.width;
650 height = event.xcreatewindow.height;
651 x = event.xcreatewindow.x;
652 y = event.xcreatewindow.y;
654 GETWINDOW( xconfigure );
655 width = event.xconfigure.width;
656 height = event.xconfigure.height;
657 x = event.xconfigure.x;
658 y = event.xconfigure.y;
661 /* Update state and call callback, if there was a change */
662 fghOnPositionNotify(window, x, y, GL_FALSE);
663 /* Update state and call callback, if there was a change */
664 fghOnReshapeNotify(window, width, height, GL_FALSE);
670 * This is sent to confirm the XDestroyWindow call.
672 * XXX WHY is this commented out? Should we re-enable it?
674 /* fgAddToWindowDestroyList ( window ); */
679 * We are too dumb to process partial exposes...
681 * XXX Well, we could do it. However, it seems to only
682 * XXX be potentially useful for single-buffered (since
683 * XXX double-buffered does not respect viewport when we
684 * XXX do a buffer-swap).
687 if( event.xexpose.count == 0 )
689 GETWINDOW( xexpose );
690 window->State.WorkMask |= GLUT_DISPLAY_WORK;
698 /* We get this when iconifying a window. */
700 INVOKE_WCB( *window, WindowStatus, ( GLUT_HIDDEN ) );
701 window->State.Visible = GL_FALSE;
706 * Have the client's keyboard knowledge updated (xlib.ps,
707 * page 206, says that's a good thing to do)
709 XRefreshKeyboardMapping( (XMappingEvent *) &event );
712 case VisibilityNotify:
715 * Sending this event, the X server can notify us that the window
716 * has just acquired one of the three possible visibility states:
717 * VisibilityUnobscured, VisibilityPartiallyObscured or
718 * VisibilityFullyObscured. Note that we DO NOT receive a
719 * VisibilityNotify event when iconifying a window, we only get an
722 GETWINDOW( xvisibility );
723 switch( event.xvisibility.state )
725 case VisibilityUnobscured:
726 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_RETAINED ) );
727 window->State.Visible = GL_TRUE;
730 case VisibilityPartiallyObscured:
731 INVOKE_WCB( *window, WindowStatus,
732 ( GLUT_PARTIALLY_RETAINED ) );
733 window->State.Visible = GL_TRUE;
736 case VisibilityFullyObscured:
737 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_COVERED ) );
738 window->State.Visible = GL_FALSE;
742 fgWarning( "Unknown X visibility state: %d",
743 event.xvisibility.state );
751 GETWINDOW( xcrossing );
752 GETMOUSE( xcrossing );
753 if( ( event.type == LeaveNotify ) && window->IsMenu &&
754 window->ActiveMenu && window->ActiveMenu->IsActive )
755 fgUpdateMenuHighlight( window->ActiveMenu );
757 INVOKE_WCB( *window, Entry, ( ( EnterNotify == event.type ) ?
764 /* if GLUT_SKIP_STALE_MOTION_EVENTS is true, then discard all but
765 * the last motion event from the queue
767 if(fgState.SkipStaleMotion) {
768 while(XCheckIfEvent(fgDisplay.pDisplay.Display, &event, match_motion, 0));
771 GETWINDOW( xmotion );
774 if( window->ActiveMenu )
776 if( window == window->ActiveMenu->ParentWindow )
778 window->ActiveMenu->Window->State.MouseX =
779 event.xmotion.x_root - window->ActiveMenu->X;
780 window->ActiveMenu->Window->State.MouseY =
781 event.xmotion.y_root - window->ActiveMenu->Y;
784 fgUpdateMenuHighlight( window->ActiveMenu );
790 * XXX For more than 5 buttons, just check {event.xmotion.state},
791 * XXX rather than a host of bit-masks? Or maybe we need to
792 * XXX track ButtonPress/ButtonRelease events in our own
795 fgState.Modifiers = fgPlatformGetModifiers( event.xmotion.state );
796 if ( event.xmotion.state & ( Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask ) ) {
797 INVOKE_WCB( *window, Motion, ( event.xmotion.x,
800 INVOKE_WCB( *window, Passive, ( event.xmotion.x,
803 fgState.Modifiers = INVALID_MODIFIERS;
814 * A mouse button has been pressed or released. Traditionally,
815 * break if the window was found within the freeglut structures.
817 GETWINDOW( xbutton );
821 * An X button (at least in XFree86) is numbered from 1.
822 * A GLUT button is numbered from 0.
823 * Old GLUT passed through buttons other than just the first
824 * three, though it only gave symbolic names and official
825 * support to the first three.
827 button = event.xbutton.button - 1;
829 pressed = event.type == ButtonPress ? GL_TRUE : GL_FALSE;
834 * Do not execute the application's mouse callback if a menu
835 * is hooked to this button. In that case an appropriate
836 * private call should be generated.
838 if(fgCheckActiveMenu( window, button, pressed, x, y))
842 * Check if there is a mouse or mouse wheel callback hooked to the
845 if(!FETCH_WCB(*window, Mouse) && !FETCH_WCB(*window, MouseWheel))
848 fgState.Modifiers = fgPlatformGetModifiers(event.xbutton.state);
850 /* Finally execute the mouse or mouse wheel callback.
851 * The mouse wheel is reported as buttons 4 (down) and 5 (up) by
852 * the X server. "button" has been converted to 0-based above, so
853 * that's 3 and 4 for us.
854 * If a wheel callback hasn't been registered, we simply treat them
855 * as button presses and pass them to the mouse handler. This is
856 * important for compatibility with the original GLUT.
858 if(button < 3 || button > 4 || !FETCH_WCB(*window, MouseWheel)) {
859 INVOKE_WCB(*window, Mouse, (button, pressed ? GLUT_DOWN : GLUT_UP, x, y));
862 int dir = button & 1 ? 1 : -1;
863 /* there's no way to know if X buttons after 5 are more
864 * wheels/wheel axes, or regular buttons. So we'll only
865 * ever invoke the wheel CB for wheel 0.
867 INVOKE_WCB(*window, MouseWheel, (0, dir, x, y));
870 fgState.Modifiers = INVALID_MODIFIERS;
877 FGCBKeyboardUC keyboard_cb;
878 FGCBSpecialUC special_cb;
879 FGCBUserData keyboard_ud;
880 FGCBUserData special_ud;
885 /* Detect auto repeated keys, if configured globally or per-window */
887 if ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE )
889 if (event.type==KeyRelease)
892 * Look at X11 keystate to detect repeat mode.
893 * While X11 says the key is actually held down, we'll ignore KeyRelease/KeyPress pairs.
897 XQueryKeymap( fgDisplay.pDisplay.Display, keys ); /* Look at X11 keystate to detect repeat mode */
899 if ( event.xkey.keycode<256 ) /* XQueryKeymap is limited to 256 keycodes */
901 if ( keys[event.xkey.keycode>>3] & (1<<(event.xkey.keycode%8)) )
902 window->State.pWState.KeyRepeating = GL_TRUE;
904 window->State.pWState.KeyRepeating = GL_FALSE;
909 window->State.pWState.KeyRepeating = GL_FALSE;
911 /* Cease processing this event if it is auto repeated */
913 if (window->State.pWState.KeyRepeating)
915 if (event.type == KeyPress) window->State.pWState.KeyRepeating = GL_FALSE;
919 if( event.type == KeyPress )
921 keyboard_cb = (FGCBKeyboardUC)( FETCH_WCB( *window, Keyboard ));
922 special_cb = (FGCBSpecialUC) ( FETCH_WCB( *window, Special ));
923 keyboard_ud = FETCH_USER_DATA_WCB( *window, Keyboard );
924 special_ud = FETCH_USER_DATA_WCB( *window, Special );
928 keyboard_cb = (FGCBKeyboardUC)( FETCH_WCB( *window, KeyboardUp ));
929 special_cb = (FGCBSpecialUC) ( FETCH_WCB( *window, SpecialUp ));
930 keyboard_ud = FETCH_USER_DATA_WCB( *window, KeyboardUp );
931 special_ud = FETCH_USER_DATA_WCB( *window, SpecialUp );
934 /* Is there a keyboard/special callback hooked for this window? */
935 if( keyboard_cb || special_cb )
937 XComposeStatus composeStatus;
938 char asciiCode[ 32 ];
942 /* Check for the ASCII/KeySym codes associated with the event: */
943 len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode),
944 &keySym, &composeStatus
947 /* GLUT API tells us to have two separate callbacks... */
950 /* ...one for the ASCII translateable keypresses... */
953 fgSetWindow( window );
954 fgState.Modifiers = fgPlatformGetModifiers( event.xkey.state );
955 keyboard_cb( asciiCode[ 0 ],
956 event.xkey.x, event.xkey.y,
959 fgState.Modifiers = INVALID_MODIFIERS;
967 * ...and one for all the others, which need to be
968 * translated to GLUT_KEY_Xs...
972 case XK_F1: special = GLUT_KEY_F1; break;
973 case XK_F2: special = GLUT_KEY_F2; break;
974 case XK_F3: special = GLUT_KEY_F3; break;
975 case XK_F4: special = GLUT_KEY_F4; break;
976 case XK_F5: special = GLUT_KEY_F5; break;
977 case XK_F6: special = GLUT_KEY_F6; break;
978 case XK_F7: special = GLUT_KEY_F7; break;
979 case XK_F8: special = GLUT_KEY_F8; break;
980 case XK_F9: special = GLUT_KEY_F9; break;
981 case XK_F10: special = GLUT_KEY_F10; break;
982 case XK_F11: special = GLUT_KEY_F11; break;
983 case XK_F12: special = GLUT_KEY_F12; break;
986 case XK_Left: special = GLUT_KEY_LEFT; break;
988 case XK_Right: special = GLUT_KEY_RIGHT; break;
990 case XK_Up: special = GLUT_KEY_UP; break;
992 case XK_Down: special = GLUT_KEY_DOWN; break;
995 case XK_Prior: special = GLUT_KEY_PAGE_UP; break;
997 case XK_Next: special = GLUT_KEY_PAGE_DOWN; break;
999 case XK_Home: special = GLUT_KEY_HOME; break;
1001 case XK_End: special = GLUT_KEY_END; break;
1003 case XK_Insert: special = GLUT_KEY_INSERT; break;
1005 case XK_Num_Lock : special = GLUT_KEY_NUM_LOCK; break;
1006 case XK_KP_Begin : special = GLUT_KEY_BEGIN; break;
1007 case XK_KP_Delete: special = GLUT_KEY_DELETE; break;
1009 case XK_Shift_L: special = GLUT_KEY_SHIFT_L; break;
1010 case XK_Shift_R: special = GLUT_KEY_SHIFT_R; break;
1011 case XK_Control_L: special = GLUT_KEY_CTRL_L; break;
1012 case XK_Control_R: special = GLUT_KEY_CTRL_R; break;
1013 case XK_Alt_L: special = GLUT_KEY_ALT_L; break;
1014 case XK_Alt_R: special = GLUT_KEY_ALT_R; break;
1018 * Execute the callback (if one has been specified),
1019 * given that the special code seems to be valid...
1021 if( special_cb && (special != -1) )
1023 fgSetWindow( window );
1024 fgState.Modifiers = fgPlatformGetModifiers( event.xkey.state );
1025 special_cb( special, event.xkey.x, event.xkey.y, special_ud );
1026 fgState.Modifiers = INVALID_MODIFIERS;
1033 case ReparentNotify:
1034 break; /* XXX Should disable this event */
1041 /* enter handling of Extension Events here */
1042 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
1043 fgHandleExtensionEvents( &event );
1051 static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg)
1053 return xev->type == MotionNotify;
1056 void fgPlatformMainLoopPreliminaryWork ( void )
1061 /* deal with work list items */
1062 void fgPlatformInitWork(SFG_Window* window)
1064 /* Notify windowStatus/visibility, position and size get notified on window creation with message handlers above
1065 * XXX CHECK: do the messages happen too early like on windows, so client code cannot have registered
1066 * a callback yet and the message is thus never received by client?
1067 * -> this is a no-op
1072 void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
1074 if (workMask & GLUT_FULL_SCREEN_WORK)
1075 fgPlatformFullScreenToggle( window );
1076 if (workMask & GLUT_POSITION_WORK)
1077 fgPlatformPositionWindow( window, window->State.DesiredXpos, window->State.DesiredYpos );
1078 if (workMask & GLUT_SIZE_WORK)
1079 fgPlatformReshapeWindow ( window, window->State.DesiredWidth, window->State.DesiredHeight );
1080 if (workMask & GLUT_ZORDER_WORK)
1082 if (window->State.DesiredZOrder < 0)
1083 fgPlatformPushWindow( window );
1085 fgPlatformPopWindow( window );
1089 void fgPlatformVisibilityWork(SFG_Window* window)
1091 /* Visibility status of window gets updated in the window message handlers above
1092 * XXX: is this really the case? check
1094 SFG_Window *win = window;
1095 switch (window->State.DesiredVisibility)
1097 case DesireHiddenState:
1098 fgPlatformHideWindow( window );
1100 case DesireIconicState:
1101 /* Call on top-level window */
1104 fgPlatformIconifyWindow( win );
1106 case DesireNormalState:
1107 fgPlatformShowWindow( window );