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)
45 * Try to get the maximum value allowed for ints, falling back to the minimum
46 * guaranteed by ISO C99 if there is no suitable header.
52 # define INT_MAX 32767
56 # define MIN(a,b) (((a)<(b)) ? (a) : (b))
59 /* used in the event handling code to match and discard stale mouse motion events */
60 static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg);
63 * TODO BEFORE THE STABLE RELEASE:
65 * There are some issues concerning window redrawing under X11, and maybe
66 * some events are not handled. The Win32 version lacks some more features,
67 * but seems acceptable for not demanding purposes.
69 * Need to investigate why the X11 version breaks out with an error when
70 * closing a window (using the window manager, not glutDestroyWindow)...
75 fg_time_t fgPlatformSystemTime ( void )
77 #ifdef CLOCK_MONOTONIC
79 clock_gettime(CLOCK_MONOTONIC, &now);
80 return now.tv_nsec/1000000 + now.tv_sec*1000;
81 #elif defined(HAVE_GETTIMEOFDAY)
83 gettimeofday( &now, NULL );
84 return now.tv_usec/1000 + now.tv_sec*1000;
89 * Does the magic required to relinquish the CPU until something interesting
93 void fgPlatformSleepForEvents( fg_time_t msec )
96 * Possibly due to aggressive use of XFlush() and friends,
97 * it is possible to have our socket drained but still have
98 * unprocessed events. (Or, this may just be normal with
99 * X, anyway?) We do non-trivial processing of X events
100 * after the event-reading loop, in any case, so we
101 * need to allow that we may have an empty socket but non-
104 if( ! XPending( fgDisplay.pDisplay.Display ) )
111 socket = ConnectionNumber( fgDisplay.pDisplay.Display );
113 FD_SET( socket, &fdset );
114 wait.tv_sec = msec / 1000;
115 wait.tv_usec = (msec % 1000) * 1000;
116 err = select( socket+1, &fdset, NULL, NULL, &wait );
119 if( ( -1 == err ) && ( errno != EINTR ) )
120 fgWarning ( "freeglut select() error: %d", errno );
127 * Returns GLUT modifier mask for the state field of an X11 event.
129 int fgPlatformGetModifiers( int state )
133 if( state & ( ShiftMask | LockMask ) )
134 ret |= GLUT_ACTIVE_SHIFT;
135 if( state & ControlMask )
136 ret |= GLUT_ACTIVE_CTRL;
137 if( state & Mod1Mask )
138 ret |= GLUT_ACTIVE_ALT;
143 static const char* fghTypeToString( int type )
146 case KeyPress: return "KeyPress";
147 case KeyRelease: return "KeyRelease";
148 case ButtonPress: return "ButtonPress";
149 case ButtonRelease: return "ButtonRelease";
150 case MotionNotify: return "MotionNotify";
151 case EnterNotify: return "EnterNotify";
152 case LeaveNotify: return "LeaveNotify";
153 case FocusIn: return "FocusIn";
154 case FocusOut: return "FocusOut";
155 case KeymapNotify: return "KeymapNotify";
156 case Expose: return "Expose";
157 case GraphicsExpose: return "GraphicsExpose";
158 case NoExpose: return "NoExpose";
159 case VisibilityNotify: return "VisibilityNotify";
160 case CreateNotify: return "CreateNotify";
161 case DestroyNotify: return "DestroyNotify";
162 case UnmapNotify: return "UnmapNotify";
163 case MapNotify: return "MapNotify";
164 case MapRequest: return "MapRequest";
165 case ReparentNotify: return "ReparentNotify";
166 case ConfigureNotify: return "ConfigureNotify";
167 case ConfigureRequest: return "ConfigureRequest";
168 case GravityNotify: return "GravityNotify";
169 case ResizeRequest: return "ResizeRequest";
170 case CirculateNotify: return "CirculateNotify";
171 case CirculateRequest: return "CirculateRequest";
172 case PropertyNotify: return "PropertyNotify";
173 case SelectionClear: return "SelectionClear";
174 case SelectionRequest: return "SelectionRequest";
175 case SelectionNotify: return "SelectionNotify";
176 case ColormapNotify: return "ColormapNotify";
177 case ClientMessage: return "ClientMessage";
178 case MappingNotify: return "MappingNotify";
179 default: return "UNKNOWN";
183 static const char* fghBoolToString( Bool b )
185 return b == False ? "False" : "True";
188 static const char* fghNotifyHintToString( char is_hint )
191 case NotifyNormal: return "NotifyNormal";
192 case NotifyHint: return "NotifyHint";
193 default: return "UNKNOWN";
197 static const char* fghNotifyModeToString( int mode )
200 case NotifyNormal: return "NotifyNormal";
201 case NotifyGrab: return "NotifyGrab";
202 case NotifyUngrab: return "NotifyUngrab";
203 case NotifyWhileGrabbed: return "NotifyWhileGrabbed";
204 default: return "UNKNOWN";
208 static const char* fghNotifyDetailToString( int detail )
211 case NotifyAncestor: return "NotifyAncestor";
212 case NotifyVirtual: return "NotifyVirtual";
213 case NotifyInferior: return "NotifyInferior";
214 case NotifyNonlinear: return "NotifyNonlinear";
215 case NotifyNonlinearVirtual: return "NotifyNonlinearVirtual";
216 case NotifyPointer: return "NotifyPointer";
217 case NotifyPointerRoot: return "NotifyPointerRoot";
218 case NotifyDetailNone: return "NotifyDetailNone";
219 default: return "UNKNOWN";
223 static const char* fghVisibilityToString( int state ) {
225 case VisibilityUnobscured: return "VisibilityUnobscured";
226 case VisibilityPartiallyObscured: return "VisibilityPartiallyObscured";
227 case VisibilityFullyObscured: return "VisibilityFullyObscured";
228 default: return "UNKNOWN";
232 static const char* fghConfigureDetailToString( int detail )
235 case Above: return "Above";
236 case Below: return "Below";
237 case TopIf: return "TopIf";
238 case BottomIf: return "BottomIf";
239 case Opposite: return "Opposite";
240 default: return "UNKNOWN";
244 static const char* fghPlaceToString( int place )
247 case PlaceOnTop: return "PlaceOnTop";
248 case PlaceOnBottom: return "PlaceOnBottom";
249 default: return "UNKNOWN";
253 static const char* fghMappingRequestToString( int request )
256 case MappingModifier: return "MappingModifier";
257 case MappingKeyboard: return "MappingKeyboard";
258 case MappingPointer: return "MappingPointer";
259 default: return "UNKNOWN";
263 static const char* fghPropertyStateToString( int state )
266 case PropertyNewValue: return "PropertyNewValue";
267 case PropertyDelete: return "PropertyDelete";
268 default: return "UNKNOWN";
272 static const char* fghColormapStateToString( int state )
275 case ColormapUninstalled: return "ColormapUninstalled";
276 case ColormapInstalled: return "ColormapInstalled";
277 default: return "UNKNOWN";
281 __fg_unused static void fghPrintEvent( XEvent *event )
283 switch( event->type ) {
287 XKeyEvent *e = &event->xkey;
288 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
289 "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
290 "keycode=%u, same_screen=%s", fghTypeToString( e->type ),
291 e->window, e->root, e->subwindow, (unsigned long)e->time,
292 e->x, e->y, e->x_root, e->y_root, e->state, e->keycode,
293 fghBoolToString( e->same_screen ) );
298 case ButtonRelease: {
299 XButtonEvent *e = &event->xbutton;
300 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
301 "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
302 "button=%u, same_screen=%d", fghTypeToString( e->type ),
303 e->window, e->root, e->subwindow, (unsigned long)e->time,
304 e->x, e->y, e->x_root, e->y_root, e->state, e->button,
305 fghBoolToString( e->same_screen ) );
310 XMotionEvent *e = &event->xmotion;
311 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
312 "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
313 "is_hint=%s, same_screen=%d", fghTypeToString( e->type ),
314 e->window, e->root, e->subwindow, (unsigned long)e->time,
315 e->x, e->y, e->x_root, e->y_root, e->state,
316 fghNotifyHintToString( e->is_hint ),
317 fghBoolToString( e->same_screen ) );
323 XCrossingEvent *e = &event->xcrossing;
324 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
325 "(x,y)=(%d,%d), mode=%s, detail=%s, same_screen=%d, "
326 "focus=%d, state=0x%x", fghTypeToString( e->type ),
327 e->window, e->root, e->subwindow, (unsigned long)e->time,
328 e->x, e->y, fghNotifyModeToString( e->mode ),
329 fghNotifyDetailToString( e->detail ), (int)e->same_screen,
330 (int)e->focus, e->state );
336 XFocusChangeEvent *e = &event->xfocus;
337 fgWarning( "%s: window=0x%x, mode=%s, detail=%s",
338 fghTypeToString( e->type ), e->window,
339 fghNotifyModeToString( e->mode ),
340 fghNotifyDetailToString( e->detail ) );
345 XKeymapEvent *e = &event->xkeymap;
346 char buf[32 * 2 + 1];
348 for ( i = 0; i < 32; i++ ) {
349 snprintf( &buf[ i * 2 ], sizeof( buf ) - i * 2,
350 "%02x", e->key_vector[ i ] );
353 fgWarning( "%s: window=0x%x, %s", fghTypeToString( e->type ), e->window,
359 XExposeEvent *e = &event->xexpose;
360 fgWarning( "%s: window=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), "
361 "count=%d", fghTypeToString( e->type ), e->window, e->x,
362 e->y, e->width, e->height, e->count );
366 case GraphicsExpose: {
367 XGraphicsExposeEvent *e = &event->xgraphicsexpose;
368 fgWarning( "%s: drawable=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), "
369 "count=%d, (major_code,minor_code)=(%d,%d)",
370 fghTypeToString( e->type ), e->drawable, e->x, e->y,
371 e->width, e->height, e->count, e->major_code,
377 XNoExposeEvent *e = &event->xnoexpose;
378 fgWarning( "%s: drawable=0x%x, (major_code,minor_code)=(%d,%d)",
379 fghTypeToString( e->type ), e->drawable, e->major_code,
384 case VisibilityNotify: {
385 XVisibilityEvent *e = &event->xvisibility;
386 fgWarning( "%s: window=0x%x, state=%s", fghTypeToString( e->type ),
387 e->window, fghVisibilityToString( e->state) );
392 XCreateWindowEvent *e = &event->xcreatewindow;
393 fgWarning( "%s: (x,y)=(%d,%d), (width,height)=(%d,%d), border_width=%d, "
394 "window=0x%x, override_redirect=%s",
395 fghTypeToString( e->type ), e->x, e->y, e->width, e->height,
396 e->border_width, e->window,
397 fghBoolToString( e->override_redirect ) );
401 case DestroyNotify: {
402 XDestroyWindowEvent *e = &event->xdestroywindow;
403 fgWarning( "%s: event=0x%x, window=0x%x",
404 fghTypeToString( e->type ), e->event, e->window );
409 XUnmapEvent *e = &event->xunmap;
410 fgWarning( "%s: event=0x%x, window=0x%x, from_configure=%s",
411 fghTypeToString( e->type ), e->event, e->window,
412 fghBoolToString( e->from_configure ) );
417 XMapEvent *e = &event->xmap;
418 fgWarning( "%s: event=0x%x, window=0x%x, override_redirect=%s",
419 fghTypeToString( e->type ), e->event, e->window,
420 fghBoolToString( e->override_redirect ) );
425 XMapRequestEvent *e = &event->xmaprequest;
426 fgWarning( "%s: parent=0x%x, window=0x%x",
427 fghTypeToString( event->type ), e->parent, e->window );
431 case ReparentNotify: {
432 XReparentEvent *e = &event->xreparent;
433 fgWarning( "%s: event=0x%x, window=0x%x, parent=0x%x, (x,y)=(%d,%d), "
434 "override_redirect=%s", fghTypeToString( e->type ),
435 e->event, e->window, e->parent, e->x, e->y,
436 fghBoolToString( e->override_redirect ) );
440 case ConfigureNotify: {
441 XConfigureEvent *e = &event->xconfigure;
442 fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d), "
443 "(width,height)=(%d,%d), border_width=%d, above=0x%x, "
444 "override_redirect=%s", fghTypeToString( e->type ), e->event,
445 e->window, e->x, e->y, e->width, e->height, e->border_width,
446 e->above, fghBoolToString( e->override_redirect ) );
450 case ConfigureRequest: {
451 XConfigureRequestEvent *e = &event->xconfigurerequest;
452 fgWarning( "%s: parent=0x%x, window=0x%x, (x,y)=(%d,%d), "
453 "(width,height)=(%d,%d), border_width=%d, above=0x%x, "
454 "detail=%s, value_mask=%lx", fghTypeToString( e->type ),
455 e->parent, e->window, e->x, e->y, e->width, e->height,
456 e->border_width, e->above,
457 fghConfigureDetailToString( e->detail ), e->value_mask );
461 case GravityNotify: {
462 XGravityEvent *e = &event->xgravity;
463 fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d)",
464 fghTypeToString( e->type ), e->event, e->window, e->x, e->y );
468 case ResizeRequest: {
469 XResizeRequestEvent *e = &event->xresizerequest;
470 fgWarning( "%s: window=0x%x, (width,height)=(%d,%d)",
471 fghTypeToString( e->type ), e->window, e->width, e->height );
475 case CirculateNotify: {
476 XCirculateEvent *e = &event->xcirculate;
477 fgWarning( "%s: event=0x%x, window=0x%x, place=%s",
478 fghTypeToString( e->type ), e->event, e->window,
479 fghPlaceToString( e->place ) );
483 case CirculateRequest: {
484 XCirculateRequestEvent *e = &event->xcirculaterequest;
485 fgWarning( "%s: parent=0x%x, window=0x%x, place=%s",
486 fghTypeToString( e->type ), e->parent, e->window,
487 fghPlaceToString( e->place ) );
491 case PropertyNotify: {
492 XPropertyEvent *e = &event->xproperty;
493 fgWarning( "%s: window=0x%x, atom=%lu, time=%lu, state=%s",
494 fghTypeToString( e->type ), e->window,
495 (unsigned long)e->atom, (unsigned long)e->time,
496 fghPropertyStateToString( e->state ) );
500 case SelectionClear: {
501 XSelectionClearEvent *e = &event->xselectionclear;
502 fgWarning( "%s: window=0x%x, selection=%lu, time=%lu",
503 fghTypeToString( e->type ), e->window,
504 (unsigned long)e->selection, (unsigned long)e->time );
508 case SelectionRequest: {
509 XSelectionRequestEvent *e = &event->xselectionrequest;
510 fgWarning( "%s: owner=0x%x, requestor=0x%x, selection=0x%x, "
511 "target=0x%x, property=%lu, time=%lu",
512 fghTypeToString( e->type ), e->owner, e->requestor,
513 (unsigned long)e->selection, (unsigned long)e->target,
514 (unsigned long)e->property, (unsigned long)e->time );
518 case SelectionNotify: {
519 XSelectionEvent *e = &event->xselection;
520 fgWarning( "%s: requestor=0x%x, selection=0x%x, target=0x%x, "
521 "property=%lu, time=%lu", fghTypeToString( e->type ),
522 e->requestor, (unsigned long)e->selection,
523 (unsigned long)e->target, (unsigned long)e->property,
524 (unsigned long)e->time );
528 case ColormapNotify: {
529 XColormapEvent *e = &event->xcolormap;
530 fgWarning( "%s: window=0x%x, colormap=%lu, new=%s, state=%s",
531 fghTypeToString( e->type ), e->window,
532 (unsigned long)e->colormap, fghBoolToString( e->new ),
533 fghColormapStateToString( e->state ) );
537 case ClientMessage: {
538 XClientMessageEvent *e = &event->xclient;
541 char* end = buf + sizeof( buf );
543 switch( e->format ) {
545 for ( i = 0; i < 20; i++, p += 3 ) {
546 snprintf( p, end - p, " %02x", e->data.b[ i ] );
550 for ( i = 0; i < 10; i++, p += 5 ) {
551 snprintf( p, end - p, " %04x", e->data.s[ i ] );
555 for ( i = 0; i < 5; i++, p += 9 ) {
556 snprintf( p, end - p, " %08lx", e->data.l[ i ] );
561 fgWarning( "%s: window=0x%x, message_type=%lu, format=%d, data=(%s )",
562 fghTypeToString( e->type ), e->window,
563 (unsigned long)e->message_type, e->format, buf );
567 case MappingNotify: {
568 XMappingEvent *e = &event->xmapping;
569 fgWarning( "%s: window=0x%x, request=%s, first_keycode=%d, count=%d",
570 fghTypeToString( e->type ), e->window,
571 fghMappingRequestToString( e->request ), e->first_keycode,
577 fgWarning( "%s", fghTypeToString( event->type ) );
584 void fgPlatformProcessSingleEvent ( void )
589 /* This code was repeated constantly, so here it goes into a definition: */
590 #define GETWINDOW(a) \
591 window = fgWindowByHandle( event.a.window ); \
592 if( window == NULL ) \
595 #define GETMOUSE(a) \
596 window->State.MouseX = event.a.x; \
597 window->State.MouseY = event.a.y;
599 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
601 while( XPending( fgDisplay.pDisplay.Display ) )
603 XNextEvent( fgDisplay.pDisplay.Display, &event );
605 fghPrintEvent( &event );
611 if (fgStructure.CurrentWindow)
612 if(fgIsSpaceballXEvent(&event)) {
613 fgSpaceballHandleXEvent(&event);
616 /* Destroy the window when the WM_DELETE_WINDOW message arrives */
617 if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.pDisplay.DeleteWindow )
619 GETWINDOW( xclient );
621 fgDestroyWindow ( window );
623 if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
628 else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
629 fgState.ExecState = GLUT_EXEC_STATE_STOP;
636 * CreateNotify causes a configure-event so that sub-windows are
637 * handled compatibly with GLUT. Otherwise, your sub-windows
638 * (in freeglut only) will not get an initial reshape event,
639 * which can break things.
641 * GLUT presumably does this because it generally tries to treat
642 * sub-windows the same as windows.
645 case ConfigureNotify:
648 if( event.type == CreateNotify ) {
649 GETWINDOW( xcreatewindow );
650 width = event.xcreatewindow.width;
651 height = event.xcreatewindow.height;
653 GETWINDOW( xconfigure );
654 width = event.xconfigure.width;
655 height = event.xconfigure.height;
658 if( ( width != window->State.pWState.OldWidth ) ||
659 ( height != window->State.pWState.OldHeight ) )
661 SFG_Window *current_window = fgStructure.CurrentWindow;
663 window->State.pWState.OldWidth = width;
664 window->State.pWState.OldHeight = height;
665 if( FETCH_WCB( *window, Reshape ) )
666 INVOKE_WCB( *window, Reshape, ( width, height ) );
669 fgSetWindow( window );
670 glViewport( 0, 0, width, height );
672 glutPostRedisplay( );
674 fgSetWindow( current_window );
681 * This is sent to confirm the XDestroyWindow call.
683 * XXX WHY is this commented out? Should we re-enable it?
685 /* fgAddToWindowDestroyList ( window ); */
690 * We are too dumb to process partial exposes...
692 * XXX Well, we could do it. However, it seems to only
693 * XXX be potentially useful for single-buffered (since
694 * XXX double-buffered does not respect viewport when we
695 * XXX do a buffer-swap).
698 if( event.xexpose.count == 0 )
700 GETWINDOW( xexpose );
701 window->State.Redisplay = GL_TRUE;
709 /* We get this when iconifying a window. */
711 INVOKE_WCB( *window, WindowStatus, ( GLUT_HIDDEN ) );
712 window->State.Visible = GL_FALSE;
717 * Have the client's keyboard knowledge updated (xlib.ps,
718 * page 206, says that's a good thing to do)
720 XRefreshKeyboardMapping( (XMappingEvent *) &event );
723 case VisibilityNotify:
726 * Sending this event, the X server can notify us that the window
727 * has just acquired one of the three possible visibility states:
728 * VisibilityUnobscured, VisibilityPartiallyObscured or
729 * VisibilityFullyObscured. Note that we DO NOT receive a
730 * VisibilityNotify event when iconifying a window, we only get an
733 GETWINDOW( xvisibility );
734 switch( event.xvisibility.state )
736 case VisibilityUnobscured:
737 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_RETAINED ) );
738 window->State.Visible = GL_TRUE;
741 case VisibilityPartiallyObscured:
742 INVOKE_WCB( *window, WindowStatus,
743 ( GLUT_PARTIALLY_RETAINED ) );
744 window->State.Visible = GL_TRUE;
747 case VisibilityFullyObscured:
748 INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_COVERED ) );
749 window->State.Visible = GL_FALSE;
753 fgWarning( "Unknown X visibility state: %d",
754 event.xvisibility.state );
762 GETWINDOW( xcrossing );
763 GETMOUSE( xcrossing );
764 if( ( event.type == LeaveNotify ) && window->IsMenu &&
765 window->ActiveMenu && window->ActiveMenu->IsActive )
766 fgUpdateMenuHighlight( window->ActiveMenu );
768 INVOKE_WCB( *window, Entry, ( ( EnterNotify == event.type ) ?
775 /* if GLUT_SKIP_STALE_MOTION_EVENTS is true, then discard all but
776 * the last motion event from the queue
778 if(fgState.SkipStaleMotion) {
779 while(XCheckIfEvent(fgDisplay.pDisplay.Display, &event, match_motion, 0));
782 GETWINDOW( xmotion );
785 if( window->ActiveMenu )
787 if( window == window->ActiveMenu->ParentWindow )
789 window->ActiveMenu->Window->State.MouseX =
790 event.xmotion.x_root - window->ActiveMenu->X;
791 window->ActiveMenu->Window->State.MouseY =
792 event.xmotion.y_root - window->ActiveMenu->Y;
795 fgUpdateMenuHighlight( window->ActiveMenu );
801 * XXX For more than 5 buttons, just check {event.xmotion.state},
802 * XXX rather than a host of bit-masks? Or maybe we need to
803 * XXX track ButtonPress/ButtonRelease events in our own
806 fgState.Modifiers = fgPlatformGetModifiers( event.xmotion.state );
807 if ( event.xmotion.state & ( Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask ) ) {
808 INVOKE_WCB( *window, Motion, ( event.xmotion.x,
811 INVOKE_WCB( *window, Passive, ( event.xmotion.x,
814 fgState.Modifiers = INVALID_MODIFIERS;
821 GLboolean pressed = GL_TRUE;
824 if( event.type == ButtonRelease )
828 * A mouse button has been pressed or released. Traditionally,
829 * break if the window was found within the freeglut structures.
831 GETWINDOW( xbutton );
835 * An X button (at least in XFree86) is numbered from 1.
836 * A GLUT button is numbered from 0.
837 * Old GLUT passed through buttons other than just the first
838 * three, though it only gave symbolic names and official
839 * support to the first three.
841 button = event.xbutton.button - 1;
844 * Do not execute the application's mouse callback if a menu
845 * is hooked to this button. In that case an appropriate
846 * private call should be generated.
848 if( fgCheckActiveMenu( window, button, pressed,
849 event.xbutton.x_root, event.xbutton.y_root ) )
853 * Check if there is a mouse or mouse wheel callback hooked to the
856 if( ! FETCH_WCB( *window, Mouse ) &&
857 ! FETCH_WCB( *window, MouseWheel ) )
860 fgState.Modifiers = fgPlatformGetModifiers( event.xbutton.state );
862 /* Finally execute the mouse or mouse wheel callback */
863 if( ( button < glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS ) ) || ( ! FETCH_WCB( *window, MouseWheel ) ) )
864 INVOKE_WCB( *window, Mouse, ( button,
865 pressed ? GLUT_DOWN : GLUT_UP,
872 * Map 4 and 5 to wheel zero; EVEN to +1, ODD to -1
873 * " 6 and 7 " " one; ...
875 * XXX This *should* be behind some variables/macros,
876 * XXX since the order and numbering isn't certain
877 * XXX See XFree86 configuration docs (even back in the
878 * XXX 3.x days, and especially with 4.x).
880 * XXX Note that {button} has already been decremented
881 * XXX in mapping from X button numbering to GLUT.
883 * XXX Should add support for partial wheel turns as Windows does -- 5/27/11
885 int wheel_number = (button - glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS )) / 2;
891 INVOKE_WCB( *window, MouseWheel, ( wheel_number,
897 fgState.Modifiers = INVALID_MODIFIERS;
904 FGCBKeyboard keyboard_cb;
905 FGCBSpecial special_cb;
910 /* Detect auto repeated keys, if configured globally or per-window */
912 if ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE )
914 if (event.type==KeyRelease)
917 * Look at X11 keystate to detect repeat mode.
918 * While X11 says the key is actually held down, we'll ignore KeyRelease/KeyPress pairs.
922 XQueryKeymap( fgDisplay.pDisplay.Display, keys ); /* Look at X11 keystate to detect repeat mode */
924 if ( event.xkey.keycode<256 ) /* XQueryKeymap is limited to 256 keycodes */
926 if ( keys[event.xkey.keycode>>3] & (1<<(event.xkey.keycode%8)) )
927 window->State.KeyRepeating = GL_TRUE;
929 window->State.KeyRepeating = GL_FALSE;
934 window->State.KeyRepeating = GL_FALSE;
936 /* Cease processing this event if it is auto repeated */
938 if (window->State.KeyRepeating)
940 if (event.type == KeyPress) window->State.KeyRepeating = GL_FALSE;
944 if( event.type == KeyPress )
946 keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, Keyboard ));
947 special_cb = (FGCBSpecial) ( FETCH_WCB( *window, Special ));
951 keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, KeyboardUp ));
952 special_cb = (FGCBSpecial) ( FETCH_WCB( *window, SpecialUp ));
955 /* Is there a keyboard/special callback hooked for this window? */
956 if( keyboard_cb || special_cb )
958 XComposeStatus composeStatus;
959 char asciiCode[ 32 ];
963 /* Check for the ASCII/KeySym codes associated with the event: */
964 len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode),
965 &keySym, &composeStatus
968 /* GLUT API tells us to have two separate callbacks... */
971 /* ...one for the ASCII translateable keypresses... */
974 fgSetWindow( window );
975 fgState.Modifiers = fgPlatformGetModifiers( event.xkey.state );
976 keyboard_cb( asciiCode[ 0 ],
977 event.xkey.x, event.xkey.y
979 fgState.Modifiers = INVALID_MODIFIERS;
987 * ...and one for all the others, which need to be
988 * translated to GLUT_KEY_Xs...
992 case XK_F1: special = GLUT_KEY_F1; break;
993 case XK_F2: special = GLUT_KEY_F2; break;
994 case XK_F3: special = GLUT_KEY_F3; break;
995 case XK_F4: special = GLUT_KEY_F4; break;
996 case XK_F5: special = GLUT_KEY_F5; break;
997 case XK_F6: special = GLUT_KEY_F6; break;
998 case XK_F7: special = GLUT_KEY_F7; break;
999 case XK_F8: special = GLUT_KEY_F8; break;
1000 case XK_F9: special = GLUT_KEY_F9; break;
1001 case XK_F10: special = GLUT_KEY_F10; break;
1002 case XK_F11: special = GLUT_KEY_F11; break;
1003 case XK_F12: special = GLUT_KEY_F12; break;
1006 case XK_Left: special = GLUT_KEY_LEFT; break;
1008 case XK_Right: special = GLUT_KEY_RIGHT; break;
1010 case XK_Up: special = GLUT_KEY_UP; break;
1012 case XK_Down: special = GLUT_KEY_DOWN; break;
1015 case XK_Prior: special = GLUT_KEY_PAGE_UP; break;
1017 case XK_Next: special = GLUT_KEY_PAGE_DOWN; break;
1019 case XK_Home: special = GLUT_KEY_HOME; break;
1021 case XK_End: special = GLUT_KEY_END; break;
1023 case XK_Insert: special = GLUT_KEY_INSERT; break;
1025 case XK_Num_Lock : special = GLUT_KEY_NUM_LOCK; break;
1026 case XK_KP_Begin : special = GLUT_KEY_BEGIN; break;
1027 case XK_KP_Delete: special = GLUT_KEY_DELETE; break;
1029 case XK_Shift_L: special = GLUT_KEY_SHIFT_L; break;
1030 case XK_Shift_R: special = GLUT_KEY_SHIFT_R; break;
1031 case XK_Control_L: special = GLUT_KEY_CTRL_L; break;
1032 case XK_Control_R: special = GLUT_KEY_CTRL_R; break;
1033 case XK_Alt_L: special = GLUT_KEY_ALT_L; break;
1034 case XK_Alt_R: special = GLUT_KEY_ALT_R; break;
1038 * Execute the callback (if one has been specified),
1039 * given that the special code seems to be valid...
1041 if( special_cb && (special != -1) )
1043 fgSetWindow( window );
1044 fgState.Modifiers = fgPlatformGetModifiers( event.xkey.state );
1045 special_cb( special, event.xkey.x, event.xkey.y );
1046 fgState.Modifiers = INVALID_MODIFIERS;
1053 case ReparentNotify:
1054 break; /* XXX Should disable this event */
1061 /* enter handling of Extension Events here */
1062 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
1063 fgHandleExtensionEvents( &event );
1071 static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg)
1073 return xev->type == MotionNotify;
1076 void fgPlatformMainLoopPreliminaryWork ( void )