Last active
June 6, 2025 12:58
-
-
Save dgl/cfa357ab9f77818e28465e3c9e2435f3 to your computer and use it in GitHub Desktop.
Check for DECDHL support (DEC double-height lines)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# © David Leadbeater, 2025 <http://©.st/dgl> | |
# SPDX-License-Identifier: 0BSD | |
# | |
# Detect whether terminals support DECDHL (DEC double-height lines). This is | |
# not perfect, as terminals are a mess, to put it mildly. | |
# | |
# If you want see the output in your terminal, try: curl -i ip.wtf | |
# | |
# Usage: | |
# Run directly to see the variables. | |
# | |
# Or, in bash, or a script: | |
# . check-decdhl | |
# check_decdhl | |
# echo $DECDHL | |
# | |
# Known issues: | |
# - Alacritty gets detected as supporting, when it doesn't (it seems to | |
# understand that DECDHL has an impact on cursor position, but doesn't | |
# render it). | |
# - Apple Terminal does not implement cursor position correctly, we guess | |
# based on the $TERM_PROGRAM environment variable and a heuristic, but this | |
# may not always work. | |
# - xterm before patch #380 will segfault if using TrueType fonts (e.g. | |
# xterm -fa Mono; which you need to correctly render the emoji). | |
# - On Windows there are multiple layers to getting Unicode working correctly, | |
# if you use "curl.exe -i ip.wtf" to test you may see Mojibake. Run "chcp | |
# 65001" first. This only works out of the box on WezTerm on Windows as | |
# Windows itself does not ship flag emojis. (Windows Terminal has other | |
# issues, but non-flag emojis in the BMP should work.) | |
# | |
# Terminals that do support DECDHL: | |
# - xterm (and so anything claiming TERM=xterm should) | |
# - VT100 (yes, but obviously not with emojis, etc.) | |
# - Windows Terminal | |
# - Apple Terminal | |
# - MinTTY (no emoji support) | |
# - WezTerm (with some strange behaviour where it renders \e#3, ignores #4) | |
# | |
# Terminals that don't support DECDHL: | |
# - Ghostty (https://github.com/ghostty-org/ghostty/discussions/3243) | |
# - iTerm2 | |
# - Kitty (https://github.com/kovidgoyal/kitty/issues/6816, but see | |
# https://github.com/kovidgoyal/kitty/blob/master/docs/text-sizing-protocol.rst) | |
# - Alacritty (but see above) | |
# - VTE based terminals (strong no: :( https://bugzilla.gnome.org/show_bug.cgi?id=587049) | |
# | |
getpos() { | |
local r | |
printf '\e[6n' > /dev/tty | |
read -d R r | |
echo "${r/*;}" | |
} | |
check_decdhl() { | |
local pos | |
if [[ -z "$COLUMNS" ]]; then | |
printf '\e[10000G' | |
# Note if you run bash interactively, it sets COLUMNS for you. | |
# But set it ourselves if not set so this works in scripts too. | |
COLUMNS=$(getpos) | |
printf '\e[A' | |
fi | |
# Does this terminal render emoji as double width? | |
printf '\e[G\e[K✅' | |
pos=$(getpos) | |
if [[ $pos = 3 ]]; then | |
EMOJI=1 | |
else | |
EMOJI=0 | |
fi | |
# Does this terminal support double height fonts? | |
printf '\e[G\e[2K\e#3' | |
eval "printf ' %.0s' {1..$[1+$COLUMNS/2]}" | |
pos=$(getpos) | |
if [[ $pos = 2 ]]; then | |
# Yes, xterm style reporting (line wrapped) | |
DECDHL=1 | |
# Delete extra line | |
printf '\e[M\e[F' | |
elif [[ $pos = $[2+$COLUMNS/2] ]]; then | |
# Correct cursor position for non-supporting, but still could be Apple | |
# Terminal. | |
if [[ $TERM_PROGRAM = Apple_Terminal ]]; then | |
DECDHL=1 | |
DECDHL_EMOJI=1 | |
# Reset state so there is no output left. | |
printf '\e[M\e[G\e[2K' | |
return | |
else | |
# It could still be Apple Terminal, but without the environment variable. | |
printf '\e[G\e[2Kx\e[2b🤗\e[2b' | |
# Expect "xxx🤗🤗🤗", but Apple Terminal renders. "xxx🤗[cursor]" => 6, if REP | |
# isn't supported at all the result is 4. | |
# Windows Terminal does not get here, but if it did it may trip: | |
# https://github.com/microsoft/terminal/issues/15390 | |
pos=$(getpos) | |
if [[ $pos = 6 ]]; then | |
# Apple terminal heuristic | |
DECDHL=1 | |
DECDHL_EMOJI=1 | |
# Reset state so there is no output left. | |
printf '\e[M\e[G\e[2K' | |
return | |
fi | |
DECDHL=0 | |
fi | |
else | |
# Strange answer | |
DECDHL=0 | |
fi | |
printf '\e[G\e[2K\e#3' | |
eval "printf '☑️%.0s' {1..$[1+$COLUMNS/4]}" | |
pos=$(getpos) | |
if [[ $pos = 3 ]]; then | |
# Yes, xterm style reporting (line wrapped) | |
DECDHL_EMOJI=1 | |
# Delete extra line | |
printf '\e[M\e[F' | |
elif [[ $pos = $[3+$COLUMNS/2] ]]; then | |
# Correct cursor position for non-supporting, Apple was checked above. | |
DECDHL_EMOJI=0 | |
else | |
# Strange answer | |
DECDHL_EMOJI=0 | |
fi | |
# Reset state so there is no output left. | |
printf '\e[M\e[G\e[2K' | |
} | |
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then | |
check_decdhl | |
echo "EMOJI=$EMOJI, DECDHL=$DECDHL, DECDHL_EMOJI=$DECDHL_EMOJI" | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment