terminal: Avoid doing too many redraws + support non-canonical mode
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
apio 2023-09-20 22:01:26 +02:00
parent 9636b5d8da
commit b4a9ea3857
Signed by: apio
GPG Key ID: B8A7D06E42258954
2 changed files with 58 additions and 33 deletions

View File

@ -90,17 +90,23 @@ Result<ui::EventResult> TerminalWidget::handle_key_event(const ui::KeyEventReque
{ {
// Avoid handling "key released" events // Avoid handling "key released" events
if (!request.pressed) return ui::EventResult::DidNotHandle; if (!request.pressed) return ui::EventResult::DidNotHandle;
// Non-printable key or key that has no special character (unlike Tab or Enter). We exit early to avoid inserting an // Non-printable key or key that has no special character (unlike Tab or Enter). We exit early to avoid inserting an
// invalid zero byte into the terminal input (this would also happen on Shift or Ctrl keypresses). // invalid zero byte into the terminal input (this would also happen on Shift or Ctrl keypresses).
if (request.letter == '\0') return ui::EventResult::DidNotHandle; if (request.letter == '\0') return ui::EventResult::DidNotHandle;
return handle_input(request.letter);
}
Result<ui::EventResult> TerminalWidget::handle_input(char key)
{
query_termios(); query_termios();
bool is_special_character { false }; bool is_special_character { false };
if (/*is_canonical()*/ true) if (is_canonical())
{ {
if (request.letter == m_settings.c_cc[VERASE]) if (key == m_settings.c_cc[VERASE])
{ {
auto maybe_char = m_line_buffer.try_pop(); auto maybe_char = m_line_buffer.try_pop();
if (maybe_char.has_value() && maybe_char.value()) if (maybe_char.has_value() && maybe_char.value())
@ -114,17 +120,17 @@ Result<ui::EventResult> TerminalWidget::handle_key_event(const ui::KeyEventReque
put_code_point(L'\b'); put_code_point(L'\b');
put_code_point(L'\b'); put_code_point(L'\b');
} }
return ui::EventResult::DidHandle;
} }
ui::App::the().main_window()->draw(); return ui::EventResult::DidNotHandle;
return ui::EventResult::DidHandle;
} }
if ((m_settings.c_lflag & ECHOE)) return ui::EventResult::DidHandle; if ((m_settings.c_lflag & ECHOE)) return ui::EventResult::DidNotHandle;
else else
is_special_character = true; is_special_character = true;
} }
if (request.letter == m_settings.c_cc[VEOF]) if (key == m_settings.c_cc[VEOF])
{ {
write(m_pty, m_line_buffer.data(), m_line_buffer.size()); write(m_pty, m_line_buffer.data(), m_line_buffer.size());
m_line_buffer.clear(); m_line_buffer.clear();
@ -135,7 +141,7 @@ Result<ui::EventResult> TerminalWidget::handle_key_event(const ui::KeyEventReque
if (m_settings.c_lflag & ISIG) if (m_settings.c_lflag & ISIG)
{ {
if (request.letter == m_settings.c_cc[VINTR]) if (key == m_settings.c_cc[VINTR])
{ {
if (!(m_settings.c_lflag & NOFLSH)) m_line_buffer.clear(); if (!(m_settings.c_lflag & NOFLSH)) m_line_buffer.clear();
@ -145,7 +151,7 @@ Result<ui::EventResult> TerminalWidget::handle_key_event(const ui::KeyEventReque
is_special_character = true; is_special_character = true;
} }
if (request.letter == m_settings.c_cc[VQUIT]) if (key == m_settings.c_cc[VQUIT])
{ {
if (!(m_settings.c_lflag & NOFLSH)) m_line_buffer.clear(); if (!(m_settings.c_lflag & NOFLSH)) m_line_buffer.clear();
@ -159,48 +165,44 @@ Result<ui::EventResult> TerminalWidget::handle_key_event(const ui::KeyEventReque
if (!is_special_character) if (!is_special_character)
{ {
if (/*is_canonical()*/ true) if (is_canonical())
{ {
m_line_buffer.try_append((u8)request.letter); m_line_buffer.try_append((u8)key);
if (request.letter == '\n') if (key == '\n')
{ {
write(m_pty, m_line_buffer.data(), m_line_buffer.size()); write(m_pty, m_line_buffer.data(), m_line_buffer.size());
m_line_buffer.clear(); m_line_buffer.clear();
} }
} }
else else
write(m_pty, &request.letter, 1); write(m_pty, &key, 1);
} }
if (!(m_settings.c_lflag & ECHO)) return ui::EventResult::DidHandle; if (!(m_settings.c_lflag & ECHO)) return ui::EventResult::DidNotHandle;
if (_iscntrl(request.letter)) if (_iscntrl(key))
{ {
if (m_settings.c_lflag & ECHOCTL) if (m_settings.c_lflag & ECHOCTL)
{ {
bool should_echo = true; bool should_echo = true;
if (request.letter == '\n' || request.letter == '\t' || request.letter == '\0' || if (key == '\n' || key == '\t' || key == '\0' || key == m_settings.c_cc[VEOF]) should_echo = false;
request.letter == m_settings.c_cc[VEOF])
should_echo = false;
if (should_echo) if (should_echo)
{ {
char caret_notation[3] = { '^', '\0', '\0' }; char caret_notation[3] = { '^', '\0', '\0' };
if (request.letter == 0x7f) caret_notation[1] = '?'; if (key == 0x7f) caret_notation[1] = '?';
else else
caret_notation[1] = request.letter + 0x40; caret_notation[1] = key + 0x40;
for (int i = 0; i < 2; i++) { putchar(caret_notation[i]); } for (int i = 0; i < 2; i++) { putchar(caret_notation[i]); }
} }
else else
putchar(request.letter); putchar(key);
} }
} }
else else
putchar(request.letter); putchar(key);
ui::App::the().main_window()->draw();
return ui::EventResult::DidHandle; return ui::EventResult::DidHandle;
} }
@ -225,9 +227,15 @@ Result<bool> TerminalWidget::process()
bool should_update_cursor = tick_cursor(); bool should_update_cursor = tick_cursor();
for (ssize_t i = 0; i < nread; i++) TRY(putchar(buffer[i])); ssize_t drawn = 0;
if (should_update_cursor || nread > 0) ui::App::the().main_window()->draw(); for (ssize_t i = 0; i < nread; i++)
{
bool did_draw = TRY(putchar(buffer[i]));
if (did_draw) drawn++;
}
if (should_update_cursor || drawn > 0) ui::App::the().main_window()->draw();
return nread == 0; return nread == 0;
} }
@ -260,6 +268,11 @@ void TerminalWidget::query_termios()
tcgetattr(m_pty, &m_settings); tcgetattr(m_pty, &m_settings);
} }
bool TerminalWidget::is_canonical()
{
return m_settings.c_lflag & ICANON;
}
void TerminalWidget::draw_glyph(wchar_t c, int x, int y) void TerminalWidget::draw_glyph(wchar_t c, int x, int y)
{ {
auto subcanvas = m_terminal_canvas.subcanvas({ x, y, m_font->width(), m_font->height() }); auto subcanvas = m_terminal_canvas.subcanvas({ x, y, m_font->width(), m_font->height() });
@ -516,26 +529,28 @@ bool TerminalWidget::handle_escape_sequence(wchar_t c)
return true; return true;
} }
Result<void> TerminalWidget::putchar(char c) Result<bool> TerminalWidget::putchar(char c)
{ {
auto guard = make_scope_guard([this] { m_decoder.reset(); }); auto guard = make_scope_guard([this] { m_decoder.reset(); });
bool is_ready = TRY(m_decoder.feed(c)); bool is_ready = TRY(m_decoder.feed(c));
if (is_ready) put_code_point(TRY(m_decoder.extract())); bool result = false;
if (is_ready) result = put_code_point(TRY(m_decoder.extract()));
guard.deactivate(); guard.deactivate();
return {}; return result;
} }
void TerminalWidget::put_code_point(wchar_t c) bool TerminalWidget::put_code_point(wchar_t c)
{ {
if (c > (wchar_t)255) c = (wchar_t)256; if (c > (wchar_t)255) c = (wchar_t)256;
if (m_escape_parser.has_value()) if (m_escape_parser.has_value())
{ {
if (handle_escape_sequence(c)) return; if (handle_escape_sequence(c)) return false;
} }
// Erase the current cursor. // Erase the current cursor.
@ -543,6 +558,8 @@ void TerminalWidget::put_code_point(wchar_t c)
bool should_draw_cursor = m_cursor_enabled; bool should_draw_cursor = m_cursor_enabled;
bool did_draw = false;
switch (c) switch (c)
{ {
case L'\n': { case L'\n': {
@ -552,6 +569,7 @@ void TerminalWidget::put_code_point(wchar_t c)
} }
case L'\t': { case L'\t': {
for (int i = 0; i < 4; i++) { put_code_point(L' '); } for (int i = 0; i < 4; i++) { put_code_point(L' '); }
did_draw = true;
break; break;
} }
case L'\r': m_x_position = 0; break; case L'\r': m_x_position = 0; break;
@ -560,6 +578,7 @@ void TerminalWidget::put_code_point(wchar_t c)
{ {
prev_char(); prev_char();
erase_current_char(); erase_current_char();
did_draw = true;
} }
break; break;
case L'\x1b': case L'\x1b':
@ -568,9 +587,10 @@ void TerminalWidget::put_code_point(wchar_t c)
case L'\x9d': case L'\x9d':
m_escape_parser = EscapeSequenceParser { (u8)c }; m_escape_parser = EscapeSequenceParser { (u8)c };
should_draw_cursor = false; should_draw_cursor = false;
did_draw = true;
break; break;
default: { default: {
if (iscntrl(c)) return; if (iscntrl(c)) return false;
draw_glyph(c, m_x_position, m_y_position); draw_glyph(c, m_x_position, m_y_position);
next_char(); next_char();
if (at_end_of_screen()) if (at_end_of_screen())
@ -588,6 +608,8 @@ void TerminalWidget::put_code_point(wchar_t c)
m_cursor_activated = true; m_cursor_activated = true;
draw_cursor(); draw_cursor();
} }
return did_draw;
} }
void TerminalWidget::quit() void TerminalWidget::quit()

View File

@ -54,6 +54,9 @@ class TerminalWidget : public ui::Widget
Utf8StateDecoder m_decoder; Utf8StateDecoder m_decoder;
Option<EscapeSequenceParser> m_escape_parser; Option<EscapeSequenceParser> m_escape_parser;
Result<ui::EventResult> handle_input(char key);
bool is_canonical();
void draw_glyph(wchar_t c, int x, int y); void draw_glyph(wchar_t c, int x, int y);
void erase_current_line(); void erase_current_line();
void scroll(); void scroll();
@ -65,6 +68,6 @@ class TerminalWidget : public ui::Widget
void draw_cursor(); void draw_cursor();
bool at_end_of_screen(); bool at_end_of_screen();
bool handle_escape_sequence(wchar_t c); bool handle_escape_sequence(wchar_t c);
Result<void> putchar(char c); Result<bool> putchar(char c);
void put_code_point(wchar_t c); bool put_code_point(wchar_t c);
}; };