The blurb

Below is the emacs-lisp code I’ve evolved over the years to setup reasonable font sizes on all the machines where I use Emacs.

The main goal is to get the physical / apparent size of the font, in millimetres, to be more similar across all displays and display systems (Different screens across macOS, Windows with and without display scaling, WSL). This sounds like it should be easy, but setting font point size to the same number is in fact interpreted differently on various display systems.

Some noteworthy and maybe educational aspects:

  • cpb-get-windows-display-scale uses powershell on Windows to determine the current display scale, e.g. 125% should yield 1.25, in order to determine the effective (scaled) resolution on that platform. The problem is that Emacs reports raw resolution (not scaled), but setting font size works in the scaled space.
  • get-monitor-width-mm uses awk to parse out the physical display width from the weston.log file, because on WSL, the reported physical widhth (in mm) is hardcoded to 96 DPI. Doh.

This code is probably not going to work for you as is. Or it might. Whatever the case may be, have fun with it.

The code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
(defun get-mon-attr (which)
  (cdr (assoc which (car (display-monitor-attributes-list)))))

(defun cpb-get-windows-display-scale ()
  "Use powershell to get the HiDPI display scale as a floating point number."
  (let ((output-buffer (generate-new-buffer "output-buffer")))
    (unwind-protect
        (progn
          ;; powershell to get scaling: https://unix.stackexchange.com/a/713831/32868
          (call-process "powershell.exe" nil output-buffer nil "-NoProfile" "-Command" "Add-Type -AssemblyName System.Windows.Forms; [int](Get-CimInstance -ClassName Win32_VideoController)[0].CurrentVerticalResolution / [int][System.Windows.Forms.SystemInformation]::VirtualScreen.Height")
          (with-current-buffer output-buffer
            (string-to-number (string-replace "," "." (string-trim (buffer-string))))))
      (kill-buffer output-buffer))))

(defun get-monitor-width-mm ()
  (if (getenv "WSL_DISTRO_NAME")
      ;; under WSLg 2021-01-21, physical dimensions are not correctly reported anywhere but weston.log,
      ;; (I hear this is a wayland issue, not just wslg)
      ;; so we use good old awk to get the last occurrence in the log file
      (let* ((awk-dims (shell-command-to-string "awk 'match($0, /physicalWidth:([0-9]+), physicalHeight:([0-9]+)/,a) {width=a[1];height=a[2]} END {print width \",\" height}' /mnt/wslg/weston.log"))
             (splitd (split-string awk-dims ","))
             (width-mm (string-to-number (car splitd))))
        width-mm)
    ;; on other systems, we use whatever Emacs thinks it is
    (car (get-mon-attr 'mm-size))))

(setq monitor-width (get-monitor-width-mm))

(if monitor-width
    (progn
      (setq monitor-hpix (nth 2 (get-mon-attr 'geometry)))
      ;; estimate *effective* PPI (i.e. taking into account scaling / super-sampling)
      ;; the higher the PPI, the more font-pts we need to get characters that are big enough
      ;; - on macOS, it reports the effective PPI (taking into account scaling) already
      ;; - on native Windows, it reports the raw resolution, so we divide by the display scaling to get effective
      ;;   important because font size setting is via effective
      ;; - on WSL it reports native, but font size setting is also native and not scaled, so it works like it should
      (setq display-scale (if (eq system-type 'windows-nt) (cpb-get-windows-display-scale) 1.0))
      (setq ppi-est (/ monitor-hpix display-scale (/ monitor-width 25.4)))

      ;; thinkpad 14" at 100%: (/ 1920 (/ 344 25.4)) => 141.767 -> 140 pts
      ;; 4K at 100% because mac: 3840x => 139
      ;; m1 mba built-in display: (/ 1440.0 (/ 286 25.4)) => 127.88
      ;; 4K at 125%: (/ 3072.0 (/ 697 25.4)) =>  111.949
      ;; - with wslg 1.0.32, geometry now returns the full, unscaled resolution
      ;;   so we get ppi-est 139 even with 125% scaling
      ;; 4K with WACKY macOS scaling 3008x1692: => 109.147
      ;; 27" at 2560x1440 is 108.92 -> font size 130 pts looks fine there
      ;; use ppi-est to adjust font points for your eyes
      (setq my-font-pts (cond
                         ((>= ppi-est 139) 160)
                         ((>= ppi-est 109) 140)
                         (t 130))))

  ;; fallback for when we DON'T have monitor-width, e.g. in text mode
  (setq my-font-pts 140))


;; FONTS =================================================================
;; MONOSPACE FONT
;; NB NB NB: on WSL remember your .Xresources for smoothing! (else looks quite bad)
;; mac: brew install font-jetbrains-mono
;; ubu2204: sudo apt install fonts-jetbrains-mono
(setq my-fixed-font "JetBrains Mono")
;;(setq my-fixed-font "Noto Sans Mono")
(set-face-attribute 'default nil :family my-fixed-font :height my-font-pts)
(set-face-attribute 'fixed-pitch nil :family my-fixed-font :height 1.0) ;; relative to default
;; VARIABLE PITCH FONT -- NB on WSL remember your .Xresources for smoothing!
;; I go through serif and sans phases
;; - Sans:
;;   - Inter is great, but can become a bit boring
;;   - Deja Vu Sans appears too fat compared to Inter
;;   - Noto Sans: growing on me 2023-09-02! (see e.g. the condensed rendering of the org-modern labels)
;; - Serif:
;;   - IBM Plex Serif is clean and understated ON MACOS, looks crappy on WSL (remember to xrdb -merge .Xresources)
;;   - Noto Serif more pronounced and probably easier on the eyes
(set-face-attribute 'variable-pitch nil :family "Noto Sans" :height 1.05)