Actually, we'll manage selection and viewports on the windowing thread

This commit is contained in:
Mitchell Hashimoto
2022-11-04 22:32:06 -07:00
parent 989046a06c
commit 5cb6ebe34d
4 changed files with 43 additions and 50 deletions

View File

@ -810,17 +810,25 @@ fn charCallback(window: glfw.Window, codepoint: u21) void {
return;
}
// Anytime a char is created, we have to clear the selection if there is one.
_ = win.io_thread.mailbox.push(.{
.clear_selection = {},
}, .{ .forever = {} });
// Critical area
{
win.renderer_state.mutex.lock();
defer win.renderer_state.mutex.unlock();
// Scroll to the bottom
_ = win.io_thread.mailbox.push(.{
.scroll_viewport = .{ .bottom = {} },
}, .{ .forever = {} });
// Clear the selction if we have one.
if (win.terminal.selection != null) {
win.terminal.selection = null;
win.queueRender() catch |err|
log.err("error scheduling render in charCallback err={}", .{err});
}
// Write the char to the pty
// We want to scroll to the bottom
// TODO: detect if we're at the bottom to avoid the render call here.
win.terminal.scrollViewport(.{ .bottom = {} }) catch |err|
log.err("error scrolling viewport err={}", .{err});
}
// Ask our IO thread to write the data
var data: termio.message.IO.SmallWriteArray = undefined;
data[0] = @intCast(u8, codepoint);
_ = win.io_thread.mailbox.push(.{
@ -835,16 +843,8 @@ fn charCallback(window: glfw.Window, codepoint: u21) void {
// TODO: the stuff below goes away with IO thread
if (win.terminal.selection != null) {
win.terminal.selection = null;
win.queueRender() catch |err|
log.err("error scheduling render in charCallback err={}", .{err});
}
// We want to scroll to the bottom
// TODO: detect if we're at the bottom to avoid the render call here.
win.terminal.scrollViewport(.{ .bottom = {} }) catch |err|
log.err("error scrolling viewport err={}", .{err});
win.queueRender() catch |err|
log.err("error scheduling render in charCallback err={}", .{err});
@ -959,8 +959,13 @@ fn keyCallback(
},
.copy_to_clipboard => {
if (win.terminal.selection) |sel| {
var buf = win.terminal.screen.selectionString(win.alloc, sel) catch |err| {
// We can read from the renderer state without holding
// the lock because only we will write to this field.
if (win.renderer_state.terminal.selection) |sel| {
var buf = win.renderer_state.terminal.screen.selectionString(
win.alloc,
sel,
) catch |err| {
log.err("error reading selection string err={}", .{err});
return;
};
@ -1129,12 +1134,17 @@ fn scrollCallback(window: glfw.Window, xoff: f64, yoff: f64) void {
const sign: isize = if (yoff > 0) -1 else 1;
const delta: isize = sign * @max(@divFloor(win.grid_size.rows, 15), 1);
log.info("scroll: delta={}", .{delta});
win.terminal.scrollViewport(.{ .delta = delta }) catch |err|
log.err("error scrolling viewport err={}", .{err});
// Schedule render since scrolling usually does something.
// TODO(perf): we can only schedule render if we know scrolling
// did something
// Modify our viewport, this requires a lock since it affects rendering
{
win.renderer_state.mutex.lock();
defer win.renderer_state.mutex.unlock();
win.terminal.scrollViewport(.{ .delta = delta }) catch |err|
log.err("error scrolling viewport err={}", .{err});
}
// TODO: drop after IO thread
win.queueRender() catch unreachable;
}
@ -1389,6 +1399,9 @@ fn mouseButtonCallback(
// Selection is always cleared
if (win.terminal.selection != null) {
// We only need the lock to write since only we'll ever write.
win.renderer_state.mutex.lock();
defer win.renderer_state.mutex.unlock();
win.terminal.selection = null;
win.queueRender() catch |err|
log.err("error scheduling render in mouseButtinCallback err={}", .{err});
@ -1462,6 +1475,12 @@ fn cursorPosCallback(
// often.
// TODO: unit test this, this logic sucks
// At some point below we likely write selection state so we just grab
// a lock. This is not optimal efficiency but its not a common operation
// so its okay.
win.renderer_state.mutex.lock();
defer win.renderer_state.mutex.unlock();
// If we were selecting, and we switched directions, then we restart
// calculations because it forces us to reconsider if the first cell is
// selected.

View File

@ -199,24 +199,6 @@ pub fn resize(
}
}
pub fn clearSelection(self: *Exec) !void {
// We don't need a lock to read because nothing else can possibly write
// as we're looking at this.
if (self.terminal.selection != null) {
// We need to lock so we can write because other things might be reading.
self.renderer_state.mutex.lock();
defer self.renderer_state.mutex.unlock();
self.terminal.selection = null;
}
}
pub fn scrollViewport(self: *Exec, scroll: terminal.Terminal.ScrollViewport) !void {
self.renderer_state.mutex.lock();
defer self.renderer_state.mutex.unlock();
try self.terminal.scrollViewport(scroll);
}
pub inline fn queueWrite(self: *Exec, data: []const u8) !void {
try self.data.?.queueWrite(data);
}

View File

@ -162,8 +162,6 @@ fn drainMailbox(self: *Thread) !void {
log.debug("mailbox message={}", .{message});
switch (message) {
.resize => |v| try self.impl.resize(v.grid_size, v.screen_size),
.clear_selection => try self.impl.clearSelection(),
.scroll_viewport => |v| try self.impl.scrollViewport(v),
.small_write => |v| try self.impl.queueWrite(v.data[0..v.len]),
}
}

View File

@ -12,12 +12,6 @@ pub const IO = union(enum) {
screen_size: renderer.ScreenSize,
},
/// Clear the selection
clear_selection: void,
/// Scroll the viewport
scroll_viewport: terminal.Terminal.ScrollViewport,
/// Write where the data fits in the union.
small_write: struct {
data: [22]u8,