Skip to content

Commit 61ee7b0

Browse files
authored
Add support for querying the character cell size (#17504)
This PR add supports for two query sequences that are used to determine the pixel size of a character cell: * `CSI 16 t` reports the pixel size of a character cell directly. * `CSI 14 t` reports the pixel size of the text area, and when divided by the character size of the text area, you can get the character cell size indirectly (this method predates the introduction of `CSI 16 t`). These queries are used by Sixel applications that want to fit an image within specific text boundaries, so need to know how many cells would be covered by a particular pixel size, or vice versa. Our implementation of Sixel uses a virtual cell size that is always 10x20 (in order to emulate the VT340 more accurately), so these queries shouldn't really be needed, but some applications will fail to work without them. ## References and Relevant Issues Sixel support was added to conhost in PR #17421. ## Validation Steps Performed I've added some unit tests to verify that these queries are producing the expected responses, and I've manually tested on [XtermDOOM] (which uses `CSI 16 t`), and the [Notcurses] library (which uses `CSI 14 t`). [XtermDOOM]: https://gitlab.com/AutumnMeowMeow/xtermdoom [Notcurses]: https://github.com/dankamongmen/notcurses ## PR Checklist - [x] Tests added/passed
1 parent ee40166 commit 61ee7b0

File tree

6 files changed

+43
-8
lines changed

6 files changed

+43
-8
lines changed

src/terminal/adapter/DispatchTypes.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,8 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
589589
IconifyWindow = 2,
590590
RefreshWindow = 7,
591591
ResizeWindowInCharacters = 8,
592+
ReportTextSizeInPixels = 14,
593+
ReportCharacterCellSize = 16,
592594
ReportTextSizeInCharacters = 18
593595
};
594596

src/terminal/adapter/PageManager.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ void Page::SetAttributes(const TextAttribute& attr, ITerminalApi* api) const
5252
}
5353
}
5454

55+
til::size Page::Size() const noexcept
56+
{
57+
return { Width(), Height() };
58+
}
59+
5560
til::CoordType Page::Top() const noexcept
5661
{
5762
// If we ever support vertical window panning, the page top won't

src/terminal/adapter/PageManager.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ namespace Microsoft::Console::VirtualTerminal
2626
Cursor& Cursor() const noexcept;
2727
const TextAttribute& Attributes() const noexcept;
2828
void SetAttributes(const TextAttribute& attr, ITerminalApi* api = nullptr) const;
29+
til::size Size() const noexcept;
2930
til::CoordType Top() const noexcept;
3031
til::CoordType Bottom() const noexcept;
3132
til::CoordType Width() const noexcept;

src/terminal/adapter/SixelParser.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ namespace Microsoft::Console::VirtualTerminal
2828
public:
2929
static constexpr VTInt DefaultConformance = 9;
3030

31-
static til::size CellSizeForLevel(const VTInt conformanceLevel) noexcept;
32-
static size_t MaxColorsForLevel(const VTInt conformanceLevel) noexcept;
31+
static til::size CellSizeForLevel(const VTInt conformanceLevel = DefaultConformance) noexcept;
32+
static size_t MaxColorsForLevel(const VTInt conformanceLevel = DefaultConformance) noexcept;
3333

3434
SixelParser(AdaptDispatch& dispatcher, const StateMachine& stateMachine, const VTInt conformanceLevel = DefaultConformance) noexcept;
3535
void SoftReset();

src/terminal/adapter/adaptDispatch.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3648,6 +3648,12 @@ bool AdaptDispatch::WindowManipulation(const DispatchTypes::WindowManipulationTy
36483648
// Other Window Manipulation functions:
36493649
// MSFT:13271098 - QueryViewport
36503650
// MSFT:13271146 - QueryScreenSize
3651+
3652+
const auto reportSize = [&](const auto size) {
3653+
const auto reportType = function - 10;
3654+
_api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033[{};{};{}t"), reportType, size.height, size.width));
3655+
};
3656+
36513657
switch (function)
36523658
{
36533659
case DispatchTypes::WindowManipulationType::DeIconifyWindow:
@@ -3663,11 +3669,19 @@ bool AdaptDispatch::WindowManipulation(const DispatchTypes::WindowManipulationTy
36633669
_api.ResizeWindow(parameter2.value_or(0), parameter1.value_or(0));
36643670
return true;
36653671
case DispatchTypes::WindowManipulationType::ReportTextSizeInCharacters:
3666-
{
3667-
const auto page = _pages.VisiblePage();
3668-
_api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033[8;{};{}t"), page.Height(), page.Width()));
3672+
reportSize(_pages.VisiblePage().Size());
3673+
return true;
3674+
case DispatchTypes::WindowManipulationType::ReportTextSizeInPixels:
3675+
// Prior to the existence of the character cell size query, Sixel applications
3676+
// that wanted to know the cell size would request the text area in pixels and
3677+
// divide that by the text area in characters. But for this to work, we need to
3678+
// return the virtual pixel size, as used in the Sixel graphics emulation, and
3679+
// not the physical pixel size (which should be of no concern to applications).
3680+
reportSize(_pages.VisiblePage().Size() * SixelParser::CellSizeForLevel());
3681+
return true;
3682+
case DispatchTypes::WindowManipulationType::ReportCharacterCellSize:
3683+
reportSize(SixelParser::CellSizeForLevel());
36693684
return true;
3670-
}
36713685
default:
36723686
return false;
36733687
}

src/terminal/adapter/ut_adapter/adapterTest.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3377,10 +3377,23 @@ class AdapterTest
33773377

33783378
TEST_METHOD(WindowManipulationTypeTests)
33793379
{
3380+
// Our pixel size reports are based on a virtual cell size of 10x20 pixels
3381+
// for compatibility with the VT240 and VT340 graphic terminals.
3382+
const auto cellSize = til::size{ 10, 20 };
3383+
33803384
_testGetSet->PrepData();
3381-
_pDispatch->WindowManipulation(DispatchTypes::WindowManipulationType::ReportTextSizeInCharacters, NULL, NULL);
33823385
const auto [textBuffer, viewport, _] = _testGetSet->GetBufferAndViewport();
3383-
const std::wstring expectedResponse = fmt::format(L"\033[8;{};{}t", viewport.height(), textBuffer.GetSize().Width());
3386+
3387+
_pDispatch->WindowManipulation(DispatchTypes::WindowManipulationType::ReportTextSizeInCharacters, NULL, NULL);
3388+
std::wstring expectedResponse = fmt::format(L"\033[8;{};{}t", viewport.height(), textBuffer.GetSize().Width());
3389+
_testGetSet->ValidateInputEvent(expectedResponse.c_str());
3390+
3391+
_pDispatch->WindowManipulation(DispatchTypes::WindowManipulationType::ReportTextSizeInPixels, NULL, NULL);
3392+
expectedResponse = fmt::format(L"\033[4;{};{}t", viewport.height() * cellSize.height, textBuffer.GetSize().Width() * cellSize.width);
3393+
_testGetSet->ValidateInputEvent(expectedResponse.c_str());
3394+
3395+
_pDispatch->WindowManipulation(DispatchTypes::WindowManipulationType::ReportCharacterCellSize, NULL, NULL);
3396+
expectedResponse = fmt::format(L"\033[6;{};{}t", cellSize.height, cellSize.width);
33843397
_testGetSet->ValidateInputEvent(expectedResponse.c_str());
33853398
}
33863399

0 commit comments

Comments
 (0)