Is there a way to blacklist a set of frames so that other-frame ignores / skips them?


If you want a replacement command for other-frame then this will do it. The definition is the same, except for the condition for a candidate frame not to be a member of the blacklist, my-frames-blacklist.

(defvar my-frames-blacklist ()
  "List of frames to be ignored by `my-other-frame'.")

(defun my-other-frame (arg)
  "Select the ARGth different visible frame on current display, and raise it.
All frames are arranged in a cyclic order.
This command selects the frame ARG steps away in that order.
A negative ARG moves in the opposite order.

To make this command work properly, you must tell Emacs how the
system (or the window manager) generally handles focus-switching
between windows.  If moving the mouse onto a window selects
it (gives it focus), set `focus-follows-mouse' to t.  Otherwise,
that variable should be nil."
  (interactive "p")
  (let ((sframe (selected-frame))
        (frame (selected-frame)))
    (while (> arg 0)
      (setq frame (next-frame frame))
      (while (and (not (eq frame sframe))
                  (not (eq (frame-visible-p frame) t))
                  (not (memq frame my-frames-blacklist))) ; <============
        (setq frame (next-frame frame)))
      (setq arg (1- arg)))
    (while (< arg 0)
      (setq frame (previous-frame frame))
      (while (and (not (eq frame sframe))
                  (not (eq (frame-visible-p frame) t))
                  (not (memq frame my-frames-blacklist))) ; <============
        (setq frame (previous-frame frame)))
      (setq arg (1+ arg)))
    (select-frame-set-input-focus frame)))

If you want the effect for more than interactive use then advise function other-frame similarly, instead of defining a new command for this.

Note: It's up to you to define the blacklist, populating it with actual frame objects.

You can instead use frame names instead of frame objects, to test for equality. But in that case you need to adjust for the possibility that more than one frame has the same name (assuming you use the value of frame parameter name as the name value).

Icicles does this, for example, for command icicle-select-frame (C-x 5 o), which lets you cycle among frames or complete their names, to select them.

It uses function icicle-make-frame-alist to create an alist of unique frame names for completing-read. If two frames have the same name parameter value then it distinguishes them by adding a suffix [NUMBER] to one of them to make its name unique.

(defun icicle-make-frame-alist ()
  "Return an alist of entries (FNAME . FRAME), where FNAME names FRAME.
Frame parameter `name' is used as FNAME, unless there is more than one
frame with the same name.  In that case, FNAME includes a suffix
\[NUMBER], to make it a unique name.  The NUMBER order among frame
names that differ only by their [NUMBER] is arbitrary."
  (let ((fr-alist  ())
        (count     2)
        fname new-name)
    (dolist (fr  (frame-list))
      (setq fname  (frame-parameter fr 'name))
      (if (not (assoc fname fr-alist))
          (push (cons fname fr) fr-alist)
        (setq new-name  fname)
        (while (assoc new-name fr-alist)
          (setq new-name  (format "%s[%d]" fname count)
                count     (1+ count)))
        (push (cons new-name fr) fr-alist))
      (setq count  2))

You can use similar code.

| improve this answer | |
  • This is a decent solution, but it would be nice if instead of my-other-frame using a static blacklist of frame objects, it used a list of regexps to match frame titles. – Mark Feb 26 '19 at 9:33
  • That's just as trivial to do. change my-frames-blacklist to be a list of regexp strings, and change the memq to an iteration of string-match-p over those strings (stopping at the first match). – Drew Feb 26 '19 at 16:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.