[Xcb] Ideal event handling
Barton C Massey
bart at cs.pdx.edu
Tue Aug 9 18:25:05 EST 2005
I'll respond with 30 seconds of thought to your 17 hours of
musings---it's late and I have an early meeting
tomorrow. :-)
Another way to look at what Travis/GDK timestamps and the X
error association cases want is this: A bunch of things in X
that probably should be replies to requests are instead
asynchronous events or errors. I think this is because of
X's hard constraint that replies must be delivered in
request order.
It might be reasonable to allow some special kind of XCB
requests for specific cases, whose response cookie is in
fact a cookie that will match a particular X event. It
might also be reasonable to have any forced cookie
potentially return an X error at that point (but don't we
already do this?).
I think most of the machinery to make this work is already
in XCB (although it would need some analysis). This
mechanism *might* provide a clean separation between events
that are generated in response to requests
(e.g. ClearRectangle) and events that are external and thus
truly asynchronous.
None of this handles the pointer event collapse case, of
course. I think Jamey has the right idea about this one.
Thoughts and comments?
Bart
In message <1123571096.10370.325.camel at id.minilop.net> you wrote:
> Travis asked a simple question about how to port code using XIfEvent to
> XCB, and here I'm about to try to explain exactly how I think event
> handling should work in X apps generally. I got bogged down for a while
> in details of Gdk's implementation, so lets start by speaking completely
> generically.
>
> Xlib exposes to callers the presence of an event queue, allowing
> operations like XIfEvent that traverse the entire queue looking for an
> event matching a particular set of criteria. Although XCB also has a
> queue of events internally, it does not expose that queue publicly:
> callers can only remove the first available event, in either a blocking
> or non-blocking mode. [1] We expect this decision to allow us some
> implementation flexibility later.
>
> The simplest strategy for porting Xlib code that relies on the event
> queue is to write your own event queue that supports the searching
> operations you need. Given a trivial linked list implementation, these
> operations are really easy to write, so this part of a porting effort
won't take long. I hope you can guess by now that I don't think this is
> a very good strategy, though. Consider that the event accessed out of
> the queue most often is the first one: these queue manipulation
> functions are used rarely. We ought to be able to devise targeted
> solutions suited to those rare occasions.
>
> So lets talk requirements. Why do people use functions like XIfEvent,
> and how *should* such code be ported to XCB? A common reason shows up in
> code that makes a request that should trigger an event, then wants that
> event. Travis' example is one of the simplest I know of:
>
> XChangeProperty (xdisplay, xwindow, timestamp_prop_atom,
> timestamp_prop_atom,
> 8, PropModeReplace, &c, 1);
> XIfEvent (xdisplay, &xevent,
> timestamp_predicate, GUINT_TO_POINTER(xwindow));
>
> Last fall, when I was exchanging a bunch of e-mail with Robert Bragg,
> author of MetaWM (http://metawm.sourceforge.net/), he raised a similar
> issue. When a window manager attempts to select for SubstructureRedirect
> events on the root window, it needs to check for an error in response.
> (Xlib doesn't intermingle events and errors like XCB does, so XIfEvent
> wouldn't solve this problem for an Xlib app, but the similarities are
> clear.)
>
> Another common reason for using functions like XIfEvent is to collapse
> sequences of related events, to avoid making wasted requests in response
> to old but not-yet-processed events. The canonical example seems to be
> compressing pointer motion events. Gdk does this under special
> circumstances, as do at least some window managers.
>
> In all of these cases, it seems to me that the problem is a desire to
> peer into the future, rather than making decisions based solely on the
> past. When viewed that way, this is obviously an insane plan, no?
>
> Consider Expose event handling. Many X applications, including all those
> using Gdk and presumably Qt, don't immediately paint when they process
> an expose event: they add the exposed rectangle to a "region" data
> structure and go on to the next event. At some point in the future, they
> examine that region and repaint everything inside of it. This is
> idiomatic for X: you read some Xlib event loops, you learn to recognize
> this pattern.
>
> Gdk does *precisely* the right thing with these exposure regions: on the
> first Expose event, it registers a low-priority callback function for
> when the Glib event loop becomes idle, and repaints all the collected
> exposure regions then. Applications can choose to repaint selected
> windows sooner if desired, and one could imagine using a timer to ensure
> a maximum response time, but you're guaranteed at least to get repaints
> as soon as there's nothing else to do.
>
> I claim that X applications should follow the same pattern for all cases
> where current applications try to look into the future. The programmer
> can take advantage of knowledge about the particular kind of event being
> compressed: in the Expose event case a Region data structure is better
> than a list of events, for example.
>
>
> So how does this apply to Gdk? (The following discussion entirely
> applies equally well to both Xlib and XCB, since Xlib certainly allows
> you to ignore all "future" events until you reach them in the course of
> normal processing. I suggest that these changes would be reasonable ones
> to apply to the existing Xlib backend of Gdk, on their own merits and
> independent of making the XCB port easier.)
>
> When Gdk compresses MotionNotify and ButtonRelease events, it's trying
> to remain responsive while minimizing the number of calls to a function
> that updates the position of some widget. The goal is to call update_pos
> whenever either a ButtonRelease is encountered, or there aren't any more
> MotionNotify events *in the queue*. Obviously, if a drag is in progress
> Gdk should call update_pos from an idle callback, eliminating a bunch of
> code and that irritating call to XCheckIfEvent.
>
> The other calls to XIfEvent and XCheckIfEvent in Gdk are all there
> because they (won't? can't?) just iterate the event loop. As those
> callers walk over the event stream, they should handle the events that
> they're *not* looking for just like the main event loop does. I think
> that gdk_window_add_filter, g_main_iteration, and
> gdk_window_remove_filter ought to allow these cases to be cleaned up,
> though it's possible that there are constraints I don't understand on
> these functions. Here's an example of what I mean for the
> gdk_x11_get_server_time function Travis asked about.
>
> GdkFilterReturn timestamp_filter (GdkXEvent *gdkxevent,
> GdkEvent *event,
> gpointer data)
> {
> XEvent *xevent = (XEvent *) gdkxevent;
> if (xevent->type == PropertyNotify &&
> xevent->xproperty.atom ==
> gdk_x11_get_xatom_by_name_for_display
> (GDK_WINDOW_DISPLAY (event->property.window),
> "GDK_TIMESTAMP_PROP"))
> {
> guint32 *time = data;
> *time = event->property.time;
> return GDK_FILTER_REMOVE;
> }
> return GDK_FILTER_CONTINUE;
> }
>
> guint32 gdk_x11_get_server_time (GdkWindow *window)
> {
> guint32 time = 0;
> ...
>
> gdk_window_add_filter (window, timestamp_filter, &time);
> while (!time)
> g_main_iteration (TRUE);
> gdk_window_remove_filter (window, timestamp_filter,
> &time);
> return time;
> }
>
> A harmless refactoring change that can be made to the Gdk Xlib backend
> replaces XPending calls with calls to gdk_check_xpending. Both of the
> XPending calls that aren't inside gdk_check_xpending itself can be
> replaced with 'gdk_check_xpending (display)'. Then the one remaining
> event function issue for the XCB port is one call each to XPending,
> XPeekEvent, and XNextEvent. You can satisfy all of these with a
> look-ahead buffer of one event. So add this to struct _GdkDisplayX11:
>
> XCBGenericEvent *next_event;
>
> and re-write gdk_check_xpending like so:
>
> static gboolean
> gdk_check_xpending (GdkDisplay *display)
> {
> GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
> if (!display_x11->next_event)
> display_x11->next_event = XCBPollForEvent (...);
> return display_x11->next_event != NULL;
> }
>
> Then, instead of calling XPeekEvent, just look at next_event; and
> instead of calling XNextEvent, copy next_event somewhere and then set
> next_event to NULL. Of course, it's possible that the key auto-repeat
> code that's using XPeekEvent should be rewritten to look backwards
> rather than forwards as well, but given the Glib event source model Gdk
> needs an XPending equivalent anyway, so you might as well use the one
> event lookahead.
>
> There. Porting event handling is easy, see? I just needed seventeen
> hours to write this e-mail, that's all.
>
> OK, that's what I wanted to say. Feedback would be greatly appreciated.
>
> --Jamey
>
> [1] Travis used the terms "queue" and "list" correctly, but I'm fudging.
> When I say that Xlib exposes a queue and XCB doesn't, I mean that Xlib's
> list-backed queue is traversable and XCB's isn't.
>
> _______________________________________________
> xcb mailing list
> xcb at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/xcb
More information about the xcb
mailing list