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 extern void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify);
60 extern void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify);
61 extern void fgPlatformFullScreenToggle( SFG_Window *win );
62 extern void fgPlatformPositionWindow( SFG_Window *window, int x, int y );
63 extern void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height );
64 extern void fgPlatformPushWindow( SFG_Window *window );
65 extern void fgPlatformPopWindow( SFG_Window *window );
66 extern void fgPlatformHideWindow( SFG_Window *window );
67 extern void fgPlatformIconifyWindow( SFG_Window *window );
68 extern void fgPlatformShowWindow( SFG_Window *window );
70 /* used in the event handling code to match and discard stale mouse motion events */
71 static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg);
74 * TODO BEFORE THE STABLE RELEASE:
76 * There are some issues concerning window redrawing under X11, and maybe
77 * some events are not handled.
79 * Need to investigate why the X11 version breaks out with an error when
80 * closing a window (using the window manager, not glutDestroyWindow)...
85 fg_time_t fgPlatformSystemTime ( void )
87 #ifdef CLOCK_MONOTONIC
89 clock_gettime(CLOCK_MONOTONIC, &now);
90 return now.tv_nsec/1000000 + now.tv_sec*1000;
91 #elif defined(HAVE_GETTIMEOFDAY)
93 gettimeofday( &now, NULL );
94 return now.tv_usec/1000 + now.tv_sec*1000;
99 * Does the magic required to relinquish the CPU until something interesting
103 void fgPlatformSleepForEvents( fg_time_t msec )
106 * Possibly due to aggressive use of XFlush() and friends,
107 * it is possible to have our socket drained but still have
108 * unprocessed events. (Or, this may just be normal with
109 * X, anyway?) We do non-trivial processing of X events
110 * after the event-reading loop, in any case, so we
111 * need to allow that we may have an empty socket but non-
114 if( ! XPending( fgDisplay.pDisplay.Display ) )
121 socket = ConnectionNumber( fgDisplay.pDisplay.Display );
123 FD_SET( socket, &fdset );
124 wait.tv_sec = msec / 1000;
125 wait.tv_usec = (msec % 1000) * 1000;
126 err = select( socket+1, &fdset, NULL, NULL, &wait );
129 if( ( -1 == err ) && ( errno != EINTR ) )
130 fgWarning ( "freeglut select() error: %d", errno );
137 * Returns GLUT modifier mask for the state field of an X11 event.
139 int fgPlatformGetModifiers( int state )
143 if( state & ( ShiftMask | LockMask ) )
144 ret |= GLUT_ACTIVE_SHIFT;
145 if( state & ControlMask )
146 ret |= GLUT_ACTIVE_CTRL;
147 if( state & Mod1Mask )
148 ret |= GLUT_ACTIVE_ALT;
153 static const char* fghTypeToString( int type )
156 case KeyPress: return "KeyPress";
157 case KeyRelease: return "KeyRelease";
158 case ButtonPress: return "ButtonPress";
159 case ButtonRelease: return "ButtonRelease";
160 case MotionNotify: return "MotionNotify";
161 case EnterNotify: return "EnterNotify";
162 case LeaveNotify: return "LeaveNotify";
163 case FocusIn: return "FocusIn";
164 case FocusOut: return "FocusOut";
165 case KeymapNotify: return "KeymapNotify";
166 case Expose: return "Expose";
167 case GraphicsExpose: return "GraphicsExpose";
168 case NoExpose: return "NoExpose";
169 case VisibilityNotify: return "VisibilityNotify";
170 case CreateNotify: return "CreateNotify";
171 case DestroyNotify: return "DestroyNotify";
172 case UnmapNotify: return "UnmapNotify";
173 case MapNotify: return "MapNotify";
174 case MapRequest: return "MapRequest";
175 case ReparentNotify: return "ReparentNotify";
176 case ConfigureNotify: return "ConfigureNotify";
177 case ConfigureRequest: return "ConfigureRequest";
178 case GravityNotify: return "GravityNotify";
179 case ResizeRequest: return "ResizeRequest";
180 case CirculateNotify: return "CirculateNotify";
181 case CirculateRequest: return "CirculateRequest";
182 case PropertyNotify: return "PropertyNotify";
183 case SelectionClear: return "SelectionClear";
184 case SelectionRequest: return "SelectionRequest";
185 case SelectionNotify: return "SelectionNotify";
186 case ColormapNotify: return "ColormapNotify";
187 case ClientMessage: return "ClientMessage";
188 case MappingNotify: return "MappingNotify";
189 default: return "UNKNOWN";
193 static const char* fghBoolToString( Bool b )
195 return b == False ? "False" : "True";
198 static const char* fghNotifyHintToString( char is_hint )
201 case NotifyNormal: return "NotifyNormal";
202 case NotifyHint: return "NotifyHint";
203 default: return "UNKNOWN";
207 static const char* fghNotifyModeToString( int mode )
210 case NotifyNormal: return "NotifyNormal";
211 case NotifyGrab: return "NotifyGrab";
212 case NotifyUngrab: return "NotifyUngrab";
213 case NotifyWhileGrabbed: return "NotifyWhileGrabbed";
214 default: return "UNKNOWN";
218 static const char* fghNotifyDetailToString( int detail )
221 case NotifyAncestor: return "NotifyAncestor";
222 case NotifyVirtual: return "NotifyVirtual";
223 case NotifyInferior: return "NotifyInferior";
224 case NotifyNonlinear: return "NotifyNonlinear";
225 case NotifyNonlinearVirtual: return "NotifyNonlinearVirtual";
226 case NotifyPointer: return "NotifyPointer";
227 case NotifyPointerRoot: return "NotifyPointerRoot";
228 case NotifyDetailNone: return "NotifyDetailNone";
229 default: return "UNKNOWN";
233 static const char* fghVisibilityToString( int state ) {
235 case VisibilityUnobscured: return "VisibilityUnobscured";
236 case VisibilityPartiallyObscured: return "VisibilityPartiallyObscured";
237 case VisibilityFullyObscured: return "VisibilityFullyObscured";
238 default: return "UNKNOWN";
242 static const char* fghConfigureDetailToString( int detail )
245 case Above: return "Above";
246 case Below: return "Below";
247 case TopIf: return "TopIf";
248 case BottomIf: return "BottomIf";
249 case Opposite: return "Opposite";
250 default: return "UNKNOWN";
254 static const char* fghPlaceToString( int place )
257 case PlaceOnTop: return "PlaceOnTop";
258 case PlaceOnBottom: return "PlaceOnBottom";
259 default: return "UNKNOWN";
263 static const char* fghMappingRequestToString( int request )
266 case MappingModifier: return "MappingModifier";
267 case MappingKeyboard: return "MappingKeyboard";
268 case MappingPointer: return "MappingPointer";
269 default: return "UNKNOWN";
273 static const char* fghPropertyStateToString( int state )
276 case PropertyNewValue: return "PropertyNewValue";
277 case PropertyDelete: return "PropertyDelete";
278 default: return "UNKNOWN";
282 static const char* fghColormapStateToString( int state )
285 case ColormapUninstalled: return "ColormapUninstalled";
286 case ColormapInstalled: return "ColormapInstalled";
287 default: return "UNKNOWN";
291 __fg_unused static void fghPrintEvent( XEvent *event )
293 switch( event->type ) {
297 XKeyEvent *e = &event->xkey;
298 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
299 "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
300 "keycode=%u, same_screen=%s", fghTypeToString( e->type ),
301 e->window, e->root, e->subwindow, (unsigned long)e->time,
302 e->x, e->y, e->x_root, e->y_root, e->state, e->keycode,
303 fghBoolToString( e->same_screen ) );
308 case ButtonRelease: {
309 XButtonEvent *e = &event->xbutton;
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 "button=%u, 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, e->button,
315 fghBoolToString( e->same_screen ) );
320 XMotionEvent *e = &event->xmotion;
321 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
322 "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, "
323 "is_hint=%s, same_screen=%d", fghTypeToString( e->type ),
324 e->window, e->root, e->subwindow, (unsigned long)e->time,
325 e->x, e->y, e->x_root, e->y_root, e->state,
326 fghNotifyHintToString( e->is_hint ),
327 fghBoolToString( e->same_screen ) );
333 XCrossingEvent *e = &event->xcrossing;
334 fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, "
335 "(x,y)=(%d,%d), mode=%s, detail=%s, same_screen=%d, "
336 "focus=%d, state=0x%x", fghTypeToString( e->type ),
337 e->window, e->root, e->subwindow, (unsigned long)e->time,
338 e->x, e->y, fghNotifyModeToString( e->mode ),
339 fghNotifyDetailToString( e->detail ), (int)e->same_screen,
340 (int)e->focus, e->state );
346 XFocusChangeEvent *e = &event->xfocus;
347 fgWarning( "%s: window=0x%x, mode=%s, detail=%s",
348 fghTypeToString( e->type ), e->window,
349 fghNotifyModeToString( e->mode ),
350 fghNotifyDetailToString( e->detail ) );
355 XKeymapEvent *e = &event->xkeymap;
356 char buf[32 * 2 + 1];
358 for ( i = 0; i < 32; i++ ) {
359 snprintf( &buf[ i * 2 ], sizeof( buf ) - i * 2,
360 "%02x", e->key_vector[ i ] );
363 fgWarning( "%s: window=0x%x, %s", fghTypeToString( e->type ), e->window,
369 XExposeEvent *e = &event->xexpose;
370 fgWarning( "%s: window=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), "
371 "count=%d", fghTypeToString( e->type ), e->window, e->x,
372 e->y, e->width, e->height, e->count );
376 case GraphicsExpose: {
377 XGraphicsExposeEvent *e = &event->xgraphicsexpose;
378 fgWarning( "%s: drawable=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), "
379 "count=%d, (major_code,minor_code)=(%d,%d)",
380 fghTypeToString( e->type ), e->drawable, e->x, e->y,
381 e->width, e->height, e->count, e->major_code,
387 XNoExposeEvent *e = &event->xnoexpose;
388 fgWarning( "%s: drawable=0x%x, (major_code,minor_code)=(%d,%d)",
389 fghTypeToString( e->type ), e->drawable, e->major_code,
394 case VisibilityNotify: {
395 XVisibilityEvent *e = &event->xvisibility;
396 fgWarning( "%s: window=0x%x, state=%s", fghTypeToString( e->type ),
397 e->window, fghVisibilityToString( e->state) );
402 XCreateWindowEvent *e = &event->xcreatewindow;
403 fgWarning( "%s: (x,y)=(%d,%d), (width,height)=(%d,%d), border_width=%d, "
404 "window=0x%x, override_redirect=%s",
405 fghTypeToString( e->type ), e->x, e->y, e->width, e->height,
406 e->border_width, e->window,
407 fghBoolToString( e->override_redirect ) );
411 case DestroyNotify: {
412 XDestroyWindowEvent *e = &event->xdestroywindow;
413 fgWarning( "%s: event=0x%x, window=0x%x",
414 fghTypeToString( e->type ), e->event, e->window );
419 XUnmapEvent *e = &event->xunmap;
420 fgWarning( "%s: event=0x%x, window=0x%x, from_configure=%s",
421 fghTypeToString( e->type ), e->event, e->window,
422 fghBoolToString( e->from_configure ) );
427 XMapEvent *e = &event->xmap;
428 fgWarning( "%s: event=0x%x, window=0x%x, override_redirect=%s",
429 fghTypeToString( e->type ), e->event, e->window,
430 fghBoolToString( e->override_redirect ) );
435 XMapRequestEvent *e = &event->xmaprequest;
436 fgWarning( "%s: parent=0x%x, window=0x%x",
437 fghTypeToString( event->type ), e->parent, e->window );
441 case ReparentNotify: {
442 XReparentEvent *e = &event->xreparent;
443 fgWarning( "%s: event=0x%x, window=0x%x, parent=0x%x, (x,y)=(%d,%d), "
444 "override_redirect=%s", fghTypeToString( e->type ),
445 e->event, e->window, e->parent, e->x, e->y,
446 fghBoolToString( e->override_redirect ) );
450 case ConfigureNotify: {
451 XConfigureEvent *e = &event->xconfigure;
452 fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d), "
453 "(width,height)=(%d,%d), border_width=%d, above=0x%x, "
454 "override_redirect=%s", fghTypeToString( e->type ), e->event,
455 e->window, e->x, e->y, e->width, e->height, e->border_width,
456 e->above, fghBoolToString( e->override_redirect ) );
460 case ConfigureRequest: {
461 XConfigureRequestEvent *e = &event->xconfigurerequest;
462 fgWarning( "%s: parent=0x%x, window=0x%x, (x,y)=(%d,%d), "
463 "(width,height)=(%d,%d), border_width=%d, above=0x%x, "
464 "detail=%s, value_mask=%lx", fghTypeToString( e->type ),
465 e->parent, e->window, e->x, e->y, e->width, e->height,
466 e->border_width, e->above,
467 fghConfigureDetailToString( e->detail ), e->value_mask );
471 case GravityNotify: {
472 XGravityEvent *e = &event->xgravity;
473 fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d)",
474 fghTypeToString( e->type ), e->event, e->window, e->x, e->y );
478 case ResizeRequest: {
479 XResizeRequestEvent *e = &event->xresizerequest;
480 fgWarning( "%s: window=0x%x, (width,height)=(%d,%d)",
481 fghTypeToString( e->type ), e->window, e->width, e->height );
485 case CirculateNotify: {
486 XCirculateEvent *e = &event->xcirculate;
487 fgWarning( "%s: event=0x%x, window=0x%x, place=%s",
488 fghTypeToString( e->type ), e->event, e->window,
489 fghPlaceToString( e->place ) );
493 case CirculateRequest: {
494 XCirculateRequestEvent *e = &event->xcirculaterequest;
495 fgWarning( "%s: parent=0x%x, window=0x%x, place=%s",
496 fghTypeToString( e->type ), e->parent, e->window,
497 fghPlaceToString( e->place ) );
501 case PropertyNotify: {
502 XPropertyEvent *e = &event->xproperty;
503 fgWarning( "%s: window=0x%x, atom=%lu, time=%lu, state=%s",
504 fghTypeToString( e->type ), e->window,
505 (unsigned long)e->atom, (unsigned long)e->time,
506 fghPropertyStateToString( e->state ) );
510 case SelectionClear: {
511 XSelectionClearEvent *e = &event->xselectionclear;
512 fgWarning( "%s: window=0x%x, selection=%lu, time=%lu",
513 fghTypeToString( e->type ), e->window,
514 (unsigned long)e->selection, (unsigned long)e->time );
518 case SelectionRequest: {
519 XSelectionRequestEvent *e = &event->xselectionrequest;
520 fgWarning( "%s: owner=0x%x, requestor=0x%x, selection=0x%x, "
521 "target=0x%x, property=%lu, time=%lu",
522 fghTypeToString( e->type ), e->owner, e->requestor,
523 (unsigned long)e->selection, (unsigned long)e->target,
524 (unsigned long)e->property, (unsigned long)e->time );
528 case SelectionNotify: {
529 XSelectionEvent *e = &event->xselection;
530 fgWarning( "%s: requestor=0x%x, selection=0x%x, target=0x%x, "
531 "property=%lu, time=%lu", fghTypeToString( e->type ),
532 e->requestor, (unsigned long)e->selection,
533 (unsigned long)e->target, (unsigned long)e->property,
534 (unsigned long)e->time );
538 case ColormapNotify: {
539 XColormapEvent *e = &event->xcolormap;
540 fgWarning( "%s: window=0x%x, colormap=%lu, new=%s, state=%s",
541 fghTypeToString( e->type ), e->window,
542 (unsigned long)e->colormap, fghBoolToString( e->new ),
543 fghColormapStateToString( e->state ) );
547 case ClientMessage: {
548 XClientMessageEvent *e = &event->xclient;
551 char* end = buf + sizeof( buf );
553 switch( e->format ) {
555 for ( i = 0; i < 20; i++, p += 3 ) {
556 snprintf( p, end - p, " %02x", e->data.b[ i ] );
560 for ( i = 0; i < 10; i++, p += 5 ) {
561 snprintf( p, end - p, " %04x", e->data.s[ i ] );
565 for ( i = 0; i < 5; i++, p += 9 ) {
566 snprintf( p, end - p, " %08lx", e->data.l[ i ] );
571 fgWarning( "%s: window=0x%x, message_type=%lu, format=%d, data=(%s )",
572 fghTypeToString( e->type ), e->window,
573 (unsigned long)e->message_type, e->format, buf );
577 case MappingNotify: {
578 XMappingEvent *e = &event->xmapping;
579 fgWarning( "%s: window=0x%x, request=%s, first_keycode=%d, count=%d",
580 fghTypeToString( e->type ), e->window,
581 fghMappingRequestToString( e->request ), e->first_keycode,
587 fgWarning( "%s", fghTypeToString( event->type ) );
594 void fgPlatformProcessSingleEvent ( void )
599 /* This code was repeated constantly, so here it goes into a definition: */
600 #define GETWINDOW(a) \
601 window = fgWindowByHandle( event.a.window ); \
602 if( window == NULL ) \
605 #define GETMOUSE(a) \
606 window->State.MouseX = event.a.x; \
607 window->State.MouseY = event.a.y;
609 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
611 while( XPending( fgDisplay.pDisplay.Display ) )
613 XNextEvent( fgDisplay.pDisplay.Display, &event );
615 fghPrintEvent( &event );
621 if (fgStructure.CurrentWindow)
622 if(fgIsSpaceballXEvent(&event)) {
623 fgSpaceballHandleXEvent(&event);
626 /* Destroy the window when the WM_DELETE_WINDOW message arrives */
627 if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.pDisplay.DeleteWindow )
629 GETWINDOW( xclient );
631 fgDestroyWindow ( window );
633 if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
638 else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS )
639 fgState.ExecState = GLUT_EXEC_STATE_STOP;
646 * CreateNotify causes a configure-event so that sub-windows are
647 * handled compatibly with GLUT. Otherwise, your sub-windows
648 * (in freeglut only) will not get an initial reshape event,
649 * which can break things.
651 * GLUT presumably does this because it generally tries to treat
652 * sub-windows the same as windows.
655 case ConfigureNotify:
657 int width, height, x, y;
658 if( event.type == CreateNotify ) {
659 GETWINDOW( xcreatewindow );
660 width = event.xcreatewindow.width;
661 height = event.xcreatewindow.height;
662 x = event.xcreatewindow.x;
663 y = event.xcreatewindow.y;
665 GETWINDOW( xconfigure );
666 width = event.xconfigure.width;
667 height = event.xconfigure.height;
668 x = event.xconfigure.x;
669 y = event.xconfigure.y;
672 /* Update state and call callback, if there was a change */
673 fghOnPositionNotify(window, x, y, GL_FALSE);
674 /* Update state and call callback, if there was a change */
675 fghOnReshapeNotify(window, width, height, GL_FALSE);
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.WorkMask |= GLUT_DISPLAY_WORK;
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, event.xbutton.y ) )
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.pWState.KeyRepeating = GL_TRUE;
929 window->State.pWState.KeyRepeating = GL_FALSE;
934 window->State.pWState.KeyRepeating = GL_FALSE;
936 /* Cease processing this event if it is auto repeated */
938 if (window->State.pWState.KeyRepeating)
940 if (event.type == KeyPress) window->State.pWState.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 )
1081 /* deal with work list items */
1082 void fgPlatformInitWork(SFG_Window* window)
1084 /* Notify windowStatus/visibility, position and size get notified on window creation with message handlers above
1085 * XXX CHECK: do the messages happen too early like on windows, so client code cannot have registered
1086 * a callback yet and the message is thus never received by client?
1087 * -> this is a no-op
1092 void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
1094 if (workMask & GLUT_FULL_SCREEN_WORK)
1095 fgPlatformFullScreenToggle( window );
1096 if (workMask & GLUT_POSITION_WORK)
1097 fgPlatformPositionWindow( window, window->State.DesiredXpos, window->State.DesiredYpos );
1098 if (workMask & GLUT_SIZE_WORK)
1099 fgPlatformReshapeWindow ( window, window->State.DesiredWidth, window->State.DesiredHeight );
1100 if (workMask & GLUT_ZORDER_WORK)
1102 if (window->State.DesiredZOrder < 0)
1103 fgPlatformPushWindow( window );
1105 fgPlatformPopWindow( window );
1109 void fgPlatformVisibilityWork(SFG_Window* window)
1111 /* Visibility status of window gets updated in the window message handlers above
1112 * XXX: is this really the case? check
1114 SFG_Window *win = window;
1115 switch (window->State.DesiredVisibility)
1117 case DesireHiddenState:
1118 fgPlatformHideWindow( window );
1120 case DesireIconicState:
1121 /* Call on top-level window */
1124 fgPlatformIconifyWindow( win );
1126 case DesireNormalState:
1127 fgPlatformShowWindow( window );