Last active
June 24, 2025 16:17
-
-
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 |
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
(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
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-heightputty/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 reportsEMOJI=1, DECDHL=1, DECDHL_EMOJI=0
but the emoji checkboxes overlap making it impossible to see, unless you highlight the text