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 # define VFPRINTF(s,f,a) vfprintf((s),(f),(a))
37 #elif defined(HAVE__DOPRNT)
38 # define VFPRINTF(s,f,a) _doprnt((f),(a),(s))
40 # define VFPRINTF(s,f,a)
46 * Try to get the maximum value allowed for ints, falling back to the minimum
47 * guaranteed by ISO C99 if there is no suitable header.
53 # define INT_MAX 32767
57 # define MIN(a,b) (((a)<(b)) ? (a) : (b))
60 /* used in the event handling code to match and discard stale mouse motion events */
61 static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg);
64 * TODO BEFORE THE STABLE RELEASE:
66 * There are some issues concerning window redrawing under X11, and maybe
67 * some events are not handled. The Win32 version lacks some more features,
68 * but seems acceptable for not demanding purposes.
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 * Handle a window configuration change. When no reshape
77 * callback is hooked, the viewport size is updated to
78 * match the new window size.
80 void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height )
82 XResizeWindow( fgDisplay.pDisplay.Display, window->Window.Handle,
84 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
89 * A static helper function to execute display callback for a window
91 void fgPlatformDisplayWindow ( SFG_Window *window )
93 fghRedrawWindow ( window ) ;
97 fg_time_t fgPlatformSystemTime ( void )
99 #ifdef CLOCK_MONOTONIC
101 clock_gettime(CLOCK_MONOTONIC, &now);
102 return now.tv_nsec/1000000 + now.tv_sec*1000;
103 #elif defined(HAVE_GETTIMEOFDAY)
105 gettimeofday( &now, NULL );
106 return now.tv_usec/1000 + now.tv_sec*1000;
111 * Does the magic required to relinquish the CPU until something interesting
115 void fgPlatformSleepForEvents( fg_time_t msec )
118 * Possibly due to aggressive use of XFlush() and friends,
119 * it is possible to have our socket drained but still have
120 * unprocessed events. (Or, this may just be normal with
121 * X, anyway?) We do non-trivial processing of X events
122 * after the event-reading loop, in any case, so we
123 * need to allow that we may have an empty socket but non-
126 if( ! XPending( fgDisplay.pDisplay.Display ) )
133 socket = ConnectionNumber( fgDisplay.pDisplay.Display );
135 FD_SET( socket, &fdset );
136 wait.tv_sec = msec / 1000;
137 wait.tv_usec = (msec % 1000) * 1000;
138 err = select( socket+1, &fdset, NULL, NULL, &wait );
141 if( ( -1 == err ) && ( errno != EINTR ) )
142 fgWarning ( "freeglut select() error: %d", errno );
149 * Returns GLUT modifier mask for the state field of an X11 event.
151 int fgPlatformGetModifiers( int state )
155 if( state & ( ShiftMask | LockMask ) )
156 ret |= GLUT_ACTIVE_SHIFT;
157 if( state & ControlMask )
158 ret |= GLUT_ACTIVE_CTRL;
159 if( state & Mod1Mask )
160 ret |= GLUT_ACTIVE_ALT;
165 static const char* fghTypeToString( int type )
168 case KeyPress: return "KeyPress";
169 case KeyRelease: return "KeyRelease";
170 case ButtonPress: return "ButtonPress";
171 case ButtonRelease: return "ButtonRelease";
172 case MotionNotify: return "MotionNotify";
173 case EnterNotify: return "EnterNotify";
174 case LeaveNotify: return "LeaveNotify";
175 case FocusIn: return "FocusIn";
176 case FocusOut: return "FocusOut";
177 case KeymapNotify: return "KeymapNotify";
178 case Expose: return "Expose";
179 case GraphicsExpose: return "GraphicsExpose";
180 case NoExpose: return "NoExpose";
181 case VisibilityNotify: return "VisibilityNotify";
182 case CreateNotify: return "CreateNotify";
183 case DestroyNotify: return "DestroyNotify";
184 case UnmapNotify: return "UnmapNotify";
185 case MapNotify: return "MapNotify";
186 case MapRequest: return "MapRequest";
187 case ReparentNotify: return "ReparentNotify";
188 case ConfigureNotify: return "ConfigureNotify";
189 case ConfigureRequest: return "ConfigureRequest";
190 case GravityNotify: return "GravityNotify";
191 case ResizeRequest: return "ResizeRequest";
192 case CirculateNotify: return "CirculateNotify";
193 case CirculateRequest: return "CirculateRequest";
194 case PropertyNotify: return "PropertyNotify";
195 case SelectionClear: return "SelectionClear";
196 case SelectionRequest: return "SelectionRequest";
197 case SelectionNotify: return "SelectionNotify";
198 case ColormapNotify: return "ColormapNotify";
199 case ClientMessage: return "ClientMessage";
200 case MappingNotify: return "MappingNotify";
201 default: return "UNKNOWN";
205 static const char* fghBoolToString( Bool b )
207 return b == False ? "False" : "True";
210 static const char* fghNotifyHintToString( char is_hint )
213 case NotifyNormal: return "NotifyNormal";
214 case NotifyHint: return "NotifyHint";
215 default: return "UNKNOWN";
219 static const char* fghNotifyModeToString( int mode )
222 case NotifyNormal: return "NotifyNormal";
223 case NotifyGrab: return "NotifyGrab";
224 case NotifyUngrab: return "NotifyUngrab";
225 case NotifyWhileGrabbed: return "NotifyWhileGrabbed";
226 default: return "UNKNOWN";
230 static const char* fghNotifyDetailToString( int detail )
233 case NotifyAncestor: return "NotifyAncestor";
234 case NotifyVirtual: return "NotifyVirtual";
235 case NotifyInferior: return "NotifyInferior";
236 case NotifyNonlinear: return "NotifyNonlinear";
237 case NotifyNonlinearVirtual: return "NotifyNonlinearVirtual";
238 case NotifyPointer: return "NotifyPointer";
239 case NotifyPointerRoot: return "NotifyPointerRoot";
240 case NotifyDetailNone: return "NotifyDetailNone";
241 default: return "UNKNOWN";
245 static const char* fghVisibilityToString( int state ) {
247 case VisibilityUnobscured: return "VisibilityUnobscured";
248 case VisibilityPartiallyObscured: return "VisibilityPartiallyObscured";
249 case VisibilityFullyObscured: return "VisibilityFullyObscured";
250 default: return "UNKNOWN";
254 static const char* fghConfigureDetailToString( int detail )
257 case Above: return "Above";
258 case Below: return "Below";
259 case TopIf: return "TopIf";
260 case BottomIf: return "BottomIf";
261 case Opposite: return "Opposite";
262 default: return "UNKNOWN";
266 static const char* fghPlaceToString( int place )
269 case PlaceOnTop: return "PlaceOnTop";
270 case PlaceOnBottom: return "PlaceOnBottom";
271 default: return "UNKNOWN";
275 static const char* fghMappingRequestToString( int request )
278 case MappingModifier: return "MappingModifier";
279 case MappingKeyboard: return "MappingKeyboard";
280 case MappingPointer: return "MappingPointer";
281 default: return "UNKNOWN";
285 static const char* fghPropertyStateToString( int state )
288 case PropertyNewValue: return "PropertyNewValue";
289 case PropertyDelete: return "PropertyDelete";
290 default: return "UNKNOWN";
294 static const char* fghColormapStateToString( int state )
297 case ColormapUninstalled: return "ColormapUninstalled";
298 case ColormapInstalled: return "ColormapInstalled";
299 default: return "UNKNOWN";
303 __fg_unused static void fghPrintEvent( XEvent *event )
305 switch( event->type ) {
309 XKeyEvent *e = &event->xkey;
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 "keycode=%u, same_screen=%s", 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, e->keycode,
315 fghBoolToString( e->same_screen ) );
320 case ButtonRelease: {
321 XButtonEvent *e = &event->xbutton;
322 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
323 "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
324 "button=%u, same_screen=%d", fghTypeToString( e->type ),
325 e->window, e->root, e->subwindow, (unsigned long)e->time,
326 e->x, e->y, e->x_root, e->y_root, e->state, e->button,
327 fghBoolToString( e->same_screen ) );
332 XMotionEvent *e = &event->xmotion;
333 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
334 "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
335 "is_hint=%s, same_screen=%d", fghTypeToString( e->type ),
336 e->window, e->root, e->subwindow, (unsigned long)e->time,
337 e->x, e->y, e->x_root, e->y_root, e->state,
338 fghNotifyHintToString( e->is_hint ),
339 fghBoolToString( e->same_screen ) );
345 XCrossingEvent *e = &event->xcrossing;
346 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
347 "(x,y)=(%d,%d), mode=%s, detail=%s, same_screen=%d, "
348 "focus=%d, state=0x%x", fghTypeToString( e->type ),
349 e->window, e->root, e->subwindow, (unsigned long)e->time,
350 e->x, e->y, fghNotifyModeToString( e->mode ),
351 fghNotifyDetailToString( e->detail ), (int)e->same_screen,
352 (int)e->focus, e->state );
358 XFocusChangeEvent *e = &event->xfocus;
359 fgWarning( "%s: window=0x%x, mode=%s, detail=%s",
360 fghTypeToString( e->type ), e->window,
361 fghNotifyModeToString( e->mode ),
362 fghNotifyDetailToString( e->detail ) );
367 XKeymapEvent *e = &event->xkeymap;
368 char buf[32 * 2 + 1];
370 for ( i = 0; i < 32; i++ ) {
371 snprintf( &buf[ i * 2 ], sizeof( buf ) - i * 2,
372 "%02x", e->key_vector[ i ] );
375 fgWarning( "%s: window=0x%x, %s", fghTypeToString( e->type ), e->window,
381 XExposeEvent *e = &event->xexpose;
382 fgWarning( "%s: window=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), "
383 "count=%d", fghTypeToString( e->type ), e->window, e->x,
384 e->y, e->width, e->height, e->count );
388 case GraphicsExpose: {
389 XGraphicsExposeEvent *e = &event->xgraphicsexpose;
390 fgWarning( "%s: drawable=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), "
391 "count=%d, (major_code,minor_code)=(%d,%d)",
392 fghTypeToString( e->type ), e->drawable, e->x, e->y,
393 e->width, e->height, e->count, e->major_code,
399 XNoExposeEvent *e = &event->xnoexpose;
400 fgWarning( "%s: drawable=0x%x, (major_code,minor_code)=(%d,%d)",
401 fghTypeToString( e->type ), e->drawable, e->major_code,
406 case VisibilityNotify: {
407 XVisibilityEvent *e = &event->xvisibility;
408 fgWarning( "%s: window=0x%x, state=%s", fghTypeToString( e->type ),
409 e->window, fghVisibilityToString( e->state) );
414 XCreateWindowEvent *e = &event->xcreatewindow;
415 fgWarning( "%s: (x,y)=(%d,%d), (width,height)=(%d,%d), border_width=%d, "
416 "window=0x%x, override_redirect=%s",
417 fghTypeToString( e->type ), e->x, e->y, e->width, e->height,
418 e->border_width, e->window,
419 fghBoolToString( e->override_redirect ) );
423 case DestroyNotify: {
424 XDestroyWindowEvent *e = &event->xdestroywindow;
425 fgWarning( "%s: event=0x%x, window=0x%x",
426 fghTypeToString( e->type ), e->event, e->window );
431 XUnmapEvent *e = &event->xunmap;
432 fgWarning( "%s: event=0x%x, window=0x%x, from_configure=%s",
433 fghTypeToString( e->type ), e->event, e->window,
434 fghBoolToString( e->from_configure ) );
439 XMapEvent *e = &event->xmap;
440 fgWarning( "%s: event=0x%x, window=0x%x, override_redirect=%s",
441 fghTypeToString( e->type ), e->event, e->window,
442 fghBoolToString( e->override_redirect ) );
447 XMapRequestEvent *e = &event->xmaprequest;
448 fgWarning( "%s: parent=0x%x, window=0x%x",
449 fghTypeToString( event->type ), e->parent, e->window );
453 case ReparentNotify: {
454 XReparentEvent *e = &event->xreparent;
455 fgWarning( "%s: event=0x%x, window=0x%x, parent=0x%x, (x,y)=(%d,%d), "
456 "override_redirect=%s", fghTypeToString( e->type ),
457 e->event, e->window, e->parent, e->x, e->y,
458 fghBoolToString( e->override_redirect ) );
462 case ConfigureNotify: {
463 XConfigureEvent *e = &event->xconfigure;
464 fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d), "
465 "(width,height)=(%d,%d), border_width=%d, above=0x%x, "
466 "override_redirect=%s", fghTypeToString( e->type ), e->event,
467 e->window, e->x, e->y, e->width, e->height, e->border_width,
468 e->above, fghBoolToString( e->override_redirect ) );
472 case ConfigureRequest: {
473 XConfigureRequestEvent *e = &event->xconfigurerequest;
474 fgWarning( "%s: parent=0x%x, window=0x%x, (x,y)=(%d,%d), "
475 "(width,height)=(%d,%d), border_width=%d, above=0x%x, "
476 "detail=%s, value_mask=%lx", fghTypeToString( e->type ),
477 e->parent, e->window, e->x, e->y, e->width, e->height,
478 e->border_width, e->above,
479 fghConfigureDetailToString( e->detail ), e->value_mask );
483 case GravityNotify: {
484 XGravityEvent *e = &event->xgravity;
485 fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d)",
486 fghTypeToString( e->type ), e->event, e->window, e->x, e->y );
490 case ResizeRequest: {
491 XResizeRequestEvent *e = &event->xresizerequest;
492 fgWarning( "%s: window=0x%x, (width,height)=(%d,%d)",
493 fghTypeToString( e->type ), e->window, e->width, e->height );
497 case CirculateNotify: {
498 XCirculateEvent *e = &event->xcirculate;
499 fgWarning( "%s: event=0x%x, window=0x%x, place=%s",
500 fghTypeToString( e->type ), e->event, e->window,
501 fghPlaceToString( e->place ) );
505 case CirculateRequest: {
506 XCirculateRequestEvent *e = &event->xcirculaterequest;
507 fgWarning( "%s: parent=0x%x, window=0x%x, place=%s",
508 fghTypeToString( e->type ), e->parent, e->window,
509 fghPlaceToString( e->place ) );
513 case PropertyNotify: {
514 XPropertyEvent *e = &event->xproperty;
515 fgWarning( "%s: window=0x%x, atom=%lu, time=%lu, state=%s",
516 fghTypeToString( e->type ), e->window,
517 (unsigned long)e->atom, (unsigned long)e->time,
518 fghPropertyStateToString( e->state ) );
522 case SelectionClear: {
523 XSelectionClearEvent *e = &event->xselectionclear;
524 fgWarning( "%s: window=0x%x, selection=%lu, time=%lu",
525 fghTypeToString( e->type ), e->window,
526 (unsigned long)e->selection, (unsigned long)e->time );
530 case SelectionRequest: {
531 XSelectionRequestEvent *e = &event->xselectionrequest;
532 fgWarning( "%s: owner=0x%x, requestor=0x%x, selection=0x%x, "
533 "target=0x%x, property=%lu, time=%lu",
534 fghTypeToString( e->type ), e->owner, e->requestor,
535 (unsigned long)e->selection, (unsigned long)e->target,
536 (unsigned long)e->property, (unsigned long)e->time );
540 case SelectionNotify: {
541 XSelectionEvent *e = &event->xselection;
542 fgWarning( "%s: requestor=0x%x, selection=0x%x, target=0x%x, "
543 "property=%lu, time=%lu", fghTypeToString( e->type ),
544 e->requestor, (unsigned long)e->selection,
545 (unsigned long)e->target, (unsigned long)e->property,
546 (unsigned long)e->time );
550 case ColormapNotify: {
551 XColormapEvent *e = &event->xcolormap;
552 fgWarning( "%s: window=0x%x, colormap=%lu, new=%s, state=%s",
553 fghTypeToString( e->type ), e->window,
554 (unsigned long)e->colormap, fghBoolToString( e->new ),
555 fghColormapStateToString( e->state ) );
559 case ClientMessage: {
560 XClientMessageEvent *e = &event->xclient;
563 char* end = buf + sizeof( buf );
565 switch( e->format ) {
567 for ( i = 0; i < 20; i++, p += 3 ) {
568 snprintf( p, end - p, " %02x", e->data.b[ i ] );
572 for ( i = 0; i < 10; i++, p += 5 ) {
573 snprintf( p, end - p, " %04x", e->data.s[ i ] );
577 for ( i = 0; i < 5; i++, p += 9 ) {
578 snprintf( p, end - p, " %08lx", e->data.l[ i ] );
583 fgWarning( "%s: window=0x%x, message_type=%lu, format=%d, data=(%s )",
584 fghTypeToString( e->type ), e->window,
585 (unsigned long)e->message_type, e->format, buf );
589 case MappingNotify: {
590 XMappingEvent *e = &event->xmapping;
591 fgWarning( "%s: window=0x%x, request=%s, first_keycode=%d, count=%d",
592 fghTypeToString( e->type ), e->window,
593 fghMappingRequestToString( e->request ), e->first_keycode,
599 fgWarning( "%s", fghTypeToString( event->type ) );
606 void fgPlatformProcessSingleEvent ( void )
611 /* This code was repeated constantly, so here it goes into a definition: */
612 #define GETWINDOW(a) \
613 window = fgWindowByHandle( event.a.window ); \
614 if( window == NULL ) \
617 #define GETMOUSE(a) \
618 window->State.MouseX = event.a.x; \
619 window->State.MouseY = event.a.y;
621 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
623 while( XPending( fgDisplay.pDisplay.Display ) )
625 XNextEvent( fgDisplay.pDisplay.Display, &event );
627 fghPrintEvent( &event );
633 if(fgIsSpaceballXEvent(&event)) {
634 fgSpaceballHandleXEvent(&event);
637 /* Destroy the window when the WM_DELETE_WINDOW message arrives */
638 if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.pDisplay.DeleteWindow )
640 GETWINDOW( xclient );
642 fgDestroyWindow ( window );
644 if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
649 else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
650 fgState.ExecState = GLUT_EXEC_STATE_STOP;
657 * CreateNotify causes a configure-event so that sub-windows are
658 * handled compatibly with GLUT. Otherwise, your sub-windows
659 * (in freeglut only) will not get an initial reshape event,
660 * which can break things.
662 * GLUT presumably does this because it generally tries to treat
663 * sub-windows the same as windows.
666 case ConfigureNotify:
669 if( event.type == CreateNotify ) {
670 GETWINDOW( xcreatewindow );
671 width = event.xcreatewindow.width;
672 height = event.xcreatewindow.height;
674 GETWINDOW( xconfigure );
675 width = event.xconfigure.width;
676 height = event.xconfigure.height;
679 if( ( width != window->State.pWState.OldWidth ) ||
680 ( height != window->State.pWState.OldHeight ) )
682 SFG_Window *current_window = fgStructure.CurrentWindow;
684 window->State.pWState.OldWidth = width;
685 window->State.pWState.OldHeight = height;
686 if( FETCH_WCB( *window, Reshape ) )
687 INVOKE_WCB( *window, Reshape, ( width, height ) );
690 fgSetWindow( window );
691 glViewport( 0, 0, width, height );
693 glutPostRedisplay( );
695 fgSetWindow( current_window );
702 * This is sent to confirm the XDestroyWindow call.
704 * XXX WHY is this commented out? Should we re-enable it?
706 /* fgAddToWindowDestroyList ( window ); */
711 * We are too dumb to process partial exposes...
713 * XXX Well, we could do it. However, it seems to only
714 * XXX be potentially useful for single-buffered (since
715 * XXX double-buffered does not respect viewport when we
716 * XXX do a buffer-swap).
719 if( event.xexpose.count == 0 )
721 GETWINDOW( xexpose );
722 window->State.Redisplay = GL_TRUE;
730 /* We get this when iconifying a window. */
732 INVOKE_WCB( *window, WindowStatus, ( GLUT_HIDDEN ) );
733 window->State.Visible = GL_FALSE;
738 * Have the client's keyboard knowledge updated (xlib.ps,
739 * page 206, says that's a good thing to do)
741 XRefreshKeyboardMapping( (XMappingEvent *) &event );
744 case VisibilityNotify:
747 * Sending this event, the X server can notify us that the window
748 * has just acquired one of the three possible visibility states:
749 * VisibilityUnobscured, VisibilityPartiallyObscured or
750 * VisibilityFullyObscured. Note that we DO NOT receive a
751 * VisibilityNotify event when iconifying a window, we only get an
754 GETWINDOW( xvisibility );
755 switch( event.xvisibility.state )
757 case VisibilityUnobscured:
758 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_RETAINED ) );
759 window->State.Visible = GL_TRUE;
762 case VisibilityPartiallyObscured:
763 INVOKE_WCB( *window, WindowStatus,
764 ( GLUT_PARTIALLY_RETAINED ) );
765 window->State.Visible = GL_TRUE;
768 case VisibilityFullyObscured:
769 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_COVERED ) );
770 window->State.Visible = GL_FALSE;
774 fgWarning( "Unknown X visibility state: %d",
775 event.xvisibility.state );
783 GETWINDOW( xcrossing );
784 GETMOUSE( xcrossing );
785 if( ( event.type == LeaveNotify ) && window->IsMenu &&
786 window->ActiveMenu && window->ActiveMenu->IsActive )
787 fgUpdateMenuHighlight( window->ActiveMenu );
789 INVOKE_WCB( *window, Entry, ( ( EnterNotify == event.type ) ?
796 /* if GLUT_SKIP_STALE_MOTION_EVENTS is true, then discard all but
797 * the last motion event from the queue
799 if(fgState.SkipStaleMotion) {
800 while(XCheckIfEvent(fgDisplay.pDisplay.Display, &event, match_motion, 0));
803 GETWINDOW( xmotion );
806 if( window->ActiveMenu )
808 if( window == window->ActiveMenu->ParentWindow )
810 window->ActiveMenu->Window->State.MouseX =
811 event.xmotion.x_root - window->ActiveMenu->X;
812 window->ActiveMenu->Window->State.MouseY =
813 event.xmotion.y_root - window->ActiveMenu->Y;
816 fgUpdateMenuHighlight( window->ActiveMenu );
822 * XXX For more than 5 buttons, just check {event.xmotion.state},
823 * XXX rather than a host of bit-masks? Or maybe we need to
824 * XXX track ButtonPress/ButtonRelease events in our own
827 fgState.Modifiers = fgPlatformGetModifiers( event.xmotion.state );
828 if ( event.xmotion.state & ( Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask ) ) {
829 INVOKE_WCB( *window, Motion, ( event.xmotion.x,
832 INVOKE_WCB( *window, Passive, ( event.xmotion.x,
835 fgState.Modifiers = INVALID_MODIFIERS;
842 GLboolean pressed = GL_TRUE;
845 if( event.type == ButtonRelease )
849 * A mouse button has been pressed or released. Traditionally,
850 * break if the window was found within the freeglut structures.
852 GETWINDOW( xbutton );
856 * An X button (at least in XFree86) is numbered from 1.
857 * A GLUT button is numbered from 0.
858 * Old GLUT passed through buttons other than just the first
859 * three, though it only gave symbolic names and official
860 * support to the first three.
862 button = event.xbutton.button - 1;
865 * Do not execute the application's mouse callback if a menu
866 * is hooked to this button. In that case an appropriate
867 * private call should be generated.
869 if( fgCheckActiveMenu( window, button, pressed,
870 event.xbutton.x_root, event.xbutton.y_root ) )
874 * Check if there is a mouse or mouse wheel callback hooked to the
877 if( ! FETCH_WCB( *window, Mouse ) &&
878 ! FETCH_WCB( *window, MouseWheel ) )
881 fgState.Modifiers = fgPlatformGetModifiers( event.xbutton.state );
883 /* Finally execute the mouse or mouse wheel callback */
884 if( ( button < glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS ) ) || ( ! FETCH_WCB( *window, MouseWheel ) ) )
885 INVOKE_WCB( *window, Mouse, ( button,
886 pressed ? GLUT_DOWN : GLUT_UP,
893 * Map 4 and 5 to wheel zero; EVEN to +1, ODD to -1
894 * " 6 and 7 " " one; ...
896 * XXX This *should* be behind some variables/macros,
897 * XXX since the order and numbering isn't certain
898 * XXX See XFree86 configuration docs (even back in the
899 * XXX 3.x days, and especially with 4.x).
901 * XXX Note that {button} has already been decremented
902 * XXX in mapping from X button numbering to GLUT.
904 * XXX Should add support for partial wheel turns as Windows does -- 5/27/11
906 int wheel_number = (button - glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS )) / 2;
912 INVOKE_WCB( *window, MouseWheel, ( wheel_number,
918 fgState.Modifiers = INVALID_MODIFIERS;
925 FGCBKeyboard keyboard_cb;
926 FGCBSpecial special_cb;
931 /* Detect auto repeated keys, if configured globally or per-window */
933 if ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE )
935 if (event.type==KeyRelease)
938 * Look at X11 keystate to detect repeat mode.
939 * While X11 says the key is actually held down, we'll ignore KeyRelease/KeyPress pairs.
943 XQueryKeymap( fgDisplay.pDisplay.Display, keys ); /* Look at X11 keystate to detect repeat mode */
945 if ( event.xkey.keycode<256 ) /* XQueryKeymap is limited to 256 keycodes */
947 if ( keys[event.xkey.keycode>>3] & (1<<(event.xkey.keycode%8)) )
948 window->State.KeyRepeating = GL_TRUE;
950 window->State.KeyRepeating = GL_FALSE;
955 window->State.KeyRepeating = GL_FALSE;
957 /* Cease processing this event if it is auto repeated */
959 if (window->State.KeyRepeating)
961 if (event.type == KeyPress) window->State.KeyRepeating = GL_FALSE;
965 if( event.type == KeyPress )
967 keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, Keyboard ));
968 special_cb = (FGCBSpecial) ( FETCH_WCB( *window, Special ));
972 keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, KeyboardUp ));
973 special_cb = (FGCBSpecial) ( FETCH_WCB( *window, SpecialUp ));
976 /* Is there a keyboard/special callback hooked for this window? */
977 if( keyboard_cb || special_cb )
979 XComposeStatus composeStatus;
980 char asciiCode[ 32 ];
984 /* Check for the ASCII/KeySym codes associated with the event: */
985 len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode),
986 &keySym, &composeStatus
989 /* GLUT API tells us to have two separate callbacks... */
992 /* ...one for the ASCII translateable keypresses... */
995 fgSetWindow( window );
996 fgState.Modifiers = fgPlatformGetModifiers( event.xkey.state );
997 keyboard_cb( asciiCode[ 0 ],
998 event.xkey.x, event.xkey.y
1000 fgState.Modifiers = INVALID_MODIFIERS;
1008 * ...and one for all the others, which need to be
1009 * translated to GLUT_KEY_Xs...
1013 case XK_F1: special = GLUT_KEY_F1; break;
1014 case XK_F2: special = GLUT_KEY_F2; break;
1015 case XK_F3: special = GLUT_KEY_F3; break;
1016 case XK_F4: special = GLUT_KEY_F4; break;
1017 case XK_F5: special = GLUT_KEY_F5; break;
1018 case XK_F6: special = GLUT_KEY_F6; break;
1019 case XK_F7: special = GLUT_KEY_F7; break;
1020 case XK_F8: special = GLUT_KEY_F8; break;
1021 case XK_F9: special = GLUT_KEY_F9; break;
1022 case XK_F10: special = GLUT_KEY_F10; break;
1023 case XK_F11: special = GLUT_KEY_F11; break;
1024 case XK_F12: special = GLUT_KEY_F12; break;
1027 case XK_Left: special = GLUT_KEY_LEFT; break;
1029 case XK_Right: special = GLUT_KEY_RIGHT; break;
1031 case XK_Up: special = GLUT_KEY_UP; break;
1033 case XK_Down: special = GLUT_KEY_DOWN; break;
1036 case XK_Prior: special = GLUT_KEY_PAGE_UP; break;
1038 case XK_Next: special = GLUT_KEY_PAGE_DOWN; break;
1040 case XK_Home: special = GLUT_KEY_HOME; break;
1042 case XK_End: special = GLUT_KEY_END; break;
1044 case XK_Insert: special = GLUT_KEY_INSERT; break;
1046 case XK_Num_Lock : special = GLUT_KEY_NUM_LOCK; break;
1047 case XK_KP_Begin : special = GLUT_KEY_BEGIN; break;
1048 case XK_KP_Delete: special = GLUT_KEY_DELETE; break;
1050 case XK_Shift_L: special = GLUT_KEY_SHIFT_L; break;
1051 case XK_Shift_R: special = GLUT_KEY_SHIFT_R; break;
1052 case XK_Control_L: special = GLUT_KEY_CTRL_L; break;
1053 case XK_Control_R: special = GLUT_KEY_CTRL_R; break;
1054 case XK_Alt_L: special = GLUT_KEY_ALT_L; break;
1055 case XK_Alt_R: special = GLUT_KEY_ALT_R; break;
1059 * Execute the callback (if one has been specified),
1060 * given that the special code seems to be valid...
1062 if( special_cb && (special != -1) )
1064 fgSetWindow( window );
1065 fgState.Modifiers = fgPlatformGetModifiers( event.xkey.state );
1066 special_cb( special, event.xkey.x, event.xkey.y );
1067 fgState.Modifiers = INVALID_MODIFIERS;
1074 case ReparentNotify:
1075 break; /* XXX Should disable this event */
1082 /* enter handling of Extension Events here */
1083 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
1084 fgHandleExtensionEvents( &event );
1092 static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg)
1094 return xev->type == MotionNotify;
1097 void fgPlatformMainLoopPreliminaryWork ( void )