Skip to content

Commit

Permalink
[terminal] Adds VT sequence XTPUSHCOLORS, XTPOPCOLORS (#714).
Browse files Browse the repository at this point in the history
  • Loading branch information
christianparpart committed Jun 9, 2022
1 parent 6d04c24 commit 55fd9c2
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 0 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
- Adds configuration option `profiles.*.status_line.display` to be either `none` or `indicator` to reflect the initial state of the status line (more customizability of the Indicator status-line will come in future releases).
- Adds new action `ToggleInputProtection` to protect terminal application against accidental input (#697).
- Adds configuration options `logging.enabled` as well as `logging.file`.
- Adds VT sequence `XTPUSHCOLORS`, `XTPOPCOLORS` (#714).

### 0.3.1 (2022-05-01)

Expand Down
4 changes: 4 additions & 0 deletions src/terminal/Functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ constexpr inline auto TBC = detail::CSI(std::nullopt, 0, 1, std::nullopt
constexpr inline auto VPA = detail::CSI(std::nullopt, 0, 1, std::nullopt, 'd', VTType::VT100, "VPA", "Vertical Position Absolute");
constexpr inline auto WINMANIP = detail::CSI(std::nullopt, 1, 3, std::nullopt, 't', VTType::VT525, "WINMANIP", "Window Manipulation");
constexpr inline auto XTSMGRAPHICS= detail::CSI('?', 2, 4, std::nullopt, 'S', VTType::VT525 /*Xterm*/, "XTSMGRAPHICS", "Setting/getting Sixel/ReGIS graphics settings.");
constexpr inline auto XTPOPCOLORS = detail::CSI(std::nullopt, 0, ArgsMax, '#', 'Q', VTType::VT525 /*Extension*/, "XTPOPCOLORS", "Pops the color palette from the palette's saved-stack.");
constexpr inline auto XTPUSHCOLORS= detail::CSI(std::nullopt, 0, ArgsMax, '#', 'P', VTType::VT525 /*Extension*/, "XTPUSHCOLORS", "Pushes the color palette onto the palette's saved-stack.");
constexpr inline auto XTSHIFTESCAPE=detail::CSI('>', 0, 1, std::nullopt, 's', VTType::VT525 /*Xterm*/, "XTSHIFTESCAPE", "Set/reset shift-escape options.");
constexpr inline auto XTVERSION = detail::CSI('>', 0, 1, std::nullopt, 'q', VTType::VT525 /*Xterm*/, "XTVERSION", "Query terminal name and version");
constexpr inline auto CAPTURE = detail::CSI('>', 0, 2, std::nullopt, 't', VTType::VT525 /*Extension*/, "CAPTURE", "Report screen buffer capture.");
Expand Down Expand Up @@ -491,6 +493,8 @@ inline auto const& functions() noexcept
TBC,
VPA,
WINMANIP,
XTPOPCOLORS,
XTPUSHCOLORS,
XTSHIFTESCAPE,
XTSMGRAPHICS,
XTVERSION,
Expand Down
14 changes: 14 additions & 0 deletions src/terminal/Screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3279,6 +3279,20 @@ ApplyResult Screen<Cell>::apply(FunctionDefinition const& function, Sequence con
case WINMANIP: return impl::WINDOWMANIP(seq, _terminal);
case DECMODERESTORE: return impl::restoreDECModes(seq, *this);
case DECMODESAVE: return impl::saveDECModes(seq, *this);
case XTPOPCOLORS:
if (!seq.parameterCount())
_terminal.popColorPalette(0);
else
for (size_t i = 0; i < seq.parameterCount(); ++i)
_terminal.popColorPalette(seq.param<size_t>(i));
return ApplyResult::Ok;
case XTPUSHCOLORS:
if (!seq.parameterCount())
_terminal.pushColorPalette(0);
else
for (size_t i = 0; i < seq.parameterCount(); ++i)
_terminal.pushColorPalette(seq.param<size_t>(i));
return ApplyResult::Ok;
case XTSMGRAPHICS: return impl::XTSMGRAPHICS(seq, *this);
case XTVERSION:
_terminal.reply(fmt::format("\033P>|{} {}\033\\", LIBTERMINAL_NAME, LIBTERMINAL_VERSION_STRING));
Expand Down
33 changes: 33 additions & 0 deletions src/terminal/Terminal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ namespace terminal

namespace // {{{ helpers
{
constexpr size_t MaxColorPaletteSaveStackSize = 10;

void trimSpaceRight(string& value)
{
while (!value.empty() && value.back() == ' ')
Expand Down Expand Up @@ -123,6 +125,7 @@ Terminal::Terminal(unique_ptr<Pty> _pty,
selectionHelper_ { this },
highlightTimeout_(_highlightTimeout)
{
state_.savedColorPalettes.reserve(MaxColorPaletteSaveStackSize);
#if 0
hardReset();
#else
Expand Down Expand Up @@ -1729,4 +1732,34 @@ void Terminal::setHighlightRange(HighlightRange _range)
highlightRange_ = _range;
eventListener_.updateHighlights();
}

constexpr auto MagicStackTopId = size_t { 0 };

void Terminal::pushColorPalette(size_t slot)
{
if (slot > MaxColorPaletteSaveStackSize)
return;

auto const index = slot == MagicStackTopId
? state_.savedColorPalettes.empty() ? 0 : state_.savedColorPalettes.size() - 1
: slot - 1;

if (index >= state_.savedColorPalettes.size())
state_.savedColorPalettes.resize(index + 1);

state_.savedColorPalettes[index] = state_.colorPalette;
}

void Terminal::popColorPalette(size_t slot)
{
if (state_.savedColorPalettes.empty())
return;

auto const index = slot == MagicStackTopId ? state_.savedColorPalettes.size() - 1 : slot - 1;

state_.colorPalette = state_.savedColorPalettes[index];
if (slot == MagicStackTopId)
state_.savedColorPalettes.pop_back();
}

} // namespace terminal
3 changes: 3 additions & 0 deletions src/terminal/Terminal.h
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,9 @@ class Terminal
ColorPalette& colorPalette() noexcept { return state_.colorPalette; }
ColorPalette& defaultColorPalette() noexcept { return state_.defaultColorPalette; }

void pushColorPalette(size_t slot);
void popColorPalette(size_t slot);

ScreenBase& currentScreen() noexcept { return currentScreen_.get(); }
ScreenBase const& currentScreen() const noexcept { return currentScreen_.get(); }

Expand Down
1 change: 1 addition & 0 deletions src/terminal/TerminalState.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ struct TerminalState

ColorPalette defaultColorPalette;
ColorPalette colorPalette;
std::vector<ColorPalette> savedColorPalettes;

bool focused = true;

Expand Down
111 changes: 111 additions & 0 deletions src/terminal/Terminal_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,117 @@ TEST_CASE("Terminal.SynchronizedOutput", "[terminal]")
CHECK("Hello World" == trimmedTextScreenshot(mc));
}

TEST_CASE("Terminal.XTPUSHCOLORS_and_XTPOPCOLORS", "[terminal]")
{
using namespace terminal;

auto mc = MockTerm { ColumnCount(20), LineCount(1) };
auto& vtState = mc.terminal().state();

auto const originalPalette = vtState.colorPalette;

auto modifiedPalette = ColorPalette {};
modifiedPalette.palette[0] = 0xFF6600_rgb;

SECTION("pop on empty")
{
mc.writeToStdout("\033[#Q");
REQUIRE(vtState.savedColorPalettes.size() == 0);
REQUIRE(vtState.colorPalette.palette == originalPalette.palette);
}

SECTION("default")
{
mc.writeToStdout("\033[#P"); // XTPUSHCOLORS (default)
REQUIRE(vtState.savedColorPalettes.size() == 1);
REQUIRE(vtState.savedColorPalettes.back().palette == originalPalette.palette);
vtState.colorPalette.palette[0] = 0x123456_rgb;
REQUIRE(vtState.colorPalette.palette != originalPalette.palette);
mc.writeToStdout("\033[#Q"); // XTPOPCOLORS
REQUIRE(vtState.colorPalette.palette == originalPalette.palette);
}

SECTION("0")
{
mc.writeToStdout("\033[0#P"); // push current color palette to slot 1 (default).
REQUIRE(vtState.savedColorPalettes.size() == 1);
}

SECTION("1")
{
REQUIRE(vtState.savedColorPalettes.size() == 0);
mc.writeToStdout("\033[1#P"); // push current color palette to slot 1.
REQUIRE(vtState.savedColorPalettes.size() == 1);
}

SECTION("2")
{
REQUIRE(vtState.savedColorPalettes.size() == 0);
mc.writeToStdout("\033[2#P"); // push current color palette to slot 1.
REQUIRE(vtState.savedColorPalettes.size() == 2);
}

SECTION("10")
{
REQUIRE(vtState.savedColorPalettes.size() == 0);
mc.writeToStdout("\033[10#P"); // push current color palette to slot 10.
REQUIRE(vtState.savedColorPalettes.size() == 10);
}

SECTION("11")
{
REQUIRE(vtState.savedColorPalettes.size() == 0);
mc.writeToStdout("\033[11#P"); // push current color palette to slot 11: overflow.
REQUIRE(vtState.savedColorPalettes.size() == 0);
}

SECTION("push and direct copy")
{
vtState.colorPalette.palette[1] = 0x101010_rgb;
auto const p1 = vtState.colorPalette;
mc.writeToStdout("\033[#P");

vtState.colorPalette.palette[3] = 0x303030_rgb;
auto const p3 = vtState.colorPalette;
mc.writeToStdout("\033[3#P");

vtState.colorPalette.palette[2] = 0x202020_rgb;
auto const p2 = vtState.colorPalette;
mc.writeToStdout("\033[2#P");

REQUIRE(vtState.savedColorPalettes.size() == 3);
REQUIRE(vtState.colorPalette.palette == vtState.savedColorPalettes[2 - 1].palette);

mc.writeToStdout("\033[1#Q"); // XTPOPCOLORS
REQUIRE(vtState.savedColorPalettes.size() == 3);
REQUIRE(vtState.colorPalette.palette == vtState.savedColorPalettes[1 - 1].palette);

mc.writeToStdout("\033[2#Q"); // XTPOPCOLORS
REQUIRE(vtState.savedColorPalettes.size() == 3);
REQUIRE(vtState.colorPalette.palette == vtState.savedColorPalettes[2 - 1].palette);

mc.writeToStdout("\033[3#Q"); // XTPOPCOLORS
REQUIRE(vtState.savedColorPalettes.size() == 3);
REQUIRE(vtState.colorPalette.palette == vtState.savedColorPalettes[3 - 1].palette);

mc.writeToStdout("\033[#Q"); // XTPOPCOLORS
REQUIRE(vtState.savedColorPalettes.size() == 2);
REQUIRE(vtState.colorPalette.palette == p3.palette);

mc.writeToStdout("\033[#Q"); // XTPOPCOLORS
REQUIRE(vtState.savedColorPalettes.size() == 1);
REQUIRE(vtState.colorPalette.palette == p2.palette);

mc.writeToStdout("\033[#Q"); // XTPOPCOLORS
REQUIRE(vtState.savedColorPalettes.size() == 0);
REQUIRE(vtState.colorPalette.palette == p1.palette);

mc.writeToStdout("\033[#Q"); // XTPOPCOLORS (underflow)
REQUIRE(vtState.savedColorPalettes.size() == 0);
REQUIRE(vtState.colorPalette.palette == p1.palette);
}
}

TEST_CASE("Terminal.CurlyUnderline", "[terminal]")
{
auto const now = chrono::steady_clock::now();
Expand Down

0 comments on commit 55fd9c2

Please sign in to comment.