3

I'm trying to write a little compilation monitor frame that gets compilation updates written to it without the overhead of seeing absolutely everything going on in the compile. I've been working with frames a little bit, and it's mostly a huge pain.

I'd like to figure out if the frame that I'm creating exists yet. I've come up with two ways to do that:

1) Loop through all the frames in (frame-list) and do a string match on the title.

2) This doofy bit of code:

  (if (not (select-frame-by-name "COMPILATION MONITOR"))
  (selected-frame)
(print "ERROR" (get-buffer "*monitor*"))))

select-frame-by-name returns an error if it can't find a frame, which isn't actually a value, as near as I can tell. It just quietly exits. But if it DOES find the frame that I want, it returns nil, and focuses the frame. (Then I can return the frame if I need it using 'selected-frame'. Ignore the error reporting to my buffer--that's not terribly relevant here.)

Is there some better way? With buffers, I can use get-buffer-create to create something new if it doesn't exist and use it if it does. Am I missing something in frame creation? Maybe what I need to know is how the error handling code works, so I can catch the error that select-frame-by-name throws when it can't find what I want?

  • 1
    Consider using the two (2) functions written by Drew Adams that are specifically designed for that purpose -- emacswiki.org/emacs/frame-fns.el -- i.e., get-frame-name and get-a-frame. For a more complex example of creating/locating/targeting specific frames with buffers, see the following related thread entitled How to intercept a file before it opens and decide which frame: stackoverflow.com/questions/18346785/… – lawlist Dec 23 '15 at 19:21
  • @lawlist: Please post that (or something else) as an answer. If an answer gets accepted then people filtering on unanswered questions will not come across it needlessly. – Drew Dec 23 '15 at 23:06
  • Note: "figure out if the frame that I'm creating exists yet. Apparently you are comparing frames only by their names. Be aware that two very different frames can have the same name, as determined by string equality. Perhaps it is enough to check that a frame with the given name exists. In that case, function get-a-frame does what you want. – Drew Dec 24 '15 at 17:34
2

Consider using the two (2) functions written by Drew Adams that are specifically designed for the purpose of obtaining a particular frame by name -- http://www.emacswiki.org/emacs/frame-fns.el -- i.e., get-frame-name and get-a-frame.

For a more complex example of creating/locating/targeting specific frames with buffers, see the following related thread entitled How to intercept a file before it opens and decide which frame: https://stackoverflow.com/questions/18346785/how-to-intercept-a-file-before-it-opens-and-decide-which-frame

1

The functions lawlist mentions should work for you. If for whatever reason you don't want to use them, here's the implementation I came up with when trying to make Org-mode capture in a pop-out frame.

(defun my/get-frame-by-name (fname)
  "If there is a frame named FNAME, return it, else nil."
  (require 'dash)                       ; For `-some'
  (-some (lambda (frame)
           (when (equal fname (frame-parameter frame 'name))
             frame))
         (frame-list)))
1

So here's the code that I ended up writing (before I saw the answers here):

  (let ((prev-frame (selected-frame)))
;; select-frame-by-name returns nil if the frame exists and can be selected, but just throws an
;; error if it doesn't exist without a return value. 
(if (condition-case nil
        (select-frame-by-name "COMPILATION MONITOR")
      (error t))
    (progn
      (make-frame '((name . "COMPILATION MONITOR")
                    (width . 96)
                    (height . 30)
                    (minibuffer . nil)
                    (font . "-outline-Gill Sans MT-normal-normal-normal-sans-11-*-*-*-p-*-iso8859-1")
                    (top . 1486)
                    (left + -2250)
                    (menu-bar-lines 0)))

      ;; The *monitor* buffer is either acquired or created if it doesn't already exist, and dedicated
      ;; to the compilation frame.
      (modify-frame-parameters (select-frame-by-name "COMPILATION MONITOR") '((top . 1486)))
      (dedicate-window (display-buffer-use-some-frame (get-buffer-create "*monitor*") '((name . "COMPILATION MONITOR"))) t ))
  )
(raise-frame prev-frame))

By catching the error that select-frame-by-name throws when it can't find a frame and throwing 't' back (since 'nil' is returned when it DOES find the frame and raises it) I can use it as a condition. I'm not trying to open any files in the frame--it's meant to display exactly one buffer all the time, and no other frame should ever have the same name.

That said, I've selected one of the other answers as the 'correct' one since it's more generally correct, as opposed to my one-off solution.

(Interesting side-note: for some reason, when I make the frame, it ignores the 'top' parameter, but obeys everything else. That's why I modify-frame-parameters to set the top again.)

  • The issue with the top frame parameter (at least in so far as OS X is concerned) was fixed by Anders about a month ago in the master branch and in the Emacs 25 branch. Martin added support for a specific pixel size specification also. See bug report 21415 for more information regarding all of the bug features and fixes that were discussed and implemented under that report: debbugs.gnu.org/cgi/bugreport.cgi?bug=21415 – lawlist Dec 25 '15 at 5:51

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.