Next: , Previous: , Up: Events and the Event Loop   [Contents][Index]


13.11 Focus Handling

Ben’s capsule lecture on focus:

In GNU Emacs select-frame never changes the window-manager frame focus. All it does is change the "selected frame". This is similar to what happens when we call select-device or select-console. Whenever an event comes in (including a keyboard event), its frame is selected; therefore, evaluating select-frame in ‘*scratch*’ won’t cause any effects because the next received event (in the same frame) will cause a switch back to the frame displaying ‘*scratch*’.

Whenever a focus-change event is received from the window manager, it generates a switch-frame event, which causes the Lisp function handle-switch-frame to get run. This basically just runs select-frame (see below, however).

In GNU Emacs, if you want to have an operation run when a frame is selected, you supply an event binding for switch-frame (and then maybe call handle-switch-frame, or something ...).

In XEmacs, we do change the window-manager frame focus as a result of select-frame, but not until the next time an event is received, so that a function that momentarily changes the selected frame won’t cause WM focus flashing. (#### There’s something not quite right here; this is causing the wrong-cursor-focus problems that you occasionally see. But the general idea is correct.) This approach is winning for people who use the explicit-focus model, but is trickier to implement.

We also don’t make the switch-frame event visible but instead have select-frame-hook, which is a better approach.

There is the problem of surrogate minibuffers, where when we enter the minibuffer, you essentially want to temporarily switch the WM focus to the frame with the minibuffer, and switch it back when you exit the minibuffer.

GNU Emacs solves this with the crockish redirect-frame-focus, which says "for keyboard events received from FRAME, act like they’re coming from FOCUS-FRAME". I think what this means is that, when a keyboard event comes in and the event manager is about to select the event’s frame, if that frame has its focus redirected, the redirected-to frame is selected instead. That way, if you’re in a minibufferless frame and enter the minibuffer, then all Lisp functions that run see the selected frame as the minibuffer’s frame rather than the minibufferless frame you came from, so that (e.g.) your typing actually appears in the minibuffer’s frame and things behave sanely.

There’s also some weird logic that switches the redirected frame focus from one frame to another if Lisp code explicitly calls select-frame (but not if handle-switch-frame is called), and saves and restores the frame focus in window configurations, etc. etc. All of this logic is heavily #if 0’d, with lots of comments saying "No, this approach doesn’t seem to work, so I’m trying this ... is it reasonable? Well, I’m not sure ..." that are a red flag indicating crockishness.

Because of our way of doing things, we can avoid all this crock. Keyboard events never cause a select-frame (who cares what frame they’re associated with? They come from a console, only). We change the actual WM focus to a surrogate minibuffer frame, so we don’t have to do any internal redirection. In order to get the focus back, I took the approach in minibuf.el of just checking to see if the frame we moved to is still the selected frame, and move back to the old one if so. Conceivably we might have to do the weird "tracking" that GNU Emacs does when select-frame is called, but I don’t think so. If the selected frame moved from the minibuffer frame, then we just leave it there, figuring that someone knows what they’re doing. Because we don’t have any redirection recorded anywhere, it’s safe to do this, and we don’t end up with unwanted redirection.


Next: , Previous: , Up: Events and the Event Loop   [Contents][Index]