Skip to content

Commit

Permalink
Merge pull request #1208 from contour-terminal/random
Browse files Browse the repository at this point in the history
Various cursor/margin related fixes and minor QA improvements
  • Loading branch information
christianparpart authored Sep 21, 2023
2 parents 1ec01a8 + c4345f5 commit 4189e91
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 142 deletions.
2 changes: 2 additions & 0 deletions metainfo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@
<li>Fixes VT sequence DECSTBM and DECSLRM defaulting parameters (#1164).</li>
<li>Fixes VT sequence DECFRA (#1189).</li>
<li>Fixes VT sequence DECSCPP and DECCOLM (#1205).</li>
<li>Fixes VT sequence DECALN to properly reset margins when statusline is shown</li>
<li>Fixes VT sequences CUU/CUD/CUF/CUB to better respect margins (#1201)</li>
<li>Adds inheritance of profiles in configuration file based on default profile (#1063).</li>
<li>Clear search term when switch to insert vi mode (#1135)</li>
<li>Delete dpi_scale entry in configuration (#1137)</li>
Expand Down
129 changes: 79 additions & 50 deletions src/vtbackend/Functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,35 @@ struct FunctionDefinition // TODO: rename Function
std::string_view mnemonic;
std::string_view comment;

template <typename... Args>
std::string operator()(Args&&... parameters) const
{
assert(static_cast<size_t>(minimumParameters) <= sizeof...(Args));
assert(sizeof...(Args) <= static_cast<size_t>(maximumParameters));
std::string result;
result.reserve(8);
switch (category)
{
case FunctionCategory::C0: break;
case FunctionCategory::ESC: result += "\033"; break;
case FunctionCategory::CSI: result += "\033["; break;
case FunctionCategory::OSC: result += "\033]"; break;
case FunctionCategory::DCS: result += "\033P"; break;
}
if (leader)
result += leader;
if constexpr (sizeof...(Args) > 0)
{
result.reserve(sizeof...(Args) * 4);
((result += fmt::format("{};", std::forward<Args>(parameters))), ...);
result.pop_back(); // remove trailing ';'
}
if (intermediate)
result += intermediate;
result += finalSymbol;
return result;
}

using id_type = uint32_t;

[[nodiscard]] constexpr id_type id() const noexcept
Expand Down Expand Up @@ -186,16 +215,16 @@ namespace detail // {{{
std::string_view mnemonic,
std::string_view description) noexcept
{
return FunctionDefinition { FunctionCategory::ESC,
0,
intermediate.value_or(0),
finalCharacter,
0,
0,
vt,
VTExtension::None,
mnemonic,
description };
return FunctionDefinition { .category = FunctionCategory::ESC,
.leader = 0,
.intermediate = intermediate.value_or(0),
.finalSymbol = finalCharacter,
.minimumParameters = 0,
.maximumParameters = 0,
.conformanceLevel = vt,
.extension = VTExtension::None,
.mnemonic = mnemonic,
.comment = description };
}

constexpr auto CSI(std::optional<char> leader,
Expand All @@ -208,16 +237,16 @@ namespace detail // {{{
std::string_view description) noexcept
{
// TODO: static_assert on leader/intermediate range-or-null
return FunctionDefinition { FunctionCategory::CSI,
leader.value_or(0),
intermediate.value_or(0),
finalCharacter,
argc0,
argc1,
vt,
VTExtension::None,
mnemonic,
description };
return FunctionDefinition { .category = FunctionCategory::CSI,
.leader = leader.value_or(0),
.intermediate = intermediate.value_or(0),
.finalSymbol = finalCharacter,
.minimumParameters = argc0,
.maximumParameters = argc1,
.conformanceLevel = vt,
.extension = VTExtension::None,
.mnemonic = mnemonic,
.comment = description };
}

constexpr auto CSI(std::optional<char> leader,
Expand All @@ -230,16 +259,16 @@ namespace detail // {{{
std::string_view description) noexcept
{
// TODO: static_assert on leader/intermediate range-or-null
return FunctionDefinition { FunctionCategory::CSI,
leader.value_or(0),
intermediate.value_or(0),
finalCharacter,
argc0,
argc1,
VTType::VT100,
ext,
mnemonic,
description };
return FunctionDefinition { .category = FunctionCategory::CSI,
.leader = leader.value_or(0),
.intermediate = intermediate.value_or(0),
.finalSymbol = finalCharacter,
.minimumParameters = argc0,
.maximumParameters = argc1,
.conformanceLevel = VTType::VT100,
.extension = ext,
.mnemonic = mnemonic,
.comment = description };
}

constexpr auto DCS(std::optional<char> leader,
Expand All @@ -252,16 +281,16 @@ namespace detail // {{{
std::string_view description) noexcept
{
// TODO: static_assert on leader/intermediate range-or-null
return FunctionDefinition { FunctionCategory::DCS,
leader.value_or(0),
intermediate.value_or(0),
finalCharacter,
argc0,
argc1,
vt,
VTExtension::None,
mnemonic,
description };
return FunctionDefinition { .category = FunctionCategory::DCS,
.leader = leader.value_or(0),
.intermediate = intermediate.value_or(0),
.finalSymbol = finalCharacter,
.minimumParameters = argc0,
.maximumParameters = argc1,
.conformanceLevel = vt,
.extension = VTExtension::None,
.mnemonic = mnemonic,
.comment = description };
}

constexpr auto DCS(std::optional<char> leader,
Expand All @@ -274,16 +303,16 @@ namespace detail // {{{
std::string_view description) noexcept
{
// TODO: static_assert on leader/intermediate range-or-null
return FunctionDefinition { FunctionCategory::DCS,
leader.value_or(0),
intermediate.value_or(0),
finalCharacter,
argc0,
argc1,
VTType::VT100,
ext,
mnemonic,
description };
return FunctionDefinition { .category = FunctionCategory::DCS,
.leader = leader.value_or(0),
.intermediate = intermediate.value_or(0),
.finalSymbol = finalCharacter,
.minimumParameters = argc0,
.maximumParameters = argc1,
.conformanceLevel = VTType::VT100,
.extension = ext,
.mnemonic = mnemonic,
.comment = description };
}
} // namespace detail

Expand Down
10 changes: 10 additions & 0 deletions src/vtbackend/Grid.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ struct Margin
return from == rhs.from && to == rhs.to;
}
[[nodiscard]] constexpr bool operator!=(Horizontal rhs) const noexcept { return !(*this == rhs); }

[[nodiscard]] constexpr ColumnOffset clamp(ColumnOffset value) const noexcept
{
return std::clamp(value, from, to);
}
};

struct Vertical
Expand All @@ -76,6 +81,11 @@ struct Margin
{
return !(*this == rhs);
}

[[nodiscard]] constexpr LineOffset clamp(LineOffset value) const noexcept
{
return std::clamp(value, from, to);
}
};

Vertical vertical {}; // top-bottom
Expand Down
1 change: 1 addition & 0 deletions src/vtbackend/MockTerm.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class MockTerm: public Terminal::Events

void writeToScreen(std::string_view text)
{
ptyOutLog()("writeToScreen: {}", crispy::escape(text));
mockPty().appendStdOutBuffer(text);
while (mockPty().isStdoutDataAvailable())
terminal.processInputOnce();
Expand Down
67 changes: 27 additions & 40 deletions src/vtbackend/Screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1401,12 +1401,9 @@ CRISPY_REQUIRES(CellConcept<Cell>)
void Screen<Cell>::moveCursorUp(LineCount n)
{
_cursor.wrapPending = false;
auto const sanitizedN = min(n.as<LineOffset>(),
logicalCursorPosition().line > margin().vertical.from
? logicalCursorPosition().line - margin().vertical.from
: logicalCursorPosition().line);

_cursor.position.line -= sanitizedN;
_cursor.position.line = margin().vertical.contains(_cursor.position.line)
? margin().vertical.clamp(_cursor.position.line - n.as<LineOffset>())
: clampedLine(_cursor.position.line - n.as<LineOffset>());
updateCursorIterator();
}

Expand All @@ -1415,35 +1412,33 @@ CRISPY_REQUIRES(CellConcept<Cell>)
void Screen<Cell>::moveCursorDown(LineCount n)
{
_cursor.wrapPending = false;
auto const currentLineNumber = logicalCursorPosition().line;
auto const sanitizedN =
min(n.as<LineOffset>(),
currentLineNumber <= margin().vertical.to
? margin().vertical.to - currentLineNumber
: (boxed_cast<LineOffset>(_settings.pageSize.lines) - 1) - currentLineNumber);

_cursor.position.line += sanitizedN;
_cursor.position.line = margin().vertical.contains(_cursor.position.line)
? margin().vertical.clamp(_cursor.position.line + n.as<LineOffset>())
: clampedLine(_cursor.position.line + n.as<LineOffset>());
updateCursorIterator();
}

template <typename Cell>
CRISPY_REQUIRES(CellConcept<Cell>)
void Screen<Cell>::moveCursorForward(ColumnCount n)
{
if (margin().horizontal.contains(_cursor.position.column))
_cursor.position.column = margin().horizontal.clamp(_cursor.position.column + n.as<ColumnOffset>());
else
_cursor.position.column = clampedColumn(_cursor.position.column + boxed_cast<ColumnOffset>(n));
_cursor.wrapPending = false;
_cursor.position.column = min(_cursor.position.column + n.as<ColumnOffset>(), margin().horizontal.to);
}

template <typename Cell>
CRISPY_REQUIRES(CellConcept<Cell>)
void Screen<Cell>::moveCursorBackward(ColumnCount n)
{
// even if you move to 80th of 80 columns, it'll first write a char and THEN flag wrap pending
if (margin().horizontal.contains(_cursor.position.column))
_cursor.position.column = margin().horizontal.clamp(_cursor.position.column - n.as<ColumnOffset>());
else
_cursor.position.column = clampedColumn(_cursor.position.column + boxed_cast<ColumnOffset>(n));
_cursor.wrapPending = false;

// TODO: skip cells that in counting when iterating backwards over a wide cell (such as emoji)
auto const sanitizedN = min(n.as<ColumnOffset>(), _cursor.position.column);
setCurrentColumn(_cursor.position.column - sanitizedN);
}

template <typename Cell>
Expand Down Expand Up @@ -1774,9 +1769,9 @@ void Screen<Cell>::screenAlignmentPattern()
{
// sets the margins to the extremes of the page
margin().vertical.from = LineOffset(0);
margin().vertical.to = boxed_cast<LineOffset>(_settings.pageSize.lines) - LineOffset(1);
margin().vertical.to = boxed_cast<LineOffset>(pageSize().lines) - LineOffset(1);
margin().horizontal.from = ColumnOffset(0);
margin().horizontal.to = boxed_cast<ColumnOffset>(_settings.pageSize.columns) - ColumnOffset(1);
margin().horizontal.to = boxed_cast<ColumnOffset>(pageSize().columns) - ColumnOffset(1);

// and moves the cursor to the home position
moveCursorTo({}, {});
Expand Down Expand Up @@ -2234,7 +2229,7 @@ void Screen<Cell>::inspect(std::string const& message, std::ostream& os) const
{
auto const hline = [&]() {
for_each(crispy::times(*_settings.pageSize.columns), [&](auto) { os << '='; });
os << endl;
os << '\n';
};

auto const gridInfoLine = [&](Grid<Cell> const& grid) {
Expand All @@ -2250,7 +2245,7 @@ void Screen<Cell>::inspect(std::string const& message, std::ostream& os) const
if (!message.empty())
{
hline();
os << "\033[1;37;41m" << message << "\033[m" << endl;
os << "\033[1;37;41m" << message << "\033[m" << '\n';
hline();
}

Expand Down Expand Up @@ -3234,34 +3229,26 @@ void Screen<Cell>::executeControlCode(char controlCode)
{
case 0x00: // NUL
break;
case 0x07: // BEL
_terminal.bell();
break;
case 0x08: // BS
backspace();
break;
case 0x09: // TAB
moveCursorToNextTab();
break;
case 0x0A: // LF
linefeed();
break;
case 0x0B: // VT
case BEL.finalSymbol: _terminal.bell(); break;
case BS.finalSymbol: backspace(); break;
case TAB.finalSymbol: moveCursorToNextTab(); break;
case LF.finalSymbol: linefeed(); break;
case VT.finalSymbol:
// Even though VT means Vertical Tab, it seems that xterm is doing an IND instead.
[[fallthrough]];
case 0x0C: // FF
case FF.finalSymbol:
// Even though FF means Form Feed, it seems that xterm is doing an IND instead.
index();
break;
case LS1.finalSymbol: // LS1 (SO)
case LS1.finalSymbol: // (SO)
// Invokes G1 character set into GL. G1 is designated by a select-character-set (SCS) sequence.
_cursor.charsets.lockingShift(CharsetTable::G1);
break;
case LS0.finalSymbol: // LSO (SI)
case LS0.finalSymbol: // (SI)
// Invoke G0 character set into GL. G0 is designated by a select-character-set sequence (SCS).
_cursor.charsets.lockingShift(CharsetTable::G0);
break;
case 0x0D: moveCursorToBeginOfLine(); break;
case CR.finalSymbol: moveCursorToBeginOfLine(); break;
case 0x37: saveCursor(); break;
case 0x38: restoreCursor(); break;
default:
Expand Down
13 changes: 6 additions & 7 deletions src/vtbackend/Screen.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,9 @@
#include <fmt/format.h>

#include <algorithm>
#include <bitset>
#include <deque>
#include <functional>
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <string_view>
#include <vector>
Expand Down Expand Up @@ -394,6 +387,12 @@ class Screen final: public ScreenBase, public capabilities::StaticDatabase
return { clampedLine(coord.line), clampedColumn(coord.column) };
}

[[nodiscard]] CellLocation clampToMargin(CellLocation pos) const noexcept
{
return { std::clamp(pos.line, margin().vertical.from, margin().vertical.to),
std::clamp(pos.column, margin().horizontal.from, margin().horizontal.to) };
}

// Tests if given coordinate is within the visible screen area.
[[nodiscard]] bool contains(CellLocation coord) const noexcept override
{
Expand Down
Loading

0 comments on commit 4189e91

Please sign in to comment.