21

I'm trying to achieve the following behavior in Emacs24:

I like to use a different theme (solarized-dark vs. solarized-light) depending on whether I connect to the running server with a terminal frame

% emacsclient -t

or with a gtk frame

% emacsclient -c

This seems to have worked in older Emacs, but the current Emacs I couldn't find a way. I alreday checked SO (e.g., https://stackoverflow.com/q/18904529/152439 and answers) and the mailing lists (e.g., https://lists.gnu.org/archive/html/help-gnu-emacs/2012-02/msg00227.html and https://lists.gnu.org/archive/html/help-gnu-emacs/2012-02/msg00237.html).

My current setup looks like this:

(add-to-list 'custom-theme-load-path "~/.emacs.d/themes/solarized")
(if (daemonp)
  (add-hook 'after-make-frame-functions
    (lambda (frame)
      (select-frame frame)
      (if (display-graphic-p frame)
        (load-theme 'solarized-light t)
        (load-theme 'solarized-dark t)
      )
    )
  )
  (load-theme 'solarized-light t)
)

The problem with this is that if I have a graphical frame open and then open a terminal frame with emacsclient -t, the theme solarized-dark gets applied to both the new terminal frame (which is correct) and the already open graphical frame (which should be left untouched, ideally). The equivalent happens when a terminal frame is already open and I open a new graphical frame with emacsclient -c.

EDIT: In case this is not possible with current Emacs24, are there any plans to make it possible again?

6
  • 1
    Afaik (from other search for this very purpose), the old color-theme implementation allowed for some per-buffer settings. With emacs 24 came a built-in theme support (known as deftheme or load-theme), which doesn't support this: the theme is the same for the whole emacs session. For this kind of setup, maybe you can manage an acceptable hack by always loading solarized-dark, and using the -rv flag to start the graphical frames, but it will be far from perfect.
    – T. Verron
    Commented Oct 12, 2014 at 11:39
  • One workaround is to run separate clients for the terminal and for the GUI as discussed here: emacs.stackexchange.com/q/41/93 Depending on your workflow, this may not be a feasible solution. It works for me because I am doing different types of tasks in each, with little or no overlap.
    – nispio
    Commented Oct 12, 2014 at 14:43
  • If the inbuilt customt theming in emacs24 does not fit your needs, you can always use color-theme.el which IIRC had frame dependent color settings. There is no great advantage in using the inbuilt theming capabilities apart from ubiquity.
    – Vamsi
    Commented Oct 12, 2014 at 18:10
  • 2
    color-theme-buffer-local provides buffer-local theme support for both color-theme and the Emacs 24 theme systems. My version of the solarized theme also supports both systems, if you decide to go this route.
    – sanityinc
    Commented Oct 13, 2014 at 7:27
  • @sanityinc Thanks; I'm using Emacs 24.3, and there seem to be some problems, see github.com/vic/color-theme-buffer-local/issues/5. But I'll keep an eye on the project.
    – andreas-h
    Commented Oct 13, 2014 at 12:14

4 Answers 4

9

You cannot do this using different themes. The solution is to create a theme that has different face definitions depending on the terminal. If you look at an example like font-lock-comment-face, you'll see how it works. Instead of specifying ((class color) (min-colors 88) (background dark)) you could also specifcy (type tty) or (type graphic) etc. The manual has more info.

(defface font-lock-comment-face
  '((((class grayscale) (background light))
 :foreground "DimGray" :weight bold :slant italic)
(((class grayscale) (background dark))
 :foreground "LightGray" :weight bold :slant italic)
(((class color) (min-colors 88) (background light))
 :foreground "Firebrick")
(((class color) (min-colors 88) (background dark))
 :foreground "chocolate1")
(((class color) (min-colors 16) (background light))
 :foreground "red")
(((class color) (min-colors 16) (background dark))
 :foreground "red1")
(((class color) (min-colors 8) (background light))
 :foreground "red")
(((class color) (min-colors 8) (background dark))
 :foreground "yellow")
(t :weight bold :slant italic))
  "Font Lock mode face used to highlight comments."
  :group 'font-lock-faces)

I guess you could write a function that takes two themes and produces a merged theme, with faces from one theme being assigned (type tty) and faces from the other theme being assigned (type graphic) where both of the original themes use t.

7

color-theme-buffer-local provides buffer-local theme support for both color-theme and the Emacs 24 theme systems. My version of the solarized theme also supports both systems, if you decide to go this route.

1
  • thanks for the pointer to your version of solarized, I really like it!
    – andreas-h
    Commented Feb 26, 2015 at 0:14
3

You can define conditions for face customizations, like "terminal mode" or "minimal colors" in the customization editor:

When customizing a face, click the [State] button and select "Show all display specs". Then you can set up the conditions for further specialization of the face.

Here's a minimal variant to set the background color to black when the frame is a tty:

(custom-set-faces
 '(default (
            (((type tty) (min-colors 256))
             (:background "black"))
            (t
             (:background "#181a26")))
    ))
1

Here is a (somewhat hacky) function that creates a copy of a given theme that applies only to TTY terminals (as suggested in Alex' answer). This theme can then be enabled on top of your default theme but will only affect TTY frames.

(defun rgmacs/copy-theme-tty (from-theme to-theme)
  "Copies all faces from from-theme to to-theme but restricts to TTY frames only."
  (dolist (entry (get from-theme 'theme-settings))
    (when (eq (car entry) 'theme-face)
      (let ((face (nth 1 entry))
            (face-specs (nth 3 entry))
            (new-specs))
        (dolist (face-spec face-specs)
          (let ((display (car face-spec))
                (rest (cdr face-spec)))
            (cond
             ((listp display)
              (progn
                (setq display (cl-copy-seq display))
                (add-to-list 'display '(type tty))))
             ((eq display t)
              (setq display '((type tty)))))
            (add-to-list 'new-specs (append `(,display) rest))))
        (custom-theme-set-faces to-theme `(,face ,new-specs))))))

I use it as follows:

(load-theme 'leuven)             ;; load and enable default theme
(load-theme 'spacemacs-dark t t) ;; load but do not enable desired theme for TTY frames

;; create a copy of the latter theme, but restrict it to TTYs
(deftheme spacemacs-dark-tty)
(rgmacs/copy-theme-tty 'spacemacs-dark 'spacemacs-dark-tty)

;; now enable it (on top of currently active themes)
(enable-theme 'spacemacs-dark-tty)

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.