mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 00:07:36 +00:00
LibVT: Implement Bracketed Paste Mode
This mode allow us to escape any data that was not directly typed by the user. `vim` currently uses this. If we implement it in the shell, we could prevent newlines from being injected into the shell by pasting text or dragging files into it (see #7276).
This commit is contained in:
parent
ee24f2eb2d
commit
708f835477
4 changed files with 53 additions and 12 deletions
|
@ -105,6 +105,10 @@ void Terminal::alter_mode(bool should_set, Parameters params, Intermediates inte
|
||||||
m_client.set_cursor_style(None);
|
m_client.set_cursor_style(None);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 2004:
|
||||||
|
dbgln_if(TERMINAL_DEBUG, "Setting bracketed mode enabled={}", should_set);
|
||||||
|
m_needs_bracketed_paste = should_set;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
dbgln("Terminal::alter_mode: Unimplemented private mode {} (should_set={})", mode, should_set);
|
dbgln("Terminal::alter_mode: Unimplemented private mode {} (should_set={})", mode, should_set);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -157,6 +157,11 @@ public:
|
||||||
Attribute attribute_at(const Position&) const;
|
Attribute attribute_at(const Position&) const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool needs_bracketed_paste() const
|
||||||
|
{
|
||||||
|
return m_needs_bracketed_paste;
|
||||||
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// ^EscapeSequenceExecutor
|
// ^EscapeSequenceExecutor
|
||||||
virtual void emit_code_point(u32) override;
|
virtual void emit_code_point(u32) override;
|
||||||
|
@ -336,6 +341,8 @@ protected:
|
||||||
CursorStyle m_cursor_style { BlinkingBlock };
|
CursorStyle m_cursor_style { BlinkingBlock };
|
||||||
CursorStyle m_saved_cursor_style { BlinkingBlock };
|
CursorStyle m_saved_cursor_style { BlinkingBlock };
|
||||||
|
|
||||||
|
bool m_needs_bracketed_paste { false };
|
||||||
|
|
||||||
Attribute m_current_attribute;
|
Attribute m_current_attribute;
|
||||||
Attribute m_saved_attribute;
|
Attribute m_saved_attribute;
|
||||||
|
|
||||||
|
|
|
@ -748,17 +748,14 @@ void TerminalWidget::paste()
|
||||||
{
|
{
|
||||||
if (m_ptm_fd == -1)
|
if (m_ptm_fd == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto mime_type = GUI::Clipboard::the().mime_type();
|
auto mime_type = GUI::Clipboard::the().mime_type();
|
||||||
if (!mime_type.starts_with("text/"))
|
if (!mime_type.starts_with("text/"))
|
||||||
return;
|
return;
|
||||||
auto text = GUI::Clipboard::the().data();
|
auto text = GUI::Clipboard::the().data();
|
||||||
if (text.is_empty())
|
if (text.is_empty())
|
||||||
return;
|
return;
|
||||||
int nwritten = write(m_ptm_fd, text.data(), text.size());
|
send_non_user_input(text);
|
||||||
if (nwritten < 0) {
|
|
||||||
perror("write");
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerminalWidget::copy()
|
void TerminalWidget::copy()
|
||||||
|
@ -1091,20 +1088,21 @@ void TerminalWidget::drop_event(GUI::DropEvent& event)
|
||||||
if (event.mime_data().has_text()) {
|
if (event.mime_data().has_text()) {
|
||||||
event.accept();
|
event.accept();
|
||||||
auto text = event.mime_data().text();
|
auto text = event.mime_data().text();
|
||||||
write(m_ptm_fd, text.characters(), text.length());
|
send_non_user_input(text.bytes());
|
||||||
} else if (event.mime_data().has_urls()) {
|
} else if (event.mime_data().has_urls()) {
|
||||||
event.accept();
|
event.accept();
|
||||||
auto urls = event.mime_data().urls();
|
auto urls = event.mime_data().urls();
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto& url : event.mime_data().urls()) {
|
for (auto& url : event.mime_data().urls()) {
|
||||||
if (!first) {
|
if (!first)
|
||||||
write(m_ptm_fd, " ", 1);
|
send_non_user_input(" "sv.bytes());
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
if (url.protocol() == "file")
|
if (url.protocol() == "file")
|
||||||
write(m_ptm_fd, url.path().characters(), url.path().length());
|
send_non_user_input(url.path().bytes());
|
||||||
else
|
else
|
||||||
write(m_ptm_fd, url.to_string().characters(), url.to_string().length());
|
send_non_user_input(url.to_string().bytes());
|
||||||
|
|
||||||
|
first = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1155,4 +1153,34 @@ void TerminalWidget::set_font_and_resize_to_fit(const Gfx::Font& font)
|
||||||
set_font(font);
|
set_font(font);
|
||||||
resize(widget_size_for_font(font));
|
resize(widget_size_for_font(font));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used for sending data that was not directly typed by the user.
|
||||||
|
// This basically wraps the code that handles sending the escape sequence in bracketed paste mode.
|
||||||
|
void TerminalWidget::send_non_user_input(const ReadonlyBytes& bytes)
|
||||||
|
{
|
||||||
|
constexpr StringView leading_control_sequence = "\e[200~";
|
||||||
|
constexpr StringView trailing_control_sequence = "\e[201~";
|
||||||
|
|
||||||
|
int nwritten;
|
||||||
|
if (m_terminal.needs_bracketed_paste()) {
|
||||||
|
// We do not call write() separately for the control sequences and the data,
|
||||||
|
// because that would present a race condition where another process could inject data
|
||||||
|
// to prematurely terminate the escape. Could probably be solved by file locking.
|
||||||
|
Vector<u8> output;
|
||||||
|
output.ensure_capacity(leading_control_sequence.bytes().size() + bytes.size() + trailing_control_sequence.bytes().size());
|
||||||
|
|
||||||
|
// HACK: We don't have a `Vector<T>::unchecked_append(Span<T> const&)` yet :^(
|
||||||
|
output.append(leading_control_sequence.bytes().data(), leading_control_sequence.bytes().size());
|
||||||
|
output.append(bytes.data(), bytes.size());
|
||||||
|
output.append(trailing_control_sequence.bytes().data(), trailing_control_sequence.bytes().size());
|
||||||
|
nwritten = write(m_ptm_fd, output.data(), output.size());
|
||||||
|
} else {
|
||||||
|
nwritten = write(m_ptm_fd, bytes.data(), bytes.size());
|
||||||
|
}
|
||||||
|
if (nwritten < 0) {
|
||||||
|
perror("write");
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,6 +118,8 @@ private:
|
||||||
|
|
||||||
void set_logical_focus(bool);
|
void set_logical_focus(bool);
|
||||||
|
|
||||||
|
void send_non_user_input(const ReadonlyBytes&);
|
||||||
|
|
||||||
Gfx::IntRect glyph_rect(u16 row, u16 column);
|
Gfx::IntRect glyph_rect(u16 row, u16 column);
|
||||||
Gfx::IntRect row_rect(u16 row);
|
Gfx::IntRect row_rect(u16 row);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue