How does Emacs know whether the background is light or dark?
Nov 4, 6:01pm

Emacs determines whether the background is light or dark primarily through the frame-background-mode variable, which can be set to either 'light or 'dark'. This setting influences how Emacs displays colors, syntax highlighting, and themes to ensure readability and visual comfort.

How Emacs Detects Background Mode

  1. Default Detection: Emacs tries to guess the background mode when it starts. It does this by checking the brightness of the default background color of the frame (window). If the background color is closer to white (high brightness), it assumes a light background; if it's closer to black (low brightness), it assumes a dark background.

  2. Manual Override: You can manually set the background mode by adding the following to your Emacs configuration file (e.g., ~/.emacs or ~/.emacs.d/init.el):

    elisp
    (setq frame-background-mode 'dark) ; or 'light

    This forces Emacs to use the specified mode, regardless of the actual background color.

  3. Theme Influence: Many Emacs themes (e.g., doom-themes, modus-themes) automatically set frame-background-mode based on the theme's design. For example, a theme designed for dark backgrounds will usually set this variable to 'dark.

  4. Dynamic Adjustment: Some modern Emacs setups or packages (like doom-emacs) may dynamically adjust this setting based on system-wide appearance settings (e.g., if your OS switches between light and dark mode).

  5. Color Brightness Calculation: Emacs calculates the brightness of the background color using the formula:

    text
    (sqrt (0.241 * R^2 + 0.691 * G^2 + 0.068 * B^2))

    If the result is above a certain threshold, it assumes a light background; otherwise, it assumes a dark background.

Why It Matters

  • Syntax Highlighting: Some color schemes are optimized for either light or dark backgrounds. For example, a dark theme might use bright colors for syntax highlighting, while a light theme might use darker colors.
  • Readability: Using the wrong mode can make text hard to read (e.g., light text on a light background).

Checking the Current Setting

You can check the current value of frame-background-mode by evaluating:

elisp
(frame-parameter nil 'background-mode)

or

elisp
frame-background-mode

in the Emacs scratch buffer (M-: or Ctrl+J after typing the expression).

Practical Example

If you use a dark theme but Emacs incorrectly detects a light background, you can force it to use dark mode:

elisp
(setq frame-background-mode 'dark)

This ensures that all colors and themes are rendered as intended for a dark background.

Would you like help configuring this for your specific setup?

Nov 4, 6:01pm
How can one check the background with the default detection?
Nov 4, 6:03pm

To check how Emacs automatically detects whether the background is light or dark using its default logic, you can inspect the value of frame-background-mode and the actual background color of your frame. Here’s how you can do it step-by-step:


1. Check the Current frame-background-mode

Run this command in Emacs (e.g., in the scratch buffer or after M-:):

elisp
frame-background-mode

This will return either 'light or 'dark, which is Emacs’ current assumption.


2. Check the Actual Background Color

To see the RGB values of the background color Emacs is using for detection:

elisp
(face-attribute 'default :background)

This returns a string like "#ffffff" (white) or "#000000" (black), or any other color.


3. See How Emacs Calculates Brightness

Emacs uses the following formula to calculate the brightness of the background color:

text
(sqrt (0.241 * R^2 + 0.691 * G^2 + 0.068 * B^2))

You can evaluate this for your background color to see the brightness value:

elisp
(let ((color (face-attribute 'default :background)))
  (when color
    (setq color (color-name-to-rgb color))
    (sqrt (+ (* 0.241 (expt (car color) 2))
             (* 0.691 (expt (cadr color) 2))
             (* 0.068 (expt (caddr color) 2))))))
  • If the result is greater than ~130, Emacs assumes a light background.
  • If it’s less than ~130, it assumes a dark background.

4. Check the Default Detection Logic

Emacs’ default detection is done in the function frame-set-background-mode. You can see the logic by looking at the source:

elisp
(find-function 'frame-set-background-mode)

This will open the source code where the brightness calculation and comparison happen.


5. Force a Re-detection

If you want to see how Emacs would detect the background mode for a specific color, you can use:

elisp
(frame-set-background-mode '#xRRGGBB)

Replace #xRRGGBB with a hex color (e.g., #xffffff for white).


Summary Table

Emacs Background Detection
frame-background-mode
Returns 'light or 'dark
(face-attribute 'default :background)
Returns the background color as a string
Brightness formula
Calculates brightness from RGB values
(find-function 'frame-set-background-mode)
Shows the detection logic

Note: If you’re using a GUI Emacs, the detection is based on the frame’s actual background color. In terminal Emacs, it depends on the terminal’s reported colors.

Would you like to test this with a specific color or theme?

Nov 4, 6:03pm
frame-background-mode returns nil and (face-attribute 'default :background) returns "unspecified-bg". They do not give any information in my case.
Nov 4, 6:06pm

Thank you for clarifying. If frame-background-mode returns nil and (face-attribute 'default :background) returns "unspecified-bg", it means Emacs is not explicitly set to either light or dark mode, and the background color is not explicitly defined for the default face. This often happens in terminal Emacs or when using a theme that doesn’t set the background color directly.

How to Proceed

1. Check the Terminal or GUI Context

  • In a terminal: Emacs may not have access to the terminal’s actual background color, so it can’t automatically detect the brightness.
  • In a GUI frame: Emacs usually detects the background color, but if the theme or configuration doesn’t set it, it may remain unspecified.

2. Check the Frame’s Background Color

Try this command to see the background color of the selected frame:

elisp
(frame-parameter nil 'background-color)

If this returns nil or "unspecified-bg", Emacs doesn’t know the background color.

3. Check the Default Face’s Background

Sometimes the background is set for the default face in a different way. Try:

elisp
(face-background 'default)

or

elisp
(face-spec-reset-face 'default)
(face-background 'default)

4. Check the Theme’s Background

If you’re using a theme, it might override the background. To see the current theme’s background:

elisp
(custom-theme-value background-color)

or list all face attributes for the default face:

elisp
(face-all-attributes 'default)

5. Manually Set the Background Mode

If Emacs can’t detect it, you can manually set it by adding this to your config:

elisp
(setq frame-background-mode 'dark) ; or 'light

This forces Emacs to use the specified mode for all color and theme calculations.

6. Check the Terminal’s Background

If you’re in a terminal, Emacs relies on the terminal emulator’s reported background color. Some terminals (like xterm, urxvt, or modern terminal emulators) support querying the background color, but not all do. You can try setting the terminal’s background color explicitly in your shell or terminal emulator settings.

7. Debugging in Terminal

If you’re in a terminal and want to see what Emacs thinks the background color is, you can use:

elisp
(getenv "COLORFGBG")

or check if your terminal supports dynamic color queries (e.g., using tput or ANSI escape codes).


Summary Table

Debugging Unspecified Background
(frame-parameter nil 'background-color)
Returns the frame’s background color
(face-background 'default)
Returns the background of the default face
(custom-theme-value background-color)
Returns the theme’s background color
(getenv "COLORFGBG")
Returns terminal color environment variable

Practical Next Step

If you want Emacs to behave as if the background is dark (or light), just set:

elisp
(setq frame-background-mode 'dark) ; or 'light

in your config.

Would you like help debugging this further for your specific terminal or GUI setup? If so, let me know whether you’re using Emacs in a terminal or as a GUI application.

Nov 4, 6:06pm
The suggestions from the summary table do not work for me. For instance, `(frame-parameter nil 'background-color)` gives "unspecified-bg". However, I've found that `(terminal-parameter nil 'background-mode)` gives `dark` with a black background and `nil` with a white background (though it should have given `light`). Could you give more information about this?
Nov 4, 6:51pm

Your observation about terminal-parameter is very insightful! This function is specifically relevant for terminal Emacs (not GUI Emacs) and is used to determine the background mode when Emacs is running inside a terminal emulator.


terminal-parameter and Background Mode

What is terminal-parameter?

  • (terminal-parameter nil 'background-mode) This function returns the background mode (dark or light) for the current terminal, as reported by the terminal emulator itself. It is only meaningful in terminal Emacs, not in GUI frames.

  • How it works: Emacs queries the terminal emulator for its default background color (usually via ANSI escape codes or environment variables). If the terminal reports a dark background (e.g., black or dark gray), Emacs sets this parameter to 'dark. If the terminal reports a light background (e.g., white or light gray), it should set it to 'light. However, not all terminal emulators report this correctly, which is why you sometimes get nil even for a white background.


Why You See nil for a White Background

  • Some terminal emulators do not report the background mode to Emacs, or do so inconsistently.
  • If the terminal does not explicitly tell Emacs that the background is light, terminal-parameter may return nil, even if the background is visually white.
  • This is a known limitation and depends on the terminal emulator (e.g., xterm, urxvt, alacritty, kitty, etc.).

How Emacs Uses This Information

  • If terminal-parameter returns dark, Emacs assumes a dark background.
  • If it returns nil, Emacs may fall back to other heuristics or leave frame-background-mode as nil, which can lead to suboptimal color choices in themes and syntax highlighting.

How to Fix or Work Around This

1. Manually Set frame-background-mode

You can override the detection and force Emacs to use the correct mode by adding this to your config (~/.emacs or ~/.emacs.d/init.el):

elisp
(setq frame-background-mode 'light) ; or 'dark

This ensures that all color themes and faces are rendered as if the background is light (or dark), regardless of what the terminal reports.

2. Set the Background Color Explicitly

If your terminal supports it, you can set the background color in your shell or terminal emulator config, and Emacs may pick it up. For example, in your shell startup file (e.g., .bashrc, .zshrc):

sh
export COLORFGBG="15;0"  # light background (foreground;background)

or

sh
export COLORFGBG="0;15"  # dark background

Then, in Emacs, you can use:

elisp
(when (getenv "COLORFGBG")
  (setq frame-background-mode (if (string-match "\\<15;" (getenv "COLORFGBG")) 'light 'dark)))

3. Use Terminal-Specific Workarounds

Some terminal emulators allow you to set the background mode or color dynamically. For example:

  • Alacritty, Kitty, WezTerm, iTerm2: These often support dynamic color reporting and can be configured to tell Emacs the correct background mode.
  • XTerm, URxvt: May require additional configuration or patches to report the background mode correctly.

4. Debug Terminal Reporting

To see what your terminal reports to Emacs, you can use:

elisp
(getenv "COLORFGBG")

and

elisp
(terminal-parameters)

This will show all parameters the terminal reports to Emacs.


Summary Table

Terminal Background Mode
(terminal-parameter nil 'background-mode)
Returns the terminal’s reported background mode
(getenv "COLORFGBG")
Returns the terminal’s foreground/background color codes
(terminal-parameters)
Returns all terminal parameters reported to Emacs

Practical Recommendation

If your terminal does not report the background mode correctly, the most reliable solution is to manually set frame-background-mode in your Emacs config:

elisp
(setq frame-background-mode 'light) ; or 'dark

This ensures consistent behavior regardless of terminal quirks.


Would you like help configuring your specific terminal emulator to report the background mode correctly, or do you prefer to manually set it in Emacs? If so, let me know which terminal emulator you’re using!

Nov 4, 6:51pm
I do not want manual settings. I want to debug in order to get bugs fixed. The bug may come from the terminal. What is the escape sequence for TERM=xterm-256color (used with both xterm and GNOME Terminal)?
Nov 4, 6:56pm

For debugging and fixing the automatic detection of terminal background mode in xterm and GNOME Terminal (both commonly using TERM=xterm-256color), the relevant ANSI escape sequences and environment variables are key. Here’s how you can investigate and debug the issue:


ANSI Escape Sequences for Background Mode

1. Querying Terminal Background Color

Terminals that support dynamic color reporting use ANSI escape sequences to query or set the background color. The most relevant sequences are:

  • Request Background Color:

    sh
    printf '\e]11;?\e\\'

    This sequence asks the terminal to respond with its current background color in RGB format (if supported).

  • Response Format: The terminal should respond with an escape sequence like:

    sh
    \e]11;rgb:RR/RR/RR\e\

    where RR/RR/RR are the red, green, and blue components in hexadecimal.

  • Request Background Mode (Light/Dark): There is no universal ANSI sequence to directly query whether the terminal is in "light" or "dark" mode. Instead, Emacs infers this from the reported background color.


2. How Emacs Uses This

Emacs uses the tput utility or direct ANSI queries to get the background color, then calculates its brightness to guess if it’s light or dark. If the terminal does not respond to the query, or responds with an unexpected format, Emacs may fail to set terminal-parameter correctly.


3. Testing Your Terminal

You can manually test if your terminal supports these queries:

a. Query Background Color

Run this in your terminal (outside Emacs):

sh
printf '\e]11;?\e\\'
  • If your terminal supports it, it will respond with the background color in RGB format (e.g., rgb:00/00/00 for black, rgb:ff/ff/ff for white).
  • If nothing happens, your terminal does not support this query.

b. Check COLORFGBG

Run:

sh
echo $COLORFGBG
  • This environment variable is often set by the terminal or shell to indicate the default foreground and background colors (e.g., 0;15 for black background, white text).

4. Debugging Steps

a. Check Terminal Responses

Run the following in your terminal to see if it responds to the background color query:

sh
printf '\e]11;?\e\\'

If you see no response, your terminal does not support dynamic color reporting.

b. Check Emacs’ Detection Logic

In Emacs, evaluate:

elisp
(terminal-parameters)

Look for the 'background-mode key. If it’s missing or nil, the terminal did not report the background mode.

c. Check xterm or GNOME Terminal Settings

  • xterm: Ensure it is compiled with support for dynamic color reporting. Some older versions or builds may not support it.
  • GNOME Terminal: It supports dynamic color reporting, but the feature must be enabled and the terminal must be properly configured.

d. Check for Terminal-Specific Bugs

  • xterm: Some versions do not respond to the \e]11;?\e\\ query by default. You may need to enable it with:
    sh
    xterm -xrm 'XTerm*reportColorState: true'
  • GNOME Terminal: Should support this by default, but bugs or custom profiles may interfere.

5. Common Issues

  • xterm: May not respond to the query unless explicitly configured.
  • GNOME Terminal: Usually supports it, but custom themes or profiles might override the reported color.
  • Shell/Environment: If $COLORFGBG is not set, Emacs has no fallback.

6. Filing Bugs

If your terminal does not respond to the query or reports incorrect values:

  • For xterm, check the xterm FAQ and consider reporting to the xterm maintainers.
  • For GNOME Terminal, report to the GNOME Terminal issue tracker.

7. Workaround for Emacs

If you want Emacs to guess based on the reported color, you can add this to your config:

elisp
(when (and (eq window-system 'x) (getenv "COLORFGBG"))
  (let ((bg (string-match "\\<[0-9]+$" (getenv "COLORFGBG"))))
    (setq frame-background-mode (if (and bg (string= (match-string 0 (getenv "COLORFGBG")) "15")) 'light 'dark))))

Summary Table

Terminal Debugging
printf '\e]11;?\e\\'
Query terminal background color
echo $COLORFGBG
Check shell environment for color info
(terminal-parameters)
See what Emacs knows about the terminal

Next Steps

  • If your terminal does not respond to the query, check its documentation or settings for "report background color" or similar options.
  • If it does respond but Emacs still doesn’t detect it, the bug is likely in Emacs’ terminal handling code. You can report this to the Emacs bug tracker.

Would you like help drafting a bug report for your terminal emulator or Emacs? If so, let me know which one you’d like to target!

Nov 4, 6:56pm
Thanks. I could check with printf '\e]11;?\e\\' that both xterm and GNOME Terminal give a correct answer: "11;rgb:0000/0000/0000" for a black background, and "11;rgb:ffff/ffff/ffff" for a white background. So this seems to be a bug in Emacs when the background is white.
Nov 4, 7:00pm

Thank you for confirming that both xterm and GNOME Terminal correctly respond to the ANSI escape sequence for background color queries. This means the terminals are doing their part, and the issue lies in how Emacs interprets or acts on this information when the background is white.


Debugging Emacs' Handling of Terminal Background

1. What Should Happen

When Emacs receives 11;rgb:ffff/ffff/ffff (white background), it should:

  • Calculate the brightness of the RGB value.
  • Set terminal-parameter to 'light and frame-background-mode to 'light.

If this doesn't happen, it points to a bug in Emacs' logic for parsing the terminal's response or setting the background mode.


2. How Emacs Processes the Response

Emacs uses the function xterm-get-color (or similar) to parse the response to \e]11;?\e\\. The parsed color is then used to set terminal-parameter and, ideally, frame-background-mode.

You can check Emacs' internal handling:

elisp
(terminal-parameters)

Look for the 'background-mode key. If it’s missing or nil even after the terminal reports a white background, Emacs is not updating it correctly.


3. Check Emacs' Brightness Calculation

Emacs calculates brightness using this formula:

elisp
(sqrt (+ (* 0.241 (expt R 2)) (* 0.691 (expt G 2)) (* 0.068 (expt B 2))))

For ffff/ffff/ffff (255, 255, 255), the brightness is:

elisp
(sqrt (+ (* 0.241 65025) (* 0.691 65025) (* 0.068 65025))) ; => ~255 (light)

If the brightness is above a certain threshold (usually ~130), Emacs should set the mode to 'light.


4. Possible Bugs in Emacs

  • Parsing the Response: Emacs might not be parsing the rgb:ffff/ffff/ffff response correctly for white backgrounds.
  • Setting terminal-parameter: Even if parsed, Emacs may not be updating terminal-parameter or frame-background-mode for white backgrounds.
  • Threshold Logic: The brightness threshold or logic might be incorrect for white backgrounds.

5. How to Confirm the Bug

a. Check the Parsed Color

Evaluate this in Emacs after setting a white background in your terminal:

elisp
(terminal-parameter nil 'background-color)

If this returns nil or an incorrect value, the parsing is broken.

b. Check the Brightness Calculation

If the color is parsed but the mode is not set, evaluate:

elisp
(let ((color "#ffffff"))
  (setq color (color-name-to-rgb color))
  (sqrt (+ (* 0.241 (expt (car color) 2))
           (* 0.691 (expt (cadr color) 2))
           (* 0.068 (expt (caddr color) 2)))))

This should return a high value (~255), confirming the color is light.

c. Check frame-background-mode

If the brightness is high but frame-background-mode is still nil, the bug is in how Emacs sets the mode based on the brightness.


6. Reporting the Bug

If you confirm that Emacs parses the color but fails to set the mode, you can report this to the Emacs bug tracker. Include:

  • Your Emacs version (M-x emacs-version).
  • The output of (terminal-parameters) for both black and white backgrounds.
  • The result of the brightness calculation for ffff/ffff/ffff.

Example Bug Report:

When using a white background in xterm or GNOME Terminal, Emacs correctly receives 11;rgb:ffff/ffff/ffff in response to the ANSI query, but terminal-parameter and frame-background-mode are not set to 'light. The brightness calculation for #ffffff returns ~255, but the mode remains nil.


7. Temporary Workaround

If you want Emacs to behave correctly until the bug is fixed, add this to your config:

elisp
(when (and (eq window-system 'x) (equal (terminal-parameter nil 'background-color) "#ffffff"))
  (setq frame-background-mode 'light))

Summary Table

Emacs Bug Debugging
(terminal-parameter nil 'background-color)
"#ffffff" for white background
Brightness calculation for #ffffff
~255 (light)
frame-background-mode
'light for white background

Next Steps

  • If you’d like, I can help you draft a detailed bug report for the Emacs maintainers, including all the diagnostic information.
  • Alternatively, you can explore the Emacs source code (e.g., term/xterm.el or term.el) to see where the parsing or mode-setting logic might be failing.
Nov 4, 7:00pm
In term/xterm.el, there is a function that sets background-mode to dark: "(set-terminal-parameter nil 'background-mode 'dark)". But no similar function for light. This could explain the behavior.
Nov 4, 7:10pm

You’ve found the smoking gun! The absence of a corresponding function to set background-mode to 'light in term/xterm.el is almost certainly the cause of the bug.


Analysis of the Issue

1. The Code in term/xterm.el

The function you referenced:

elisp
(set-terminal-parameter nil 'background-mode 'dark)

is likely called when Emacs detects a dark background (e.g., rgb:0000/0000/0000). However, there is no equivalent call for light backgrounds (e.g., rgb:ffff/ffff/ffff). This means:

  • When the terminal reports a dark background, Emacs explicitly sets background-mode to 'dark.
  • When the terminal reports a light background, nothing happens, so background-mode remains nil.

This explains why you see dark for black backgrounds but nil for white backgrounds, even though the terminal reports the correct color.


Why This Happens

  • Emacs’ logic assumes that if the background is not explicitly set to dark, it should remain nil (or rely on other heuristics).
  • However, there is no code to set it to 'light when the terminal reports a light background.
  • This is a clear oversight in the handling of terminal background mode detection.

How to Confirm

1. Check the Function Context

Look for where (set-terminal-parameter nil 'background-mode 'dark) is called in term/xterm.el. It’s likely in a function that processes the response to the \e]11;?\e\\ query.

2. Check for Brightness Logic

See if there’s a conditional that only sets background-mode for dark backgrounds, but not for light ones. For example:

elisp
(if (dark-background-p color)
    (set-terminal-parameter nil 'background-mode 'dark))

If there’s no else clause or equivalent for light backgrounds, that’s the bug.


How to Fix

1. Patch the Code

The fix is to add logic to set background-mode to 'light when the background color is light. For example:

elisp
(if (dark-background-p color)
    (set-terminal-parameter nil 'background-mode 'dark)
  (set-terminal-parameter nil 'background-mode 'light))

where dark-background-p is a function that returns nil for light colors.

2. Test the Fix

You can test this by adding the following to your Emacs config (as a temporary workaround):

elisp
(when (and (eq window-system 'x)
           (equal (terminal-parameter nil 'background-color) "#ffffff"))
  (set-terminal-parameter nil 'background-mode 'light)
  (setq frame-background-mode 'light))

Reporting the Bug

1. Draft a Bug Report

Here’s a template for your report to the Emacs bug tracker:


Subject: term/xterm.el does not set background-mode to 'light for light terminal backgrounds

Description: When using a terminal with a white background (e.g., xterm or GNOME Terminal), Emacs correctly queries the background color via \e]11;?\e\\ and receives rgb:ffff/ffff/ffff. However, term/xterm.el only sets background-mode to 'dark for dark backgrounds, and does not set it to 'light for light backgrounds. As a result, (terminal-parameter nil 'background-mode) returns nil for white backgrounds, even though the terminal reports the correct color.

This causes issues with themes and syntax highlighting, as Emacs does not recognize the background as light.

Steps to Reproduce:

  1. Open xterm or GNOME Terminal with a white background.
  2. Start Emacs in the terminal.
  3. Evaluate (terminal-parameter nil 'background-mode) — it returns nil, not 'light.
  4. Evaluate (terminal-parameter nil 'background-color) — it returns "#ffffff".

Expected Behavior: (terminal-parameter nil 'background-mode) should return 'light for light backgrounds.

Proposed Fix: In term/xterm.el, update the logic to set background-mode to 'light when the background color is light, similar to how it sets it to 'dark for dark backgrounds.

Emacs Version: [Output of M-x emacs-version]


2. Submit the Patch

If you’re comfortable with Emacs Lisp, you can propose a patch to the emacs-devel mailing list or attach it to the bug report. The fix is simple: add an else clause to set background-mode to 'light when the background is not dark.


Summary Table

Bug Fix Summary
background-mode not set for light backgrounds
Missing logic in term/xterm.el
Add else clause to set background-mode to 'light

Next Steps

  • Would you like help drafting the patch or the bug report in more detail?
  • If you want to test the fix locally, I can guide you through editing term/xterm.el and reloading it.
Nov 4, 7:10pm

This is a shared conversation and may include unverified content that doesn't reflect Mistral AI's views. Login to resume chatting.

Vibe can make mistakes. Check answers. Learn more