terminal: Avoid doing too many redraws + support non-canonical mode
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
9636b5d8da
commit
b4a9ea3857
@ -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()
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user