Setting Both `fullheight` and `width` in Emacs on OS X
Asked Answered
I

2

7

I find the default size of the Emacs frame a little too small. From reading around I know that I can set the height and width quite easily with something like the following:

;;; 140 x 60 window size
(setq default-frame-alist '((width . 140) (height . 60)))

Which works great on my external monitor, however it is a litte too big for the laptop display. I can solve the height problem by changing to the follwing:

;;; automatically set the height
(setq default-frame-alist '((fullscreen . fullheight)))

Which sets the frame to be as tall as possible for the current screen. I can't however set the width of the frame if I use this method. Adding (width . 140) to the above alist sets the width to the right value but also sets the height to the default height again.

When I see the frame appear it sets itself to the full height, and then sets the width to the value I requested, and shrinks in height.

I can overcome this problem with the following code:

;;; Full height for the default window
(setq default-frame-alist
      '((fullscreen . fullheight)))
;; Set the width in a hook and have all windows inherit
(setq frame-inherited-parameters
      '(width height))
(add-hook 'after-init-hook
          (lambda ()
                  (set-frame-parameter nil 'width 140)))

Which uses a hook to set the width of the first frame to the value I want, and then sets all other windows to inherit this value.

This isn't very elegant however, so the question is "how can I accomplish this in a simpler (or less hackish) way?".

If you want to see my exact init.el script, take a look at this gist

TL;DR

How can I set both the width of a frame, and set the frame to be as tall as possible on the current monitor, on OS X? It seems you can't specify (width . 140) and (fullscreen . fullheight) in the default-frame-alist.

Inefficacy answered 28/6, 2013 at 10:50 Comment(0)
I
11

I have come up with a solution to this. I explicitly calculate the height of the window rather than relying on (fullscreen . fullheight) to do it for me.

The updated code to set the values for the height and width is quite simple:

;;; Nice size for the default window
(defun get-default-height ()
       (/ (- (display-pixel-height) 120)
          (frame-char-height)))

(add-to-list 'default-frame-alist '(width . 140))
(add-to-list 'default-frame-alist (cons 'height (get-default-height)))

In this code the subtraction of 120 from the height of the screen makes sure that the height of the window takes into account the height of the dock and the menubar. For correct results you will have to make sure that this code is executed after you have chosen the font face to use, otherwise the computed height value will not be valid.

Placing the height calculation in its own function should allow special casing certain operating systems and versions. This method also has the added advantage that is faster to open the window as it doesn't "animate" the height to the full height value.

Inefficacy answered 28/6, 2013 at 13:11 Comment(1)
@JS glad to be of help. Still using this trick as-is without any issues.Inefficacy
J
0

The last paragraph of the help text for display-pixel-height is:

For graphical terminals, note that on "multi-monitor" setups this
refers to the pixel height for all physical monitors associated
with DISPLAY.  To get information for each physical monitor, use
‘display-monitor-attributes-list’.

What got me to this page is what looks like a bug in the X11 implementation but I'm not sure yet. In any case, display-pixel-height works only often but not always.

Sample output from display-monitor-attributes-list is:

(
 (
  (geometry 0  0 1920 1080)
  (workarea 0 25 1920 1055)
  (mm-size 478 268)
  (frames #<frame *scratch* 0x14489a030>)
  (source . "NS")
 )
 (
  (geometry 192 1080 1512 982)
  (workarea 192 1080 1512 950)
  (mm-size 301 195)
  (frames)
  (source . "NS")
 )
)

In my case, I have a laptop (bottom entry) and a monitor connected to it (the top entry).

One possible solution would be to go through the list, find the monitor with the largest height and do the computations based upon that height and then at the end also do something like (set-frame-position nil 192 1080) where the two coordinates come from the top and left coordinates (the first two values of geometry and work area of the monitor that has the greatest height.

It appears that the workarea is the more prudent set of values to use.

And to further make a robust solution, a hook should be added to window-configuration-change-hook so that when a monitor is added or removed, things will get updated.

I am currently asking some questions on the Emacs developers mailing list and working on a solution for myself. I plan to return and update this entry when I have a solution that I'm happy with but thought this information may be of use to others as is.

Update:

There is no bug. x-display-pixel-height has various X11 rude facts of life. For more details, see this reply.

The "Frame Layout" node in the info documentation for ELisp as a description of the Inner and Outer Frame along with many other concepts that are pertinent to this question.

Here is my current solution. I have not done the hook yet. I don't claim to know how to program in lisp but this code is working for me.

(defun workarea-height ( monitor )
  "MONITOR is an entry from `display-monitor-attributes-list' The
height entry (4th value) of the 'workarea' is returned"
  (nth 4 (assoc 'workarea monitor)))

(defun monitor-with-largest-height-helper ( a b )
  "Compares the height of the workarea of two monitor entries such as
those contained in the output of `display-monitor-attributes-list'"
  (let* ((a-height (workarea-height a))
         (b-height (workarea-height b)))
    (if (> a-height b-height)
        a
      b)))

(defun monitor-with-largest-height ()
  "Returns the monitor entry from `display-monitor-attributes-list'
with the largest 'workarea' height"
    (cl-reduce #'monitor-with-largest-height-helper
                                     (display-monitor-attributes-list)))

(defun largest-monitor-height ()
  "Returns the usable height in lines of the largest monitor currently
attached"
  (let* ((largest-monitor (monitor-with-largest-height)))
    (/ (- (workarea-height largest-monitor)
          (- (frame-outer-height)
             (frame-inner-height)))
       (frame-char-height))))

(defun my-resize-frame-height ()
  "Resizes the current frame to the full height of the largest monitor
  currently attached."
  (interactive)
  (set-frame-height nil (largest-monitor-height)))

The other work left to do is to make sure the left and top of the frame are within the area of the largest monitor to the frame will be displayed on that monitor.

Jellicoe answered 18/5, 2022 at 23:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.