Hi,
I am pretty sure that Drei gadgets are doing something naughty with input focus.
My X11 window manager has focus-follows-mouse (well, strictly, sloppy focus) for top-level windows. If I make a CLIM application containing a drei pane, then the application window will steal focus: if I have my mouse over the drei buffer, and then move it into a window which is above some portion of the drei pane, that window will get focus for a brief moment, and then the drei application will take it back...
Cheers,
Christophe
Christophe Rhodes csr21@cantab.net writes:
I am pretty sure that Drei gadgets are doing something naughty with input focus.
Does Goatee do the same thing for you? Goatee had/has some ugly keyboard-focus grabbing code that I initially didn't copy over to Drei because it seemed to work in my window manager (ratpoison), but people complained that Drei failed to receive keyboard gestures, so I moved it over verbatim (I hope). I'm not really sure why it's necessary, but I'm pretty sure it's very ugly.
Troels Henriksen athas@sigkill.dk writes:
I'm not really sure why it's necessary, but I'm pretty sure it's very ugly.
The attached simple patch seems to make everything work properly, and removes the need for explicit management of keyboard focus in Drei and Goatee, but because of its simplicity, and my lack of knowledge about CLX, I'd like a review before I commit it and make a fool out of myself:
Troels Henriksen athas@sigkill.dk writes:
Troels Henriksen athas@sigkill.dk writes:
I'm not really sure why it's necessary, but I'm pretty sure it's very ugly.
The attached simple patch seems to make everything work properly, and removes the need for explicit management of keyboard focus in Drei and Goatee, but because of its simplicity, and my lack of knowledge about CLX, I'd like a review before I commit it and make a fool out of myself:
I don't think that this makes a fool out of you, but I also don't think it's the Right Thing.
(defmethod %set-port-keyboard-focus ((port clx-port) focus &key timestamp) (let ((mirror (sheet-mirror focus))) (when mirror
(xlib:set-input-focus (clx-port-display port) mirror :parent timestamp))))
(xlib:set-input-focus (clx-port-display port) :pointer-root :parent timestamp))))
This is in the nature of a workaround: setting the focus to whatever toplevel window is under the pointer. That might work by coincidence for many applications, in the case that I described in my original mail, but I don't think it's defined to work: if the window over which I have moved my mouse has multiple input widgets, it's not clear which will win the focus battle, if that application is not programmed such that the toplevel window will distribute events to its children.
I think the Right Thing is tricky to implement. What needs to happen is that the X event which caused Drei to become disarmed needs to be propagated through to the disarmed callback and back out again, so that the timestamp field of the set-input-focus request is set to the timestamp of that event. The key point is that this allows the X server to discard a set-input-focus request with a timestamp that is earlier than a request that has already been dealt with, which is exactly the behaviour we want. (There's much detail about this in the ICCCM document available in the xspecs package on Ubuntu and elsewhere).
Well, I say the "Right Thing": _clearly_ the right thing is to abandon this horrific focus-follows-mouse-around-widgets disaster and implement a sane keyboard focus policy. Then much of the complexity can go away. Hooray. (What did Classic CLIM do?)
Cheers,
Christophe
Christophe Rhodes csr21@cantab.net writes:
I don't think that this makes a fool out of you, but I also don't think it's the Right Thing.
Is it better than the Old Wrong Thing? If so, it might be an acceptable interim fix.
This is in the nature of a workaround: setting the focus to whatever toplevel window is under the pointer. That might work by coincidence for many applications, in the case that I described in my original mail, but I don't think it's defined to work: if the window over which I have moved my mouse has multiple input widgets, it's not clear which will win the focus battle, if that application is not programmed such that the toplevel window will distribute events to its children.
Well, fortunately McCLIM is programmed to do this. :-) AFACS, this would not affect any other programs on the X server.
On 1/19/07, Christophe Rhodes csr21@cantab.net wrote:
Well, I say the "Right Thing": _clearly_ the right thing is to abandon this horrific focus-follows-mouse-around-widgets disaster and implement a sane keyboard focus policy. Then much of the complexity can go away. Hooray. (What did Classic CLIM do?)
I'm not familiar with the horrors of the Goatee input focus kludge, but it seems straightforward to implement click-to-focus. I've attached a trivial test which demonstrates that this does indeed work (click to focus between two mock text-editor gadgets, although it is initially focus-follows-mouse until the focus is first assigned by clicking, as that simply seems to be what X does by default).
There appear to be quite a few things left to do on the keyboard focus front. The frame already has a slot to store the focus, so we need to assign the correct initial focus when the frame is adopted. I also notice the correct timestamps do not reach xlib:set-keyboard-focus, which needs fixing. There's also the issue of how a gadget can detect when the window manager has taken the focus away, since there's no 'note' function for it that I'm aware of, and relatedly why my note-input-focus-changed doesn't appear to work, which I'd intended to note transfer of the assigned focus within an application. Then there's the fun parts, like cycling of focus with the tab key.
"Andy Hefner" ahefner@gmail.com writes:
On 1/19/07, Christophe Rhodes csr21@cantab.net wrote:
Well, I say the "Right Thing": _clearly_ the right thing is to abandon this horrific focus-follows-mouse-around-widgets disaster and implement a sane keyboard focus policy. Then much of the complexity can go away. Hooray. (What did Classic CLIM do?)
I'm not familiar with the horrors of the Goatee input focus kludge, but it seems straightforward to implement click-to-focus. I've attached a trivial test which demonstrates that this does indeed work (click to focus between two mock text-editor gadgets, although it is initially focus-follows-mouse until the focus is first assigned by clicking, as that simply seems to be what X does by default).
I've attached a patch which implements click-to-focus fairly pervasively, without using the X focus mechanism. The basic idea is to separate out port-keyboard-input-focus, which mediates the X focus for top-level windows, and frame-keyboard-input-focus, which is a per-frame setting. This patch deviates from CLIM II in that stream-set-input-focus does not call port-keyboard-input-focus, but merely sets the per-frame slot; the CLX event handler is adjusted to place the proper sheet in keyboard events.
The implementation of click-to-focus is kludgy. It's fine for drei-gadgets, and for text-gadgets generally; it's not so hot for general streams. I've taken the line that interactor-panes should be focusable with a click; somewhat to my surprise, you can't just write a method on handle-event to get this, but have to work with frame-input-context-button-press-handler. A potential gotcha is that I have not implented click-to-focus for application-panes; this may cause surprises, but it seemed to me the only way not to break the address book demo ;-)
One new spec compliance is that initially keyboard focus really does go to *query-io*. This might cause some surprising behaviour. Somewhat to my surprise, gsharp seems to work -- maybe because its toplevel loop is different -- but ESAs with default-frame-top-level deliver keyboard events to the minibuffer by default, which isn't ideal. On the other hand, the focus behaviour of text gadgets is, to me, much nicer -- and there's no more focus stealing going on.
I have to send this in a bit of a hurry; there's more I could say about this, but I'll be happy to hear comments.
Cheers,
Christophe
On 1/25/07, Christophe Rhodes csr21@cantab.net wrote:
The implementation of click-to-focus is kludgy. It's fine for drei-gadgets, and for text-gadgets generally; it's not so hot for general streams. I've taken the line that interactor-panes should be focusable with a click; somewhat to my surprise, you can't just write a method on handle-event to get this, but have to work with frame-input-context-button-press-handler. A potential gotcha is that I have not implented click-to-focus for application-panes; this may cause surprises, but it seemed to me the only way not to break the address book demo ;-)
The degree to which read-gesture separates the programmer from the interaction implemented by the stream via various button handling methods, functions bound in special variables, etc., has always made me nervous. It's somehow relieving to see it causing someone difficulty.
Maybe we want a slot or method defined on those panes implementing click-to-focus behavior which determines whether they are currently focussable. An interactor-pane would default to t, application-pane nil. If nil, clicks would pass through and be handled as they are currently, without reassigning focus. This should preserve the behavior of the address book.
I'm slightly concerned that because frame-input-context-button-press-handler is a function from the spec, users might expect that defining their own method for it would not to break input focus handling. Maybe focus handling should be pushed down into stream-read-gesture, but I'm not sure how/where.
One new spec compliance is that initially keyboard focus really does go to *query-io*. This might cause some surprising behaviour. Somewhat to my surprise, gsharp seems to work -- maybe because its toplevel loop is different -- but ESAs with default-frame-top-level deliver keyboard events to the minibuffer by default, which isn't ideal. On the other hand, the focus behaviour of text gadgets is, to me, much nicer -- and there's no more focus stealing going on.
Hmm, that's tricky. Playing around yesterday, I'd added an :initial-input-focus initarg for the frame, but that only works in gadget-y applications where there is no query-io (otherwise run-frame-top-level uses that instead). Maybe we can just change how the specified behavior is achieved, so that (keyboard-input-focus frame) is nil by default, and focus is initially assigned to (or (keyboard-input-focus frame) (frame-query-io frame)) ?
On 1/25/07, Andy Hefner ahefner@gmail.com wrote:
I'm slightly concerned that because frame-input-context-button-press-handler is a function from the spec, users might expect that defining their own method for it would not to break input focus handling. Maybe focus handling should be pushed down into stream-read-gesture, but I'm not sure how/where.
Or, worse, put it in dispatch-event, the same way that wheel-scrolling and select/paste is implemented, but that's really a horrible hack that needs to stop.
"Andy Hefner" ahefner@gmail.com writes:
On 1/25/07, Christophe Rhodes csr21@cantab.net wrote:
A potential gotcha is that I have not implented click-to-focus for application-panes; this may cause surprises, but it seemed to me the only way not to break the address book demo ;-)
The degree to which read-gesture separates the programmer from the interaction implemented by the stream via various button handling methods, functions bound in special variables, etc., has always made me nervous. It's somehow relieving to see it causing someone difficulty.
Well, glad to be of service :-)
Maybe we want a slot or method defined on those panes implementing click-to-focus behavior which determines whether they are currently focussable. An interactor-pane would default to t, application-pane nil. If nil, clicks would pass through and be handled as they are currently, without reassigning focus. This should preserve the behavior of the address book.
I'm slightly concerned that because frame-input-context-button-press-handler is a function from the spec, users might expect that defining their own method for it would not to break input focus handling. Maybe focus handling should be pushed down into stream-read-gesture, but I'm not sure how/where.
At the moment, because I've implemented the focus behaviour as a :before method, it shouldn't matter if users define their own methods for it. I generally agree that allowing the user to decide whether individual panes should be focussable is reasonable, though.
One new spec compliance is that initially keyboard focus really does go to *query-io*. This might cause some surprising behaviour. Somewhat to my surprise, gsharp seems to work -- maybe because its toplevel loop is different -- but ESAs with default-frame-top-level deliver keyboard events to the minibuffer by default, which isn't ideal. On the other hand, the focus behaviour of text gadgets is, to me, much nicer -- and there's no more focus stealing going on.
Hmm, that's tricky. Playing around yesterday, I'd added an :initial-input-focus initarg for the frame, but that only works in gadget-y applications where there is no query-io (otherwise run-frame-top-level uses that instead). Maybe we can just change how the specified behavior is achieved, so that (keyboard-input-focus frame) is nil by default, and focus is initially assigned to (or (keyboard-input-focus frame) (frame-query-io frame)) ?
I'm actually fairly sanguine about query-io; I think it's reasonable for clicks to invoke stream-set-input-focus, thereby taking focus away from query-io, and leaving just the initial focus setting to there by default -- it's in the spec, after all, and for "normal" clim apps this is probably the right thing anyway. For ESAs with their different gesture handling, it's perhaps not surprising that a toplevel customization is needed, and that Bad Things happen if that toplevel isn't present; if there's an adjustment needed, it's probably in ESA's classes, or to the behaviour of the minibuffer when it receives keyboard events...
How shall we move this forward? I want (very much) something implementing behaviour similar to that in my patch, because there are applications that I would like to build that would suck much less with that kind of behaviour. I'm sensitive to the fact that I'm not the only mcclim user, though, and I don't really want to break everyone else's applications, or send us down a mire of supporting an unsupportable API... on that subject, what implications does this have for other backends?
Cheers,
Christophe
Christophe Rhodes csr21@cantab.net writes:
unsupportable API... on that subject, what implications does this have for other backends?
I attach a patch which reworks my previous stuff, and implements the same behaviour (as far as I can tell) for the gtkairo backend as well as the CLX backend.
* PORT-KEYBOARD-INPUT-FOCUS, (SETF PORT-KEYBOARD-INPUT-FOCUS): trampoline to PORT-FRAME-KEYBOARD-INPUT-FOCUS and (SETF ...). The interpretation of this operator is to set the keyboard focus on a per-frame basis, and not to interact with any window manager to grab the focus from potentially unrelated applications.
* PORT-FRAME-KEYBOARD-INPUT-FOCUS / (SETF ...): per-backend methods, specialized on the port, for querying and setting the frame's focus sheet. Implemented in CLX (and Null) backend using FRAME-PROPERTIES; in gtkairo backend using gtk_window_get_focus() and gtk_widget_grab_focus().
* various editor gadgets: no longer do keyboard handling in [dis]armed-callback; handle-event methods for assigning focus. frame-pointer-button-press-handler method likewise for INTERACTOR-PANEs.
Any comments?
Cheers,
Christophe