mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 05:44:58 +00:00
LibVT+Everywhere: Introduce 'automarks' and 'clear previous command'
Automarks are similar to bookmarks placed by the terminal, allowing the user to selectively remove a single command and its output from the terminal scrollback. This commit implements a single way to add marks: automatically placing them when the shell becomes interactive. To make sure the shell behaves correctly after its expected prompt position changes, the terminal layer forces a resize event to be passed to the shell on such (possibly) partial clears; this also has the nice side effect of fixing the disappearing prompt on the preexisting "clear including history" action: Fixes #4192.
This commit is contained in:
parent
cde528fdd9
commit
54ab6fe5b9
12 changed files with 238 additions and 0 deletions
|
@ -377,6 +377,11 @@ void VirtualConsole::terminal_history_changed(int)
|
|||
// Do nothing, I guess?
|
||||
}
|
||||
|
||||
void VirtualConsole::terminal_did_perform_possibly_partial_clear()
|
||||
{
|
||||
// Do nothing, we're not going to hit this anyway.
|
||||
}
|
||||
|
||||
void VirtualConsole::emit(u8 const* data, size_t size)
|
||||
{
|
||||
for (size_t i = 0; i < size; i++)
|
||||
|
|
|
@ -99,6 +99,7 @@ private:
|
|||
virtual void set_window_progress(int, int) override;
|
||||
virtual void terminal_did_resize(u16 columns, u16 rows) override;
|
||||
virtual void terminal_history_changed(int) override;
|
||||
virtual void terminal_did_perform_possibly_partial_clear() override;
|
||||
virtual void emit(u8 const*, size_t) override;
|
||||
virtual void set_cursor_shape(VT::CursorShape) override;
|
||||
virtual void set_cursor_blinking(bool) override;
|
||||
|
|
|
@ -284,6 +284,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
window->set_obey_widget_min_size(false);
|
||||
|
||||
auto terminal = window->set_main_widget<VT::TerminalWidget>(ptm_fd, true);
|
||||
terminal->set_startup_process_id(shell_pid);
|
||||
|
||||
terminal->on_command_exit = [&] {
|
||||
app->quit(0);
|
||||
};
|
||||
|
@ -309,6 +311,13 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
terminal->set_bell_mode(VT::TerminalWidget::BellMode::Visible);
|
||||
}
|
||||
|
||||
auto automark = Config::read_string("Terminal"sv, "Terminal"sv, "AutoMark"sv, "MarkInteractiveShellPrompt"sv);
|
||||
if (automark == "MarkNothing") {
|
||||
terminal->set_auto_mark_mode(VT::TerminalWidget::AutoMarkMode::MarkNothing);
|
||||
} else {
|
||||
terminal->set_auto_mark_mode(VT::TerminalWidget::AutoMarkMode::MarkInteractiveShellPrompt);
|
||||
}
|
||||
|
||||
auto cursor_shape = VT::TerminalWidget::parse_cursor_shape(Config::read_string("Terminal"sv, "Cursor"sv, "Shape"sv, "Block"sv)).value_or(VT::CursorShape::Block);
|
||||
terminal->set_cursor_shape(cursor_shape);
|
||||
|
||||
|
@ -399,6 +408,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
window->set_fullscreen(!window->is_fullscreen());
|
||||
}));
|
||||
view_menu->add_action(terminal->clear_including_history_action());
|
||||
view_menu->add_action(terminal->clear_to_previous_mark_action());
|
||||
|
||||
auto adjust_font_size = [&](float adjustment, Gfx::Font::AllowInexactSizeMatch preference) {
|
||||
auto& font = terminal->font();
|
||||
|
|
|
@ -40,10 +40,15 @@ ErrorOr<void> MainWidget::setup()
|
|||
auto& beep_bell_radio = *find_descendant_of_type_named<GUI::RadioButton>("beep_bell_radio");
|
||||
auto& visual_bell_radio = *find_descendant_of_type_named<GUI::RadioButton>("visual_bell_radio");
|
||||
auto& no_bell_radio = *find_descendant_of_type_named<GUI::RadioButton>("no_bell_radio");
|
||||
auto& automark_off_radio = *find_descendant_of_type_named<GUI::RadioButton>("automark_of");
|
||||
auto& automark_on_interactive_prompt_radio = *find_descendant_of_type_named<GUI::RadioButton>("automark_on_interactive_prompt");
|
||||
|
||||
m_bell_mode = parse_bell(Config::read_string("Terminal"sv, "Window"sv, "Bell"sv));
|
||||
m_original_bell_mode = m_bell_mode;
|
||||
|
||||
m_automark_mode = parse_automark_mode(Config::read_string("Terminal"sv, "Terminal"sv, "AutoMark"sv));
|
||||
m_original_automark_mode = m_automark_mode;
|
||||
|
||||
switch (m_bell_mode) {
|
||||
case VT::TerminalWidget::BellMode::Visible:
|
||||
visual_bell_radio.set_checked(true, GUI::AllowCallback::No);
|
||||
|
@ -72,6 +77,26 @@ ErrorOr<void> MainWidget::setup()
|
|||
set_modified(true);
|
||||
};
|
||||
|
||||
switch (m_automark_mode) {
|
||||
case VT::TerminalWidget::AutoMarkMode::MarkNothing:
|
||||
automark_off_radio.set_checked(true, GUI::AllowCallback::No);
|
||||
break;
|
||||
case VT::TerminalWidget::AutoMarkMode::MarkInteractiveShellPrompt:
|
||||
automark_on_interactive_prompt_radio.set_checked(true, GUI::AllowCallback::No);
|
||||
break;
|
||||
}
|
||||
|
||||
automark_off_radio.on_checked = [this](bool) {
|
||||
m_automark_mode = VT::TerminalWidget::AutoMarkMode::MarkNothing;
|
||||
Config::write_string("Terminal"sv, "Terminal"sv, "AutoMark"sv, stringify_automark_mode(m_automark_mode));
|
||||
set_modified(true);
|
||||
};
|
||||
automark_on_interactive_prompt_radio.on_checked = [this](bool) {
|
||||
m_automark_mode = VT::TerminalWidget::AutoMarkMode::MarkInteractiveShellPrompt;
|
||||
Config::write_string("Terminal"sv, "Terminal"sv, "AutoMark"sv, stringify_automark_mode(m_automark_mode));
|
||||
set_modified(true);
|
||||
};
|
||||
|
||||
m_confirm_close = Config::read_bool("Terminal"sv, "Terminal"sv, "ConfirmClose"sv, true);
|
||||
m_orignal_confirm_close = m_confirm_close;
|
||||
auto& confirm_close_checkbox = *find_descendant_of_type_named<GUI::CheckBox>("terminal_confirm_close");
|
||||
|
@ -106,6 +131,24 @@ ByteString MainWidget::stringify_bell(VT::TerminalWidget::BellMode bell_mode)
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
VT::TerminalWidget::AutoMarkMode MainWidget::parse_automark_mode(StringView automark_mode)
|
||||
{
|
||||
if (automark_mode == "MarkNothing")
|
||||
return VT::TerminalWidget::AutoMarkMode::MarkNothing;
|
||||
if (automark_mode == "MarkInteractiveShellPrompt")
|
||||
return VT::TerminalWidget::AutoMarkMode::MarkInteractiveShellPrompt;
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ByteString MainWidget::stringify_automark_mode(VT::TerminalWidget::AutoMarkMode automark_mode)
|
||||
{
|
||||
if (automark_mode == VT::TerminalWidget::AutoMarkMode::MarkNothing)
|
||||
return "MarkNothing";
|
||||
if (automark_mode == VT::TerminalWidget::AutoMarkMode::MarkInteractiveShellPrompt)
|
||||
return "MarkInteractiveShellPrompt";
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
void MainWidget::apply_settings()
|
||||
{
|
||||
m_original_bell_mode = m_bell_mode;
|
||||
|
|
|
@ -29,12 +29,16 @@ private:
|
|||
void write_back_settings() const;
|
||||
|
||||
static VT::TerminalWidget::BellMode parse_bell(StringView bell_string);
|
||||
static VT::TerminalWidget::AutoMarkMode parse_automark_mode(StringView automark_mode);
|
||||
static ByteString stringify_bell(VT::TerminalWidget::BellMode bell_mode);
|
||||
static ByteString stringify_automark_mode(VT::TerminalWidget::AutoMarkMode automark_mode);
|
||||
|
||||
VT::TerminalWidget::BellMode m_bell_mode { VT::TerminalWidget::BellMode::Disabled };
|
||||
VT::TerminalWidget::AutoMarkMode m_automark_mode { VT::TerminalWidget::AutoMarkMode::MarkInteractiveShellPrompt };
|
||||
bool m_confirm_close { true };
|
||||
|
||||
VT::TerminalWidget::BellMode m_original_bell_mode;
|
||||
VT::TerminalWidget::AutoMarkMode m_original_automark_mode;
|
||||
bool m_orignal_confirm_close { true };
|
||||
};
|
||||
}
|
||||
|
|
|
@ -48,4 +48,22 @@
|
|||
text: "Confirm exit when process is active"
|
||||
}
|
||||
}
|
||||
|
||||
@GUI::GroupBox {
|
||||
title: "Auto-mark behavior"
|
||||
preferred_height: "fit"
|
||||
layout: @GUI::VerticalBoxLayout {
|
||||
margins: [8]
|
||||
}
|
||||
|
||||
@GUI::RadioButton {
|
||||
name: "automark_off"
|
||||
text: "Do not auto-mark"
|
||||
}
|
||||
|
||||
@GUI::RadioButton {
|
||||
name: "automark_on_interactive_prompt"
|
||||
text: "Auto-mark on interactive shell prompts"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ ErrorOr<void> TerminalWrapper::run_command(ByteString const& command, Optional<B
|
|||
m_pid = TRY(Core::System::fork());
|
||||
|
||||
if (m_pid > 0) {
|
||||
m_terminal_widget->set_startup_process_id(m_pid);
|
||||
|
||||
if (wait_for_exit == WaitForExit::Yes) {
|
||||
GUI::Application::the()->event_loop().spin_until([this]() {
|
||||
return m_child_exited;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/AnyOf.h>
|
||||
#include <AK/DistinctNumeric.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibVT/Attribute.h>
|
||||
|
@ -16,6 +17,10 @@
|
|||
|
||||
namespace VT {
|
||||
|
||||
AK_TYPEDEF_DISTINCT_ORDERED_ID(u32, Mark);
|
||||
|
||||
inline static constexpr Mark Unmarked = 0;
|
||||
|
||||
class Line {
|
||||
AK_MAKE_NONCOPYABLE(Line);
|
||||
AK_MAKE_NONMOVABLE(Line);
|
||||
|
@ -40,6 +45,7 @@ public:
|
|||
void clear(Attribute const& attribute = Attribute())
|
||||
{
|
||||
m_terminated_at.clear();
|
||||
m_mark = Unmarked;
|
||||
clear_range(0, m_cells.size() - 1, attribute);
|
||||
}
|
||||
void clear_range(size_t first_column, size_t last_column, Attribute const& attribute = Attribute());
|
||||
|
@ -76,6 +82,16 @@ public:
|
|||
bool is_dirty() const { return m_dirty; }
|
||||
void set_dirty(bool b) { m_dirty = b; }
|
||||
|
||||
Optional<Mark> mark() const
|
||||
{
|
||||
return m_mark == Unmarked ? OptionalNone {} : Optional<Mark>(m_mark);
|
||||
}
|
||||
void set_marked(Mark mark)
|
||||
{
|
||||
set_dirty(m_mark != mark);
|
||||
m_mark = mark;
|
||||
}
|
||||
|
||||
Optional<u16> termination_column() const { return m_terminated_at; }
|
||||
void set_terminated(u16 column) { m_terminated_at = column; }
|
||||
|
||||
|
@ -84,6 +100,7 @@ private:
|
|||
void push_cells_into_next_line(size_t new_length, Line* next_line, bool cursor_is_on_next_line, CursorPosition* cursor);
|
||||
|
||||
Vector<Cell> m_cells;
|
||||
Mark m_mark { Unmarked };
|
||||
bool m_dirty { false };
|
||||
// Note: The alignment is 8, so this member lives in the padding (that already existed before it was introduced)
|
||||
[[no_unique_address]] Optional<u16> m_terminated_at;
|
||||
|
|
|
@ -35,6 +35,7 @@ void Terminal::clear()
|
|||
for (size_t i = 0; i < rows(); ++i)
|
||||
active_buffer()[i]->clear();
|
||||
set_cursor(0, 0);
|
||||
m_client.terminal_did_perform_possibly_partial_clear();
|
||||
}
|
||||
|
||||
void Terminal::clear_history()
|
||||
|
@ -45,6 +46,68 @@ void Terminal::clear_history()
|
|||
m_history_start = 0;
|
||||
m_client.terminal_history_changed(-previous_history_size);
|
||||
}
|
||||
|
||||
void Terminal::clear_to_mark(Mark mark)
|
||||
{
|
||||
auto cursor_row = this->cursor_row();
|
||||
ScopeGuard send_sigwinch = [&] {
|
||||
set_cursor(cursor_row, 1);
|
||||
mark_cursor();
|
||||
m_client.terminal_did_perform_possibly_partial_clear();
|
||||
};
|
||||
m_valid_marks.remove(mark);
|
||||
|
||||
{
|
||||
auto it = active_buffer().rbegin();
|
||||
size_t row = m_rows - 1;
|
||||
// Skip to the cursor line.
|
||||
for (size_t i = this->cursor_row() + 1; i < active_buffer().size(); ++i, row--)
|
||||
++it;
|
||||
for (; it != active_buffer().rend(); ++it, row--) {
|
||||
auto& line = *it;
|
||||
auto line_mark = line->mark();
|
||||
auto is_target_line = line_mark == mark;
|
||||
if (line_mark.has_value())
|
||||
m_valid_marks.remove(*line_mark);
|
||||
line->clear();
|
||||
if (is_target_line) {
|
||||
cursor_row = row;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the mark is not found, go through the history.
|
||||
auto it = AK::find_if(
|
||||
m_history.rbegin(),
|
||||
m_history.rend(),
|
||||
[mark](auto& line) {
|
||||
return line->mark() == mark;
|
||||
});
|
||||
auto index = it == m_history.rend() ? 0 : m_history.size() - it.index();
|
||||
m_client.terminal_history_changed(m_history.size() - index);
|
||||
auto count = m_history.size() - index;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
if (auto mark = m_history[index + i]->mark(); mark.has_value())
|
||||
m_valid_marks.remove(*mark);
|
||||
}
|
||||
m_history.remove(index, count);
|
||||
cursor_row = 0;
|
||||
}
|
||||
|
||||
void Terminal::mark_cursor()
|
||||
{
|
||||
static u32 next_mark_id { 0 };
|
||||
|
||||
auto& line = active_buffer()[cursor_row()];
|
||||
if (line->mark().has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto mark = Mark(next_mark_id++);
|
||||
line->set_marked(mark);
|
||||
m_valid_marks.set(mark);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Terminal::alter_ansi_mode(bool should_set, Parameters params)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#ifndef KERNEL
|
||||
# include <AK/ByteString.h>
|
||||
# include <AK/HashTable.h>
|
||||
# include <LibVT/Attribute.h>
|
||||
# include <LibVT/Line.h>
|
||||
#else
|
||||
|
@ -49,6 +50,7 @@ public:
|
|||
virtual void set_window_progress(int value, int max) = 0;
|
||||
virtual void terminal_did_resize(u16 columns, u16 rows) = 0;
|
||||
virtual void terminal_history_changed(int delta) = 0;
|
||||
virtual void terminal_did_perform_possibly_partial_clear() = 0;
|
||||
virtual void emit(u8 const*, size_t) = 0;
|
||||
virtual void set_cursor_shape(CursorShape) = 0;
|
||||
virtual void set_cursor_blinking(bool) = 0;
|
||||
|
@ -85,8 +87,12 @@ public:
|
|||
}
|
||||
|
||||
#ifndef KERNEL
|
||||
void mark_cursor();
|
||||
OrderedHashTable<Mark> const& marks() const { return m_valid_marks; }
|
||||
|
||||
void clear();
|
||||
void clear_history();
|
||||
void clear_to_mark(Mark);
|
||||
#else
|
||||
virtual void clear() = 0;
|
||||
virtual void clear_history() = 0;
|
||||
|
@ -438,6 +444,7 @@ protected:
|
|||
#ifndef KERNEL
|
||||
ByteString m_current_window_title;
|
||||
Vector<ByteString> m_title_stack;
|
||||
OrderedHashTable<Mark> m_valid_marks;
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL
|
||||
|
|
|
@ -69,8 +69,18 @@ void TerminalWidget::set_pty_master_fd(int fd)
|
|||
set_pty_master_fd(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
for (ssize_t i = 0; i < nread; ++i)
|
||||
m_terminal.on_input(buffer[i]);
|
||||
|
||||
auto owned_by_startup_process = m_startup_process_owns_pty;
|
||||
auto pgrp = tcgetpgrp(m_ptm_fd);
|
||||
m_startup_process_owns_pty = pgrp == m_startup_process_id;
|
||||
if (m_startup_process_owns_pty != owned_by_startup_process) {
|
||||
// pty owner state changed, handle it.
|
||||
handle_pty_owner_change(pgrp);
|
||||
}
|
||||
|
||||
flush_dirty_lines();
|
||||
};
|
||||
}
|
||||
|
@ -140,11 +150,16 @@ TerminalWidget::TerminalWidget(int ptm_fd, bool automatic_size_policy)
|
|||
clear_including_history();
|
||||
});
|
||||
|
||||
m_clear_to_previous_mark_action = GUI::Action::create("Clear &Previous Command", { Mod_Ctrl | Mod_Shift, Key_U }, [this](auto&) {
|
||||
clear_to_previous_mark();
|
||||
});
|
||||
|
||||
m_context_menu = GUI::Menu::construct();
|
||||
m_context_menu->add_action(copy_action());
|
||||
m_context_menu->add_action(paste_action());
|
||||
m_context_menu->add_separator();
|
||||
m_context_menu->add_action(clear_including_history_action());
|
||||
m_context_menu->add_action(clear_to_previous_mark_action());
|
||||
|
||||
update_copy_action();
|
||||
update_paste_action();
|
||||
|
@ -1035,6 +1050,22 @@ void TerminalWidget::terminal_history_changed(int delta)
|
|||
m_selection.offset_row(delta);
|
||||
}
|
||||
|
||||
void TerminalWidget::terminal_did_perform_possibly_partial_clear()
|
||||
{
|
||||
// Just pretend the whole terminal was cleared.
|
||||
// Force an update by resizing slightly and then back to the original size.
|
||||
winsize ws;
|
||||
for (ssize_t offset = 1; offset >= 0; --offset) {
|
||||
ws.ws_col = m_terminal.columns() - offset;
|
||||
ws.ws_row = m_terminal.rows() - offset;
|
||||
if (m_ptm_fd != -1) {
|
||||
if (ioctl(m_ptm_fd, TIOCSWINSZ, &ws) < 0) {
|
||||
perror("ioctl(TIOCSWINSZ)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalWidget::terminal_did_resize(u16 columns, u16 rows)
|
||||
{
|
||||
auto pixel_size = widget_size_for_font(font());
|
||||
|
@ -1218,6 +1249,15 @@ void TerminalWidget::clear_including_history()
|
|||
m_terminal.clear_including_history();
|
||||
}
|
||||
|
||||
void TerminalWidget::clear_to_previous_mark()
|
||||
{
|
||||
auto marks = m_terminal.marks().values();
|
||||
size_t offset = m_startup_process_owns_pty ? 2 : 1; // If the shell is the active process, we have an extra mark.
|
||||
if (marks.size() < offset)
|
||||
return;
|
||||
m_terminal.clear_to_mark(marks[marks.size() - offset]);
|
||||
}
|
||||
|
||||
void TerminalWidget::scroll_to_bottom()
|
||||
{
|
||||
m_scrollbar->set_value(m_scrollbar->max());
|
||||
|
@ -1375,4 +1415,11 @@ ByteString TerminalWidget::stringify_cursor_shape(VT::CursorShape cursor_shape)
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
void TerminalWidget::handle_pty_owner_change(pid_t new_owner)
|
||||
{
|
||||
if (m_auto_mark_mode == AutoMarkMode::MarkInteractiveShellPrompt && new_owner == m_startup_process_id) {
|
||||
m_terminal.mark_cursor();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -55,6 +55,14 @@ public:
|
|||
BellMode bell_mode() { return m_bell_mode; }
|
||||
void set_bell_mode(BellMode bm) { m_bell_mode = bm; }
|
||||
|
||||
enum class AutoMarkMode {
|
||||
MarkNothing,
|
||||
MarkInteractiveShellPrompt,
|
||||
};
|
||||
|
||||
AutoMarkMode auto_mark_mode() { return m_auto_mark_mode; }
|
||||
void set_auto_mark_mode(AutoMarkMode am) { m_auto_mark_mode = am; }
|
||||
|
||||
bool has_selection() const;
|
||||
bool selection_contains(const VT::Position&) const;
|
||||
ByteString selected_text() const;
|
||||
|
@ -77,10 +85,14 @@ public:
|
|||
GUI::Action& copy_action() { return *m_copy_action; }
|
||||
GUI::Action& paste_action() { return *m_paste_action; }
|
||||
GUI::Action& clear_including_history_action() { return *m_clear_including_history_action; }
|
||||
GUI::Action& clear_to_previous_mark_action() { return *m_clear_to_previous_mark_action; }
|
||||
|
||||
void copy();
|
||||
void paste();
|
||||
void clear_including_history();
|
||||
void clear_to_previous_mark();
|
||||
|
||||
void set_startup_process_id(pid_t pid) { m_startup_process_id = pid; }
|
||||
|
||||
const StringView color_scheme_name() const { return m_color_scheme_name; }
|
||||
|
||||
|
@ -133,6 +145,7 @@ private:
|
|||
virtual void set_window_progress(int value, int max) override;
|
||||
virtual void terminal_did_resize(u16 columns, u16 rows) override;
|
||||
virtual void terminal_history_changed(int delta) override;
|
||||
virtual void terminal_did_perform_possibly_partial_clear() override;
|
||||
virtual void emit(u8 const*, size_t) override;
|
||||
|
||||
// ^GUI::Clipboard::ClipboardClient
|
||||
|
@ -163,6 +176,8 @@ private:
|
|||
|
||||
void update_cached_font_metrics();
|
||||
|
||||
void handle_pty_owner_change(pid_t new_owner);
|
||||
|
||||
VT::Terminal m_terminal;
|
||||
|
||||
VT::Range m_selection;
|
||||
|
@ -183,6 +198,8 @@ private:
|
|||
|
||||
ByteString m_color_scheme_name;
|
||||
|
||||
AutoMarkMode m_auto_mark_mode { AutoMarkMode::MarkInteractiveShellPrompt };
|
||||
|
||||
BellMode m_bell_mode { BellMode::Visible };
|
||||
bool m_alt_key_held { false };
|
||||
bool m_rectangle_selection { false };
|
||||
|
@ -229,6 +246,7 @@ private:
|
|||
RefPtr<GUI::Action> m_copy_action;
|
||||
RefPtr<GUI::Action> m_paste_action;
|
||||
RefPtr<GUI::Action> m_clear_including_history_action;
|
||||
RefPtr<GUI::Action> m_clear_to_previous_mark_action;
|
||||
|
||||
RefPtr<GUI::Menu> m_context_menu;
|
||||
RefPtr<GUI::Menu> m_context_menu_for_hyperlink;
|
||||
|
@ -237,6 +255,9 @@ private:
|
|||
|
||||
Gfx::IntPoint m_left_mousedown_position;
|
||||
VT::Position m_left_mousedown_position_buffer;
|
||||
|
||||
bool m_startup_process_owns_pty { false };
|
||||
pid_t m_startup_process_id { -1 };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue