Over on the Fediverse I had a long standing GNU Emacs gripe :
I would rather like to make it so that GNU Emacs never un-iconifies itself when it completes (Lisp-level) actions. If I have Emacs iconified I want it to stay that way, not suddenly appear under my mouse cursor like an extremely large modal popup. (Modal popups suck, they are a relic of single-tasking windowing environments.)
For those of you who use GNU Emacs and have never been unlucky enough to experience this, if you start some long operation in GNU Emacs and then decide to iconify it to get it out of your face, a lot of the time GNU Emacs will abruptly pop itself back open when it finishes, generally with completely unpredictable timing so that it disrupts whatever else you switched to in the mean time.
(This only happens in some X environments. In others, the desktop or window manager ignores what Emacs is trying to do and leaves it minimized in your taskbar.)
To cut straight to the answer, you can avoid a lot of this with the following snippet of Emacs Lisp:
(add-to-list 'display-buffer-alist '(t nil (inhibit-switch-frame . t)))
I believe that this has some side effects but that these side effects will generally be that Emacs doesn't yank around your mouse focus or suddenly raise windows to be on top of everything.
GNU Emacs doesn't have a specific function that it calls to de-iconify a frame , what Emacs calls a top level window. Instead, the deiconification happens in C code inside C-level functions like
raise-frame
and
make-frame-visible
, which also do other things and which are called from many places. For instance, one of
make-frame-visible
's jobs is actually displaying the frame's X level window if it doesn't already exist on the screen.
(There's an
iconify-or-deiconify-frame
function but if you look that's a Lisp function that calls
make-frame-visible
. It's only used a little bit in the Emacs Lisp code base.)
A determined person could probably hook these C-level functions through
advice-add
to make them do nothing if they were called on an existing, mapped frame that was just iconified. That would be the elegant way to do what I want. The inelegant way is to discover, via use of the Emacs Lisp debugger , that everything I seem to care about is going through '
display-buffer
' (eventually calling
window--maybe-raise-frame
), and that display-buffer's behavior can be customized to not 'switch frames', which will wind up causing things to not call window--maybe-raise-frame and not de-iconify GNU Emacs windows on me.
To understand
display-buffer-alist
I relied on Demystifying Emacs’s Window Manager . My addition to display-buffer-alist has three elements:
- the
ttellsdisplay-bufferto always use this alist entry. - the
niltellsdisplay-bufferthat I don't have any special action functions I want to use here and it should just use its regular ones. I think an empty list might be more proper here, butnilworks. - the '
(inhibit-switch-frame . t)' sets the important customization, which will be merged with any other things set by other (matching) alist entries.
The net effect is that '
display-buffer
' will see '
inhibit-switch-frame
' set for every buffer it's asked to switch to, and so will not de-iconify, raise, or otherwise monkey around with frame things in the process of displaying buffers. It's possible that this will have undesirable side effects in some circumstances, but as far as I can tell things like '
speedbar
' and 'C-x 5 <whatever>' still work for me afterward, so new frames are getting created when I want them to be.
(I could change the initial '
t
' to something more complex, for example to only apply this to MH-E buffers, which is where I mostly encounter the problem. See Demystifying Emacs’s Window Manager for a discussion of how to do this based on the major mode of the buffer.)
To see if you're affected by this, you can run the following Emacs Lisp in the scratch buffer and then immediately minimize or iconify the window.
(progn (sleep-for 5) (display-buffer "*scratch*"))
If you're affected, the Emacs window will pop back open in a few seconds (five or less, depending on how fast you minimized the window). If the Emacs window stays minimized or iconified, your desktop environment is probably overriding whatever Emacs is trying to do.
For me this generally happens any time some piece of Emacs Lisp code is taking a long time to get a buffer ready for display and then calls '
display-buffer
' at the end to show the buffer. One trigger for this is if the buffer to be displayed contains a bunch of unusual Unicode characters (possibly ones that my font doesn't have anything for). The first time the characters are used, Emacs will apparently stall working out how to render them and then de-iconify itself if I've iconified it out of impatience.
(It's quite possible that there's a better way to do this, and if so I'd love to know about it.)