Skip to content

Instantly share code, notes, and snippets.

@dgl
Last active June 24, 2025 16:17
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
@Jan69
Copy link

Jan69 commented Jun 24, 2025

urxvt also not support this, neither does terminology (based on EFL)

Konsole (based on kde tech) also has the weird behaviour like wezterm, where it overrides the bottom line with top line
doing this in the "wrong order" (first \e#4 then \e#3), in konsole has weird glitchy behaviour, but in wezterm just ignores the #4 line, rendering it empty, and has the #3 line overlap with the line below it due to double-height
putty/pterm supports everything fine (tried on linux, presumably windows version also works the same)

but running the script, both wezterm and putty/pterm report EMOJI=1, DECDHL=0, DECDHL_EMOJI=0 as if they didn't support it, even though they DO support it, and konsole reports EMOJI=1, DECDHL=1, DECDHL_EMOJI=0 but the emoji checkboxes overlap making it impossible to see, unless you highlight the text

@Jan69
Copy link

Jan69 commented Jun 24, 2025

qterminal (based on QT tech) KINDA supports it, but it's particularly weird for me, it doesn't like emojis?
it picks the text from the top half, but if I did printf '\e#4Hello world 👽\n\e#3X€Ptu W0ifo 😑\n' then it resulted in
scrot
(if the screen scrolls, then the emoji gets half-cut-off and the prompt it overwrite appears normally)

putting it in the "correct order" of \e#3 before \e#4 doesn't change anything, I didn't notice it being backwards at first

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment