mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-23 04:06:13 +03:00
termio: coalesce resize events
On macOS, we were seeing resize events dropped by child processes if too many SIGWNCH events were generated.
This commit is contained in:
@ -18,6 +18,15 @@ const log = std.log.scoped(.io_thread);
|
|||||||
/// the future if we want it configurable.
|
/// the future if we want it configurable.
|
||||||
pub const Mailbox = BlockingQueue(termio.Message, 64);
|
pub const Mailbox = BlockingQueue(termio.Message, 64);
|
||||||
|
|
||||||
|
/// This stores the information that is coalesced.
|
||||||
|
const Coalesce = struct {
|
||||||
|
/// The number of milliseconds to coalesce certain messages like resize for.
|
||||||
|
/// Not all message types are coalesced.
|
||||||
|
const min_ms = 25;
|
||||||
|
|
||||||
|
resize: ?termio.Message.Resize = null,
|
||||||
|
};
|
||||||
|
|
||||||
/// Allocator used for some state
|
/// Allocator used for some state
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
|
|
||||||
@ -34,6 +43,12 @@ wakeup_c: xev.Completion = .{},
|
|||||||
stop: xev.Async,
|
stop: xev.Async,
|
||||||
stop_c: xev.Completion = .{},
|
stop_c: xev.Completion = .{},
|
||||||
|
|
||||||
|
/// This is used to coalesce resize events.
|
||||||
|
coalesce: xev.Timer,
|
||||||
|
coalesce_c: xev.Completion = .{},
|
||||||
|
coalesce_cancel_c: xev.Completion = .{},
|
||||||
|
coalesce_data: Coalesce = .{},
|
||||||
|
|
||||||
/// The underlying IO implementation.
|
/// The underlying IO implementation.
|
||||||
impl: *termio.Impl,
|
impl: *termio.Impl,
|
||||||
|
|
||||||
@ -60,6 +75,10 @@ pub fn init(
|
|||||||
var stop_h = try xev.Async.init();
|
var stop_h = try xev.Async.init();
|
||||||
errdefer stop_h.deinit();
|
errdefer stop_h.deinit();
|
||||||
|
|
||||||
|
// This timer is used to coalesce resize events.
|
||||||
|
var coalesce_h = try xev.Timer.init();
|
||||||
|
errdefer coalesce_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);
|
||||||
@ -69,6 +88,7 @@ pub fn init(
|
|||||||
.loop = loop,
|
.loop = loop,
|
||||||
.wakeup = wakeup_h,
|
.wakeup = wakeup_h,
|
||||||
.stop = stop_h,
|
.stop = stop_h,
|
||||||
|
.coalesce = coalesce_h,
|
||||||
.impl = impl,
|
.impl = impl,
|
||||||
.mailbox = mailbox,
|
.mailbox = mailbox,
|
||||||
};
|
};
|
||||||
@ -77,6 +97,7 @@ pub fn init(
|
|||||||
/// Clean up the thread. This is only safe to call once the thread
|
/// Clean up the thread. This is only safe to call once the thread
|
||||||
/// 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.stop.deinit();
|
self.stop.deinit();
|
||||||
self.wakeup.deinit();
|
self.wakeup.deinit();
|
||||||
self.loop.deinit();
|
self.loop.deinit();
|
||||||
@ -129,7 +150,7 @@ fn drainMailbox(self: *Thread) !void {
|
|||||||
|
|
||||||
log.debug("mailbox message={}", .{message});
|
log.debug("mailbox message={}", .{message});
|
||||||
switch (message) {
|
switch (message) {
|
||||||
.resize => |v| try self.impl.resize(v.grid_size, v.screen_size, v.padding),
|
.resize => |v| self.handleResize(v),
|
||||||
.clear_screen => |v| try self.impl.clearScreen(v.history),
|
.clear_screen => |v| try self.impl.clearScreen(v.history),
|
||||||
.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),
|
||||||
@ -147,6 +168,51 @@ fn drainMailbox(self: *Thread) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handleResize(self: *Thread, resize: termio.Message.Resize) void {
|
||||||
|
self.coalesce_data.resize = resize;
|
||||||
|
|
||||||
|
// If the timer is already active we just return. In the future we want
|
||||||
|
// to reset the timer up to a maximum wait time but for now this ensures
|
||||||
|
// relatively smooth resizing.
|
||||||
|
if (self.coalesce_c.state() == .active) return;
|
||||||
|
|
||||||
|
self.coalesce.reset(
|
||||||
|
&self.loop,
|
||||||
|
&self.coalesce_c,
|
||||||
|
&self.coalesce_cancel_c,
|
||||||
|
Coalesce.min_ms,
|
||||||
|
Thread,
|
||||||
|
self,
|
||||||
|
coalesceCallback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coalesceCallback(
|
||||||
|
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 coalesce callback err={}", .{err});
|
||||||
|
return .disarm;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const self = self_ orelse return .disarm;
|
||||||
|
|
||||||
|
if (self.coalesce_data.resize) |v| {
|
||||||
|
self.coalesce_data.resize = null;
|
||||||
|
self.impl.resize(v.grid_size, v.screen_size, v.padding) catch |err| {
|
||||||
|
log.warn("error during resize err={}", .{err});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return .disarm;
|
||||||
|
}
|
||||||
|
|
||||||
fn wakeupCallback(
|
fn wakeupCallback(
|
||||||
self_: ?*Thread,
|
self_: ?*Thread,
|
||||||
_: *xev.Loop,
|
_: *xev.Loop,
|
||||||
|
@ -15,8 +15,7 @@ pub const Message = union(enum) {
|
|||||||
/// in the future.
|
/// in the future.
|
||||||
pub const WriteReq = MessageData(u8, 38);
|
pub const WriteReq = MessageData(u8, 38);
|
||||||
|
|
||||||
/// Resize the window.
|
pub const Resize = struct {
|
||||||
resize: struct {
|
|
||||||
/// The grid size for the given screen size with padding applied.
|
/// The grid size for the given screen size with padding applied.
|
||||||
grid_size: renderer.GridSize,
|
grid_size: renderer.GridSize,
|
||||||
|
|
||||||
@ -27,7 +26,10 @@ pub const Message = union(enum) {
|
|||||||
/// The padding, so that the terminal implementation can subtract
|
/// The padding, so that the terminal implementation can subtract
|
||||||
/// this to send to the pty.
|
/// this to send to the pty.
|
||||||
padding: renderer.Padding,
|
padding: renderer.Padding,
|
||||||
},
|
};
|
||||||
|
|
||||||
|
/// Resize the window.
|
||||||
|
resize: Resize,
|
||||||
|
|
||||||
/// Clear the screen.
|
/// Clear the screen.
|
||||||
clear_screen: struct {
|
clear_screen: struct {
|
||||||
|
Reference in New Issue
Block a user