02 Oct 2022

# A workaround for an annoying EXWM behavior

I have been using the Emacs X Window Manager, EXWM, for a couple of years now and I have come to depend on it for an efficient and friendly workflow for most of what I do at the computer. Before that, I had been using StumpWM for 4 or 5 years and I was already sold on tiling window managers. For someone like me who does everything in Emacs except for browsing sites that need Javascript or watching videos, EXWM seems to be the perfect setup.

The pain point with EXWM is that its development and maintenance suddenly stopped after the creator and maintainer went AWOL. There have been discussions here and there to continue the development, but, to the best of my knowledge, no initiative has really taken off.

Some well known EXWM users in the community started migrating to other WMs, like StumpWM, Herbstluftwm or or QTile. I also tried to get back to StumpWM, since I had used it for a long time, but I found the experience inferior to EXWM: having M-x everywhere and not having to use different key bindings when talking to the WM or to Emacs is a real comfort.

However, some time ago, floating windows started behaving annoyingly. For instance, when saving a file in Firefox, the file chooser application would be too high and the Save button would be below the bottom of the screen. I then would have to move the window up so that I could click. Not knowing anything about X, I wasn't able to diagnose the issue.

I ended writing a little function to resize a floating window:

(defun my/resize-floating-frame (width height)
"Resize a floating exwm frame to WIDTHxHEIGHT"
(interactive "nWidth: \nnHeight: ")
(let ((floating-container (frame-parameter exwm--floating-frame
'exwm-container)))
(exwm--set-geometry floating-container nil nil width height)
(exwm--set-geometry exwm--id nil nil width height)
(xcb:flush exwm--connection)))


and bound it to a key to call it with a size that would fit:

(exwm-input-set-key (kbd "s-x r") (lambda () (interactive) (my/resize-floating-frame 600 800)))


So now, instead of grabbing the mouse and manually moving the window, I could just use s-x r and hit enter, since the Save button is selected by default. This is a big win, but after a couple of days, I became tired of this and thought that this could be automated. Also, the arbitrary fixed size may not be appropriate depending on the monitor I am using.

So with a little more elisp, I wrote a function that computes the size of the window and works with a hook which is called by EXWM and the end of the setup of the floating window:

(defun my/adjust-floating-frame-size ()
"Ensure that the current floating exwm frame does not exceed the size of the screen"
(let* ((frame (selected-frame))
(width (frame-pixel-width frame))
(height (frame-pixel-height frame))
(w1 (elt (elt exwm-workspace--workareas 0) 2))
(h1 (elt (elt exwm-workspace--workareas 0) 3))
(w2 (elt (elt exwm-workspace--workareas 1) 2))
(h2 (elt (elt exwm-workspace--workareas 1) 3))
(max-width (round (* 0.75 (min w1 w2))))
(max-height (round (* 0.75 (min h1 h2))))
(final-height (min height max-height))
(final-width (min width max-width)))
(set-frame-size frame final-width final-height t)))

All this may seem complex and one could think that too much elisp-foo is needed to do all this. This is absolutely not the case. With a little effort and time, it is not difficult to navigate the source code and test things interactively to see how they behave. In this particular case, searching for hooks in the EXWM package (M-x describe-variable) and trying little code snippets on the *scratch* buffer got me there rather quickly.