diff --git a/bin/executable_term-background b/bin/executable_term-background new file mode 100644 index 0000000..68bba05 --- /dev/null +++ b/bin/executable_term-background @@ -0,0 +1,61 @@ +#!/bin/sh +# term-background: print "light" or "dark" for the current terminal. +# +# Asks the terminal for its background color with the OSC 11 escape sequence, +# converts the reply to perceived luminance, and prints "light" or "dark". +# Prints "dark" (the safe default) when the terminal does not answer. +# +# All terminal I/O happens on /dev/tty, so this is safe inside $(...). +# +# Works locally and over SSH (the query reaches whichever real terminal draws +# the screen). Inside tmux/screen the multiplexer may answer instead of the +# outer terminal, so detection can be wrong there; override it with +# export TERM_BACKGROUND=light # or dark +# in ~/.environment.local on that machine. +# +# Tune the response wait via TERM_BACKGROUND_TIMEOUT (tenths of a second). + +# Need a controlling terminal to talk to. +[ -t 0 ] || { echo dark; exit 0; } +exec 3<>/dev/tty || { echo dark; exit 0; } + +# Raw mode with a short read timeout (min 0 time N = up to N*0.1s) so an +# unresponsive terminal costs us at most that long. +saved=$(stty -g <&3 2>/dev/null) || { echo dark; exit 0; } +stty raw -echo min 0 time "${TERM_BACKGROUND_TIMEOUT:-4}" <&3 2>/dev/null + +printf '\033]11;?\033\\' >&3 # OSC 11 query +answer=$(dd bs=64 count=1 <&3 2>/dev/null) + +stty "$saved" <&3 2>/dev/null # restore before anything else +exec 3>&- + +# Expected reply: ESC ] 11 ; rgb:RRRR/GGGG/BBBB (ESC \ | BEL) +case "$answer" in + *rgb:*) ;; + *) echo dark; exit 0 ;; +esac + +rgb=${answer#*rgb:} # RRRR/GGGG/BBBB +r=${rgb%%/*}; rgb=${rgb#*/} +g=${rgb%%/*}; rgb=${rgb#*/} +b=$rgb + +# Keep the most-significant two hex digits of each component. +r=${r%"${r#??}"} +g=${g%"${g#??}"} +b=${b%"${b#??}"} + +case "$r$g$b" in + ""|*[!0-9a-fA-F]*) echo dark; exit 0 ;; +esac + +r=$(( 0x$r )); g=$(( 0x$g )); b=$(( 0x$b )) + +# Rec. 601 perceived luminance, 0-255. Over half => light. +lum=$(( (r * 299 + g * 587 + b * 114) / 1000 )) +if [ "$lum" -gt 127 ]; then + echo light +else + echo dark +fi diff --git a/dot_vimrc b/dot_vimrc index d26fe50..73c5d32 100644 --- a/dot_vimrc +++ b/dot_vimrc @@ -53,9 +53,17 @@ call plug#end() let g:vim_markdown_folding_disabled = 1 -colorscheme gruvbox +" Pick the gruvbox light/dark variant from the terminal background. +" $TERM_BACKGROUND is set at shell startup by ~/bin/term-background (an OSC 11 +" query); default to dark when it is unset (non-interactive, no detection). let g:gruvbox_contrast_dark = 'hard' -set background=dark +let g:gruvbox_contrast_light = 'hard' +if $TERM_BACKGROUND ==# 'light' + set background=light +else + set background=dark +endif +colorscheme gruvbox let g:indent_guides_enable_on_vim_startup = 1 diff --git a/dot_zshrc b/dot_zshrc index e327cc2..864a48c 100644 --- a/dot_zshrc +++ b/dot_zshrc @@ -1,3 +1,10 @@ +# Detect the terminal's light/dark background (OSC 11) and expose it as +# $TERM_BACKGROUND so nvim and other tools can pick a matching theme. Must run +# *above* the p10k instant-prompt block because it reads the tty. +if [[ -o interactive && -z "$TERM_BACKGROUND" && -x "$HOME/bin/term-background" ]]; then + export TERM_BACKGROUND="$("$HOME/bin/term-background")" +fi + # Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc. # Initialization code that may require console input (password prompts, [y/n] # confirmations, etc.) must go above this block; everything else may go below.