Skip to content

Instantly share code, notes, and snippets.

@dgl
Last active June 6, 2025 12:58
Show Gist options
  • Save dgl/cfa357ab9f77818e28465e3c9e2435f3 to your computer and use it in GitHub Desktop.
Save dgl/cfa357ab9f77818e28465e3c9e2435f3 to your computer and use it in GitHub Desktop.
Check for DECDHL support (DEC double-height lines)
#!/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