3

I would like to use certain unicode characters in the mode-line only if the Symbola font is available on the system.

So I put this in my emacs config and it's a great way to check if those fonts are available for a regular, non-daemon emacs session

(defvar font-symbola-p nil
  "If non-nil, Symbola font is available on the system.")

(when (find-font (font-spec :name "Symbola"))
  (setq font-symbola-p t))

But when I run emacs using emacsclient -a '' -c aka daemon mode, the value of font-symbola-p stays nil.

It seems as if emacs reads the "font database" at some point after the above code is eval'd from my config.

Q: What is the right time to call find-font when running emacs in daemon mode?

PS: I tried the below too but that too didn't work ONLY in daemon mode.

(add-hook 'window-setup-hook
          (lambda ()
            (when (find-font (font-spec :name "Symbola"))
              (setq font-symbola-p t))))

2 Answers 2

2

This trick worked for me; I basically wait for emacs to be idle for a second (which happens after its startup finishes) and do the Symbola font check just once. This seems to work when emacs in launched in regular or daemon mode.

(run-with-idle-timer 1 nil
                     (lambda ()
                       (when (find-font (font-spec :name "Symbola"))
                         (setq font-symbola-p t))))

Slightly more refined solution

;; Below macro is used to wrap stuff that need to be run only after emacs
;; starts up completely. This is very crucial when calling functions like
;; `find-font' which return correct value only after emacs startup is finished
;; especially when emacs is started in daemon mode.
(defmacro do-once-1-sec-after-emacs-startup (&rest body)
  `(run-with-idle-timer 1 ; run this after emacs is idle for 1 second
                        nil ; do this just once; don't repeat
                        (lambda () ,@body)))

Example Use

;; Check if the Symbola font is available just once, after a second delay after
;; emacs startup. This trick works when emacs is launched in regular or daemon
;; mode
(do-once-1-sec-after-emacs-startup
 (when (find-font (font-spec :name "Symbola"))
   ;; Manually choose a fallback font for Unicode
   ;; http://endlessparentheses.com/manually-choose-a-fallback-font-for-unicode.html
   (set-fontset-font "fontset-default" nil (font-spec :size 20 :name "Symbola"))
   (setq font-symbola-p t)))

Update - Alternative solution

Save the below to a file setup-symbola.el in your user-emacs-directory (or one of the directories in your load-path).

;; setup-symbola.el
(defvar font-symbola-p nil
  "If non-nil, Symbola font is available on the system.")

(when (find-font (font-spec :name "Symbola"))
  ;; Manually choose a fallback font for Unicode
  ;; http://endlessparentheses.com/manually-choose-a-fallback-font-for-unicode.html
  (set-fontset-font "fontset-default" nil (font-spec :size 20 :name "Symbola"))
  (setq font-symbola-p t))

(provide 'setup-symbola)

Put the below in the init.el. You can use the same approach (of using use-package) or use run-with-idle-timer instead to delay the loading of setup-symbola by 1 second:

(use-package setup-symbola :defer 1)

Here is an example of how I use the above setup to set an alternative lighter for shackle-mode if the Symbola font that supports Unicode characters is available.

(with-eval-after-load 'setup-symbola
     (unless font-symbola-p
       (setq shackle-lighter " §")))
3
  • 1
    @lawlist window-setup-hook is executed the last and that didn't work so I didn't try the others. Commented May 12, 2015 at 20:15
  • Can the information be obtained using x-list-fonts?
    – lawlist
    Commented May 12, 2015 at 20:25
  • @lawlist Looks like x-list-fonts also doesn't work in daemon mode. It works as expected in regular (non-daemon) mode. In daemon mode, my config doesn't get loaded due to that function call (and I can't --debug-init when running emacsclient). All I know is that presence of that function call fails my emacs init. Commented May 12, 2015 at 20:42
1

Recently I have come up with a cleaner approach that does not need the 1 second delay before checking available fonts.

In emacs sessions with frames launched via a daemon (emacsclient -c -a''), the user emacs config is evaluated first and then the frame is created. The font setup is done by emacs only after the frame is created. So the user cannot have any font check code that is evaluated at the same time when the config is evaluated.

BUT.. there is one hook, focus-in-hook that is bound to be run when GUI emacs (with frame) is run, whether that frame is created by emacs or emacsclient.

Below solution is a 3-part solution:

  1. You need to create a setup-font-check.el.
  2. Add some code to your init.el that requires that font check code only when focus-in-hook is run.
  3. Use (with-eval-after-load 'setup-font-check ..) when you need to do something font specific in your config.

(1) Font check

;; Font Check -- setup-font-check.el   
(defvar font-symbola-p nil
  "If non-nil, Symbola font is available on the system. This font is required
for emoji and other Unicode 6+ display.")    

(when (find-font (font-spec :name "Symbola"))
  ;; Manually choose a fallback font for Unicode
  ;; http://endlessparentheses.com/manually-choose-a-fallback-font-for-unicode.html
  (set-fontset-font "fontset-default" nil (font-spec :size 20 :name "Symbola"))
  (setq font-symbola-p t))

(provide 'setup-font-check)

(2) Requiring setup-font-check in focus-in-hook

(defun modi/font-check ()
  "Do font check, then remove self from `focus-in-hook'; need to run this just once."
  (require 'setup-font-check)
  (remove-hook 'focus-in-hook #'modi/font-check))
;; For non-daemon, regular emacs launches, the frame/fonts are loaded *before*
;; the emacs config is read. But when emacs is launched as a daemon (using
;; emacsclient, the fonts are not actually loaded until the point when the
;; `after-make-frame-functions' hook is run. But even at that point, the frame
;; is not yet selected (for the daemon case). Without a selected frame, the
;; `find-font' will not work correctly. So we do the font check in
;; `focus-in-hook' instead by which all the below are true:
;;  - Fonts are loaded (in both daemon and non-daemon cases).
;;  - The frame is selected and so `find-font' calls work correctly.
(add-hook 'focus-in-hook #'modi/font-check)

(3) And here is how you use that setup in your config

;; Example of customizing the new `Info-quoted' face in emacs 25+
(with-eval-after-load 'setup-font-check
  (when font-symbola-p
    ;; Do something that needs the Symbola font
    ))

PS: You can also use after-make-frame-functions hook instead of focus-in-hook. But that hook is run only when you run emacsclient, not when you run non-daemon emacs. Also you need to have (select-frame frame) in the function you add to that hook.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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