Last active
March 27, 2025 05:18
-
-
Save Jaykul/f9aac8753b5fe39fa24a96bf7f4dc6b7 to your computer and use it in GitHub Desktop.
Virtual terminal queries (DECRQSS) return values as INPUT but allow us to test support for syntax
This file contains 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
function Get-ColorMode { | |
<# | |
.SYNOPSIS | |
Tests for FullColor (RGB) mode and X11/XTerm (XColor) modes by writing SGR and verifying it with a DECRQSS | |
Returns "Uknown" if there's no DECRQSS support, or "FullColor" and/or "XColor" otherwise | |
.NOTES | |
See the XTerm FAQ for details on why some terminals use ; (to be compatible with a mistake made by XTerm) | |
https://invisible-island.net/xterm/xterm.faq.html#color_by_number | |
See the ISO-8613-6 standard for details on the ODA color modes | |
Free Here: https://www.itu.int/rec/T-REC-T.416-199303-I/en | |
I've never seen a full implementation of ODA's SGR codes in a terminal. It would have 7 parameters: | |
Foreground: ESC[38:Ps:Pi:P3:P4:P5:P6:P7:P8m | |
Fackground: ESC[48:Ps:Pi:P3:P4:P5:P6:P7:P8m | |
Where Ps is the color space (*and I've only ever seen 2 and 5 implemented) | |
0 = Implementation Defined (the default foreground color) | |
1 = Transparent | |
2 = RGB | |
3 = CMY | |
4 = CMYK | |
5 = Indexed (In terminals, this _almost always_ means XTerm's 256 colors) | |
For Ps = 0 or 1 there would be no other parameters. | |
For Ps = 5 there is only Pi: the index of the color in the palette (0-255) | |
For Ps = 2, 3, or 4, | |
the Pi parameter hypothetically specifies a colour space | |
defined in the document profile ... which of course does not exist in a terminal | |
This parameter is therefore ignored and expected to be empty by most terminals | |
The P7 is supposed to indicate a integer tolerance value | |
The P8 is supposed to indicate a color space associated with the tolerance (0 for CIELUV, 1 for CIELAB) | |
The most common case is Ps = 2, where the expected parameters are: | |
P3 = Red (0-255) | |
P4 = Green (0-255) | |
P5 = Blue (0-255) | |
P6 has no meaning | |
Per the specification | |
For Ps = 3 and 4: | |
P3 = Cyan | |
P4 = Magenta | |
P5 = Yellow | |
P6 = Black (but only for Ps = 4) | |
In MinTTY the 2nd parameter, Pi is the max value for the color channels (So you can use 0-100 or 0-255) | |
#> | |
[CmdletBinding()] | |
param() | |
$ColorMode = [Ordered]@{ | |
'48;2;255;0;255' = @{Name = 'FullColorCompatible' } # Konsole Compatible | |
'48;5;254' = @{Name = 'XColorCompatible' } # Konsole Compatible | |
'48:5:254' = @{Name = 'ODA-XColor' } # ISO-8613-6 indexed color space | |
'48:2:255:0:255' = @{Name = 'ITU-RGB' } | |
'48:2::255:0:255' = @{Name = 'ODA-RGB' } # XTerm's ISO-8613-6 implementation with no Pi | |
# MinTTY supports CMY(K) format, and uses Pi to specifying the maximum value for color channels (e.g. 100 or 255) | |
'48:3:255:255:0:255' = @{Name = 'ODA-CMY'; AsRgb = '48:2::0:255:0' } # mintty accepts CMY(K) but returns RGB | |
'48:4:255:255:0:255:255'= @{Name = 'ODA-CMYK'; AsRgb = '48:2::0:255:0' } # mintty accepts CMY(K) but returns RGB | |
'48:3:100:100:0:100' = @{Name = 'ODA-FlexCMY'; AsRgb = '48:2::0:255:0' } # mintty accepts CMY(K) but returns RGB | |
'48:3:100:100:0:100:50' = @{Name = 'ODA-FlexCMYK'; AsRgb = '48:2::0:255:0' } # mintty accepts CMY(K) but returns RGB | |
} | |
$SupportedModes = @(foreach ($SGR in $ColorMode.Keys) { | |
$DECRQSS = -join @( | |
# Set the background color | |
"`e[0m`e[$($SGR)m" | |
# Output the DECRQSS SGR query https://vt100.net/docs/vt510-rm/DECRQSS.html | |
"`eP`$qm`e`\" | |
# Reset the background | |
"`e[49m" | |
) | |
[console]::write($DECRQSS) | |
Start-Sleep -milli 25 # I'm sorry, but sometimes it doesn't respond fast enough | |
$Response = @(while ([console]::KeyAvailable) { [console]::ReadKey($true).KeyChar }) -join "" | |
# Strip the DCS and ST from the ends of the response and return just the SGR value as the Response | |
$Result, $Code = $Response -replace '^(?:\u001BP|\u0090)(\d+)\$r(?:0;)?(.*)m[\u001B\\\t]+$', '$1,$2' -split ',' | |
Write-Debug "QUERY: 'm' RESPONSE: '$($Result)r$($Code)m'" | |
# the result code is supposed to be 1 no matter what, no idea what other values represent, really | |
if ($Result -ne 1) { | |
Write-Verbose "Request Status String (DECRQSS) not supported (returned '$Result')" | |
continue | |
} | |
if ($Code -ne $SGR) { | |
Write-Verbose "Unexpected Response. We set '$SGR' but received '$Code'." | |
# The department of second chances. | |
# Terminals generally return their prefered separator, even if they handle both ; and : | |
# Windows Terminal apparently treats : separated SGR as ODA T.416 format | |
# See: | |
# Except they do not support color modes, so it just has an empty spot | |
# E.g. it returns 48:2::R:G:B where it should be 48:2:2:R:G:B | |
if (($SGR -replace '[;:]+', ':') -ne ($Code -replace '[;:]+', ':') -and $Code -ne $ColorMode[$SGR].AsRgb) { | |
continue | |
} | |
} | |
Write-Verbose "Working. We set '$SGR' and received '$Code'." | |
$SGR | |
}) | |
$ColorMode[$SupportedModes].Name | |
} |
This file contains 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
function Get-VtResponse { | |
<# | |
.SYNOPSIS | |
Write a VT ANSI escape sequence to the host and capture the response | |
.EXAMPLE | |
$Row, $Col = (Get-VtResponse "`e[6n") -split ';' -replace "[`e\[R]" | |
Gets the current cursor position into $Row and $Col | |
#> | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory)] | |
$Sequence | |
) | |
[console]::write($sequence) | |
Start-Sleep -milli 5 # I'm sorry, but sometimes it doesn't respond fast enough | |
@(while ([console]::KeyAvailable) { | |
[console]::ReadKey($true).KeyChar | |
}) -join "" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment