X copy and paste

April 05, 2009

X has two clipboard-y behaviors where Windows/Mac OS have one. jwz's doc is quite good for the whole story but what is salient here is that selecting text in app A and middle clicking in app B behaves like a copy and paste.

When you're in the midst of selecting text in app A, it's not repeatedly updating some clipboard data structure; rather, A just claims "the selection" and when B gets the middle click it then communicates with A about what needs to be exchanged.

This is all fine except that in our case the actual selection happend in the renderer subprocess, which doesn't have an X connection and is asynchronous with respect to the process that does have the X connection. It's almost no problem, anyway: when I drag my mouse over a page displayed in the app window, those mouse events are sent over to the renderer and WebKit which recognize that I'm selecting text, and it sends back a "claim the selection" IPC message.

Now say in app B you middle-click. Here's where it gets ugly: we, as the owner of the selection, get a message from X saying "send me the clipboard buffer". But we're the browser process -- we don't have the selection text, the renderer does. Worse, now suppose the renderer decides this is a good time to hang. We have a rule of never doing a blocking wait from the browser to the renderer.

I didn't tell you the whole story. X is asynchronous throughout -- we got a message requesting the clipboard buffer, but we can asynchronously respond later, when we actually have the content to paste. It's just that the high-level GTK API around this is (reasonably) synchronous. (I write reasonable here because it's async in most places that count, such as waiting for other apps to paste into us; here what we need is asynchrony between the system asking us "what was that you said you had ready to paste?" and us responding with data.)

But r13044 fixes that. I haven't looked at it carefully enough to comment on it. The comment to read is:

// When X requests the contents of the clipboard, GTK will emit the
// selection_request_event signal. The default handler would then
// synchronously emit the selection_get signal. However, we want to
// respond to the selection_request_event asynchronously, so we intercept
// the signal in OnSelectionRequest, request the selection text from the
// render view, and return TRUE so the default handler won't be called. Then
// when we get the selection text back from the renderer in
// SetSelectionText() we will call manually the selection_request_event
// default handler.

Between this post and the previous post it sometimes feels we're hamstrung by GTK APIs, but I suspect the alternative of using X directly is much worse. It's easy to overlook how much a modern toolkit provides for -- IME, Xembed, and XDND come to mind -- and we only end up needing to fight in the places where our app is especially tricky.