F1 frame being selected by `purpose-display-maybe-other-frame`
The first frame an Emacs session creates is named F1 and when running Emacs as a daemon (emacs --bg-daemon), that frame is kept around as a "virtual frame" not visible by a user, but returned by frame-list. It seems that sometimes purpose-display-maybe-other-frame selects that invisible frame and the effect is that sometimes buffers being switched to don't appear in any visible frame.
From the Emacs docs:
Note that when Emacs is run as a daemon (see Emacs Server),
there is always a virtual frame that remains after all the ordinary,
interactive frames are deleted.
(See the note about delete-frame in the bottom half of https://www.gnu.org/software/emacs/manual/html_node/emacs/Frame-Commands.html#Frame-Commands)
To ensure purpose doesn't use the F1 frame, I have the below in my setup
(defun purpose-no-emacsclient-frame (&rest args)
(let ((oframe-list (symbol-function #'frame-list)))
(cl-letf (((symbol-function #'frame-list)
(lambda ()
(let ((l (funcall oframe-list)))
(prog1 l
(while (cddr l)
(pop l))
;; The F1 frame is the first Emacs frame created
;; by Emacs and the "virtual frame" that is kept
;; around when Emacs is running as a daemon, see
;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Frame-Commands.html#Frame-Commands
(when (string= "F1" (frame-parameter (cadr l) 'name))
(setcdr l nil)))))))
(apply args))))
(advice-add 'purpose-display-maybe-other-frame
:around #'purpose-no-emacsclient-frame)
Do you suggest I submit a pull request that defines a purpose--frame-list function using the above and use it in purpose-display-maybe-other-frame? Or is there a better way to handle this?
Thanks for posting this issue, I didn't know about that virtual frame. I would appreciate a PR, but as you noticed I'm not very active so it could take some time until I merge, and I totally understand if you don't want to deal with it.
As for the code itself, I don't like the string equality - it may not be a robust solution. I looked a bit and found the variable terminal-frame, which I believe refers to the virtual frame and is eq to it when the virtual frame exists. That should be a better equality check.
Perps-mode uses that variable in the following manner, and we can do something similar: (using filtered-frame-list is also nice)
(defsubst persp-is-frame-daemons-frame (f)
(and (daemonp) (eq f terminal-frame)))
(defun persp-frame-list-without-daemon ()
"Return a list of frames without the daemon's frame."
(if (daemonp)
(filtered-frame-list
#'(lambda (f) (not (persp-is-frame-daemons-frame f))))
(frame-list)))