bmag / emacs-purpose

Manage Windows and Buffers According to Purposes
GNU General Public License v3.0
496 stars 23 forks source link

F1 frame being selected by `purpose-display-maybe-other-frame` #161

Closed nnicandro closed 5 months ago

nnicandro commented 4 years ago

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?

bmag commented 4 years ago

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)))