34

I've found that terminal emacs does not render the correct colors unless I explicitly set TERM=xterm-256color. I use gnome-terminal, and from what I understand, TERM should be set to gnome-256color. Similarly, I tend to use tmux a lot, which advises against any TERM setting other than screen-256color. Unfortunately, both of those settings (within their respective context - gnome-terminal or tmux) result in emacs having wrong colors, whereas vim displays colors correctly. However, if I export TERM=xterm-256color, the colors work just fine in emacs.

Can anyone explain what's going on, or offer a solution?

Update

Here's what I'm dealing with:

enter image description here

I can get the colors to look correct in the terminal by adding the following to my init.el:

(defun terminal-init-gnome ()
  "Terminal initialization function for gnome-terminal."

  ;; This is a dirty hack that I accidentally stumbled across:
  ;;  initializing "rxvt" first and _then_ "xterm" seems
  ;;  to make the colors work... although I have no idea why.
  (tty-run-terminal-initialization (selected-frame) "rxvt")

  (tty-run-terminal-initialization (selected-frame) "xterm"))

This feels really, really wrong though. There has to be a logical explanation for this...

P.S.

I have very little knowledge of terminfo and the precise role that $TERM plays in the process of color terminal behavior. If it's safe to always use xterm-256color (even when $TERM "should" be gnome-256color or screen-256color), I'll go with that.

Charles
  • 6,199
  • 6
  • 50
  • 66
  • My $TERM is `screen` when I'm in tmux, and colors work fine in emacs. `tmate` actually sets $TERM to `screen-256color`, which messes up my emacs colors, so I have to manually set it to `screen`. – nnyby Aug 29 '18 at 19:47

6 Answers6

28

Maybe I'm not understanding something, buy why don't you run emacs like this:

TERM=xterm-256color emacs -nw

This way Emacs has its own TERM setting that you know works. You can also make an alias or wrap this in shell-script.

event_jr
  • 17,467
  • 4
  • 47
  • 62
  • 6
    I'd much rather fix the actual problem than cover it up. (I didn't downvote you though...) – Charles Oct 05 '11 at 21:21
  • 3
    fair enough. The downvote is douchey though. It's not unimaginable that someone doesn't know they can assign a specific value to an environment variable just for a sub-shell. – event_jr Oct 09 '11 at 17:30
  • Yeah, I don't think the -1 was called for. I +1'd you to balance things out. – Charles Oct 11 '11 at 03:16
  • I had a global setting for XTERM=xterm-256color in my bashrc, but this screw up prompt colors in some ssh terminals. Using this command as an alias to emacs allowed me to have both prompt colors and emacs colors. Thanks. – Vasiliy Jul 28 '17 at 14:07
20

Terminals are a special type of device. When a process sends special byte sequences (called control sequences) to the terminal, it performs some action (like cursor positioning, change colors, etc).

You can read the ANSI terminal codes to find more detail about control sequences.

But terminals come from 70s, when hardware was limited in its capabilities, and a terminal cannot provide info about its capabilities (ie. which sequences it supports).

$TERM was used to resolve this issue - it allows programs to know what to send to the terminal to get the job done. termcap and terminfo are databases that store info about terminal capabilities for many $TERM names. If your $TERM is not in the db, you must ask an administrator to add it.

All terminal emulators inherit these limitations from old hardware terminals. So they need a properly set $TERM, and the terminfo/termcap DB MUST have data for this terminal. When a virtual terminal starts it sets the $TERM variable for you (and inside programs like bash). If $TERM is not in the terminfo/termcap you can quickly define an alias from $TERM to xterm-256color (you can find examples in the termcap file on how to do that).

gavenkoa
  • 45,285
  • 19
  • 251
  • 303
10

This behavior has to do with the logic EMACS uses to determine whether the terminal background is dark or light. Run M-x list-colors-display with TERM set to either xterm-256color or screen-256color and you'll see that the exact same colors are listed. As you pointed out in the comments, the difference in color schemes that you've observed is due to the frame background mode. To see this, with your TERM set to screen-256color, compare the colors in

emacs -Q -nw --eval "(setq frame-background-mode 'light)"

and

emacs -Q -nw --eval "(setq frame-background-mode 'dark)"

The function frame-set-background-mode (in frame.el) checks to see whether the terminal type matches "^\\(xterm\\|\\rxvt\\|dtterm\\|eterm\\)" if it can't deduce the background color otherwise.

Within a running session, you can change the color scheme to 'light by evaluating

(let ((frame-background-mode 'light)) (frame-set-background-mode nil))
Samuel Isaacson
  • 345
  • 3
  • 6
  • Support for `screen-256color` is not widespread, I would recommend avoiding it in the interests of portability. Just go with `xterm-256color` which is widely supported. – ocodo Nov 27 '14 at 01:17
  • Where does the `dark` , `light` themes' configuration file is located @Samuel Isaacson – alper Jul 19 '20 at 13:53
9

I am not that familiar with how emacs handles different terminals exactly. But looking at lisp/term directory in emacs sources, I found out that the existence of a function terminal-init-xxx allows you to add support for different terminals. For example, I've got:

(defun terminal-init-screen ()
  "Terminal initialization function for screen."
   ;; Use the xterm color initialization code.
   (xterm-register-default-colors)
   (tty-set-up-initial-frame-faces))

in my .emacs, which adds support for screen-256color. You may try defining a similar function for gnome by renaming the above function to terminal-init-gnome.

NOTE: If you are interested, you can try to track down the calls from tty-run-terminal-initialization code. It first gets the terminal type using tty-type function, then looks at certain locations to load a relevant terminal file, then tries to locate the matching terminal-init-xxx function, and finally calls it. It may help you figure out the correct name for gnome-terminal.


It looks like unless your TERM indicates that your terminal has 256 colors, emacs will only use 8. Changing TERM to gnome-256color allowed the color registration functions to work.


There is a way to cheat, after all. When I run gnome-terminal, my terminal is set to xterm by default. Instead of changing TERM variable, it is possible to redirect xterm to another terminal, say, gnome-256color. Simply create the directory $(HOME)/.terminfo/x, then run ln -s /usr/share/terminfo/g/gnome-256color ~/.terminfo/x/xterm. I think this is better than setting TERM manually in .bashrc, because it only redirects a particular terminal to something else. A console login would still leave TERM as linux, and not xterm-256color.

vhallac
  • 13,301
  • 3
  • 25
  • 36
  • I tried using your code, replacing "screen" with "gnome", and it does get called at start-up. However, emacs complains with "Symbol's function definition is void: xterm-register-default-colors". I'll dig a little further into it and see what I can figure out. – Charles Oct 05 '11 at 20:13
  • It is defined in 'term/xterm.el' in emacs RTL. Adding the line `(load "term/xterm")` before the function should do the trick. – vhallac Oct 05 '11 at 20:54
  • Alright, needed to `(load "term/xterm")`. Scratch that though, `(tty-run-terminal-initialization (selected-frame) "xterm")` also does the trick - but the colors are still wrong. Is there some way to trace emacs? I'd like to see what's being passed to `tty-run-terminal-initialization`. On another related note, I can't seem to find where `tty-run-terminal-initialization` is defined... Thanks for your help BTW - any more advice you can give would be greatly appreciated! – Charles Oct 05 '11 at 20:58
  • I was hoping that gnome terminal and xterm would share the same colors. :( I suggest you get the el sources for emacs for further digging. The function is defined (and called from) `fonts.el`, and the caller is called by `frame.el` as part of frame creation (via `frame-creation-function-alist`). Good luck. If I learn anything else that can help, I'll update the answer. – vhallac Oct 05 '11 at 21:12
  • "I was hoping that gnome terminal and xterm would share the same colors." - it would seem that they technically do (unless I'm mistaken...), considering the fact that setting `TERM=xterm-256color` results in a perfect match when compared with the GUI colors. I wonder if there's some part in the call chain that checks again for "xterm-256color". Oddly enough, if I use "rxvt" instead of "xterm", I get really close colors (they're just a tad too light colored"). Very, very odd... – Charles Oct 05 '11 at 21:18
  • It is the function `display-color-cells` that reports gnome terminal to have only 8 colors - and the color initialization functions honor it. Unfortunately, that is a C function, so I don't know how it works, nor how you can trick it to report 256 colors (aside from changing TERM). Still reading up. – vhallac Oct 05 '11 at 21:41
  • Yeah, I tried executing `(display-color-cells)` and it returned `256`. Now, what's interesting is that if I first initialize "rxvt" and then "xterm", the colors look right. I'd really like to get to the bottom of this though... I'm adding an image to my post so you can see what's going on. – Charles Oct 05 '11 at 22:23
  • 1
    OMG - I think I realize what's going on here. Emacs appears to be looking at the background color of the terminal and changing the colors, I presume to be legible with white text on black. If I fire up a white urxvt term, the colors are correct - if I change the background to white, I get the same problem in gnome-terminal. The "hack" I added to my post just seems to cause emacs to stop compensating for the dark background... I'll need to play around with it some more... – Charles Oct 05 '11 at 23:30
  • I am not sure but solution also speed up the opening time of emacs daemon – alper Jul 19 '20 at 13:41
5

Add this to your ~/.emacs:

(add-to-list 'term-file-aliases
    '("st-256color" . "xterm-256color"))

It tells emacs that if it sees TERM=st-256color then it should initialize the terminal as if it had seen TERM=xterm-256color.


Longer answer:

Emacs is showing strange colors because it thinks your terminal can only support 8 colors. In Emacs, run M-x list-colors-display to see the colors it thinks are available. The correct number of colors is detected during terminal-specific initialization. It says, in part:

Each terminal type can have its own Lisp library that Emacs loads when run on that type of terminal.

On my machine, the terminal-specific initialization files are in /usr/local/share/emacs/25.*/lisp/term. It has files for xterm, rxvt, screen, etc. but nothing for st. We need to help Emacs find the right initialization file. The documentation further says:

If there is an entry matching TERM in the term-file-aliases association list, Emacs uses the associated value in place of TERM

So that association list is a recommended way to handle unknown terminals. It works without you having to manually override the TERM environment variable.

mndrix
  • 3,131
  • 1
  • 30
  • 23
3

On ubuntu 10.04 I too had noticed that running emacs -nw inside byobu/tmux/screen was using different colours from emacs -nw in the regular gnome-terminal.

I found that this is because byobu was setting TERM to screen-bce. Then setting TERM to xterm (for me, in the normal gnome-terminal TERM=xterm) gave me the same syntax highlighting when not running through byobu/screen.

So still not sure what the proper solution is.

See also this post: Emacs Python-mode syntax highlighting

Community
  • 1
  • 1
hargriffle
  • 196
  • 2