mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00
termio: handle all the synchronized output setting, timer
This commit is contained in:
@ -170,6 +170,7 @@ const entries: []const ModeEntry = &.{
|
|||||||
.{ .name = "alt_sends_escape", .value = 1039 },
|
.{ .name = "alt_sends_escape", .value = 1039 },
|
||||||
.{ .name = "alt_screen_save_cursor_clear_enter", .value = 1049 },
|
.{ .name = "alt_screen_save_cursor_clear_enter", .value = 1049 },
|
||||||
.{ .name = "bracketed_paste", .value = 2004 },
|
.{ .name = "bracketed_paste", .value = 2004 },
|
||||||
|
.{ .name = "synchronized_output", .value = 2026 },
|
||||||
};
|
};
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
@ -296,6 +296,15 @@ pub fn resize(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reset the synchronized output mode. This is usually called by timer
|
||||||
|
/// expiration from the termio thread.
|
||||||
|
pub fn resetSynchronizedOutput(self: *Exec) void {
|
||||||
|
self.renderer_state.mutex.lock();
|
||||||
|
defer self.renderer_state.mutex.unlock();
|
||||||
|
self.terminal.modes.set(.synchronized_output, false);
|
||||||
|
self.renderer_wakeup.notify() catch {};
|
||||||
|
}
|
||||||
|
|
||||||
/// Clear the screen.
|
/// Clear the screen.
|
||||||
pub fn clearScreen(self: *Exec, history: bool) !void {
|
pub fn clearScreen(self: *Exec, history: bool) !void {
|
||||||
{
|
{
|
||||||
@ -1350,6 +1359,13 @@ const StreamHandler = struct {
|
|||||||
if (enabled) .@"132_cols" else .@"80_cols",
|
if (enabled) .@"132_cols" else .@"80_cols",
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// We need to start a timer to prevent the emulator being hung
|
||||||
|
// forever.
|
||||||
|
.synchronized_output => {
|
||||||
|
if (enabled) self.messageWriter(.{ .start_synchronized_output = {} });
|
||||||
|
try self.queueRender();
|
||||||
|
},
|
||||||
|
|
||||||
.mouse_event_x10 => self.terminal.flags.mouse_event = if (enabled) .x10 else .none,
|
.mouse_event_x10 => self.terminal.flags.mouse_event = if (enabled) .x10 else .none,
|
||||||
.mouse_event_normal => self.terminal.flags.mouse_event = if (enabled) .normal else .none,
|
.mouse_event_normal => self.terminal.flags.mouse_event = if (enabled) .normal else .none,
|
||||||
.mouse_event_button => self.terminal.flags.mouse_event = if (enabled) .button else .none,
|
.mouse_event_button => self.terminal.flags.mouse_event = if (enabled) .button else .none,
|
||||||
|
@ -27,6 +27,10 @@ const Coalesce = struct {
|
|||||||
resize: ?termio.Message.Resize = null,
|
resize: ?termio.Message.Resize = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The number of milliseconds before we reset the synchronized output flag
|
||||||
|
/// if the running program hasn't already.
|
||||||
|
const sync_reset_ms = 5000;
|
||||||
|
|
||||||
/// Allocator used for some state
|
/// Allocator used for some state
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
|
|
||||||
@ -49,6 +53,12 @@ coalesce_c: xev.Completion = .{},
|
|||||||
coalesce_cancel_c: xev.Completion = .{},
|
coalesce_cancel_c: xev.Completion = .{},
|
||||||
coalesce_data: Coalesce = .{},
|
coalesce_data: Coalesce = .{},
|
||||||
|
|
||||||
|
/// This timer is used to reset synchronized output modes so that
|
||||||
|
/// the terminal doesn't freeze with a bad actor.
|
||||||
|
sync_reset: xev.Timer,
|
||||||
|
sync_reset_c: xev.Completion = .{},
|
||||||
|
sync_reset_cancel_c: xev.Completion = .{},
|
||||||
|
|
||||||
/// The underlying IO implementation.
|
/// The underlying IO implementation.
|
||||||
impl: *termio.Impl,
|
impl: *termio.Impl,
|
||||||
|
|
||||||
@ -79,6 +89,10 @@ pub fn init(
|
|||||||
var coalesce_h = try xev.Timer.init();
|
var coalesce_h = try xev.Timer.init();
|
||||||
errdefer coalesce_h.deinit();
|
errdefer coalesce_h.deinit();
|
||||||
|
|
||||||
|
// This timer is used to reset synchronized output modes.
|
||||||
|
var sync_reset_h = try xev.Timer.init();
|
||||||
|
errdefer sync_reset_h.deinit();
|
||||||
|
|
||||||
// The mailbox for messaging this thread
|
// The mailbox for messaging this thread
|
||||||
var mailbox = try Mailbox.create(alloc);
|
var mailbox = try Mailbox.create(alloc);
|
||||||
errdefer mailbox.destroy(alloc);
|
errdefer mailbox.destroy(alloc);
|
||||||
@ -89,6 +103,7 @@ pub fn init(
|
|||||||
.wakeup = wakeup_h,
|
.wakeup = wakeup_h,
|
||||||
.stop = stop_h,
|
.stop = stop_h,
|
||||||
.coalesce = coalesce_h,
|
.coalesce = coalesce_h,
|
||||||
|
.sync_reset = sync_reset_h,
|
||||||
.impl = impl,
|
.impl = impl,
|
||||||
.mailbox = mailbox,
|
.mailbox = mailbox,
|
||||||
};
|
};
|
||||||
@ -98,6 +113,7 @@ pub fn init(
|
|||||||
/// completes executing; the caller must join prior to this.
|
/// completes executing; the caller must join prior to this.
|
||||||
pub fn deinit(self: *Thread) void {
|
pub fn deinit(self: *Thread) void {
|
||||||
self.coalesce.deinit();
|
self.coalesce.deinit();
|
||||||
|
self.sync_reset.deinit();
|
||||||
self.stop.deinit();
|
self.stop.deinit();
|
||||||
self.wakeup.deinit();
|
self.wakeup.deinit();
|
||||||
self.loop.deinit();
|
self.loop.deinit();
|
||||||
@ -158,6 +174,7 @@ fn drainMailbox(self: *Thread) !void {
|
|||||||
.clear_screen => |v| try self.impl.clearScreen(v.history),
|
.clear_screen => |v| try self.impl.clearScreen(v.history),
|
||||||
.scroll_viewport => |v| try self.impl.scrollViewport(v),
|
.scroll_viewport => |v| try self.impl.scrollViewport(v),
|
||||||
.jump_to_prompt => |v| try self.impl.jumpToPrompt(v),
|
.jump_to_prompt => |v| try self.impl.jumpToPrompt(v),
|
||||||
|
.start_synchronized_output => self.startSynchronizedOutput(),
|
||||||
.write_small => |v| try self.impl.queueWrite(v.data[0..v.len]),
|
.write_small => |v| try self.impl.queueWrite(v.data[0..v.len]),
|
||||||
.write_stable => |v| try self.impl.queueWrite(v),
|
.write_stable => |v| try self.impl.queueWrite(v),
|
||||||
.write_alloc => |v| {
|
.write_alloc => |v| {
|
||||||
@ -174,6 +191,18 @@ fn drainMailbox(self: *Thread) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn startSynchronizedOutput(self: *Thread) void {
|
||||||
|
self.sync_reset.reset(
|
||||||
|
&self.loop,
|
||||||
|
&self.sync_reset_c,
|
||||||
|
&self.sync_reset_cancel_c,
|
||||||
|
sync_reset_ms,
|
||||||
|
Thread,
|
||||||
|
self,
|
||||||
|
syncResetCallback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn handleResize(self: *Thread, resize: termio.Message.Resize) void {
|
fn handleResize(self: *Thread, resize: termio.Message.Resize) void {
|
||||||
self.coalesce_data.resize = resize;
|
self.coalesce_data.resize = resize;
|
||||||
|
|
||||||
@ -193,6 +222,25 @@ fn handleResize(self: *Thread, resize: termio.Message.Resize) void {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn syncResetCallback(
|
||||||
|
self_: ?*Thread,
|
||||||
|
_: *xev.Loop,
|
||||||
|
_: *xev.Completion,
|
||||||
|
r: xev.Timer.RunError!void,
|
||||||
|
) xev.CallbackAction {
|
||||||
|
_ = r catch |err| switch (err) {
|
||||||
|
error.Canceled => {},
|
||||||
|
else => {
|
||||||
|
log.warn("error during sync reset callback err={}", .{err});
|
||||||
|
return .disarm;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const self = self_ orelse return .disarm;
|
||||||
|
self.impl.resetSynchronizedOutput();
|
||||||
|
return .disarm;
|
||||||
|
}
|
||||||
|
|
||||||
fn coalesceCallback(
|
fn coalesceCallback(
|
||||||
self_: ?*Thread,
|
self_: ?*Thread,
|
||||||
_: *xev.Loop,
|
_: *xev.Loop,
|
||||||
|
@ -51,6 +51,11 @@ pub const Message = union(enum) {
|
|||||||
/// Jump forward/backward n prompts.
|
/// Jump forward/backward n prompts.
|
||||||
jump_to_prompt: isize,
|
jump_to_prompt: isize,
|
||||||
|
|
||||||
|
/// Send this when a synchronized output mode is started. This will
|
||||||
|
/// start the timer so that the output mode is disabled after a
|
||||||
|
/// period of time so that a bad actor can't hang the terminal.
|
||||||
|
start_synchronized_output: void,
|
||||||
|
|
||||||
/// Write where the data fits in the union.
|
/// Write where the data fits in the union.
|
||||||
write_small: WriteReq.Small,
|
write_small: WriteReq.Small,
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user