diff --git a/src/Window.zig b/src/Window.zig index b984b6886..a007e971e 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -250,6 +250,7 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo window.setKeyCallback(keyCallback); window.setFocusCallback(focusCallback); window.setRefreshCallback(refreshCallback); + window.setScrollCallback(scrollCallback); return self; } @@ -505,6 +506,24 @@ fn refreshCallback(window: glfw.Window) void { win.render_timer.schedule() catch unreachable; } +fn scrollCallback(window: glfw.Window, xoff: f64, yoff: f64) void { + const win = window.getUserPointer(Window) orelse return; + + //log.info("SCROLL: {} {}", .{ xoff, yoff }); + _ = xoff; + + // Positive is up + const sign: isize = if (yoff < 0) -1 else 1; + const delta: isize = sign * @maximum(@divFloor(win.grid.size.rows, 15), 1); + log.info("scroll: delta={}", .{delta}); + win.terminal.scrollViewport(.{ .delta = delta }); + + // Schedule render since scrolling usually does something. + // TODO(perf): we can only schedule render if we know scrolling + // did something + win.render_timer.schedule() catch unreachable; +} + fn cursorTimerCallback(t: *libuv.Timer) void { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/terminal/Screen.zig b/src/terminal/Screen.zig index f442fcb47..f30e824ce 100644 --- a/src/terminal/Screen.zig +++ b/src/terminal/Screen.zig @@ -187,12 +187,25 @@ pub fn scroll(self: *Screen, behavior: Scroll) void { .bottom => self.visible_offset = self.bottom - self.rows, // TODO: deltas greater than the entire scrollback - .delta => |delta| self.scrollDown(delta, true), - .delta_no_grow => |delta| self.scrollDown(delta, false), + .delta => |delta| self.scrollDelta(delta, true), + .delta_no_grow => |delta| self.scrollDelta(delta, false), } } -fn scrollDown(self: *Screen, delta: isize, grow: bool) void { +fn scrollDelta(self: *Screen, delta: isize, grow: bool) void { + log.info("offsets before: top={} bottom={} visible={}", .{ + self.top, + self.bottom, + self.visible_offset, + }); + defer { + log.info("offsets after: top={} bottom={} visible={}", .{ + self.top, + self.bottom, + self.visible_offset, + }); + } + // If we're scrolling up, then we just subtract and we're done. if (delta < 0) { self.visible_offset -|= @intCast(usize, -delta); @@ -255,22 +268,6 @@ fn scrollDown(self: *Screen, delta: isize, grow: bool) void { self.visible_offset -= rows_overlapped; } -/// Scroll the screen up (positive) or down (negative). Scrolling direction -/// is the direction text would move. For example, scrolling down would -/// move existing text downward. -pub fn scrollOld(self: *Screen, count: isize) void { - if (count < 0) { - const amount = @mod(@intCast(usize, -count), self.rows); - if (amount > self.top) { - self.top = self.rows - amount; - } else { - self.top -|= amount; - } - } else { - self.top = @mod(self.top + @intCast(usize, count), self.rows); - } -} - /// Copy row at src to dst. pub fn copyRow(self: *Screen, dst: usize, src: usize) void { const src_row = self.getRow(src); @@ -289,7 +286,7 @@ pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void { const old = self.*; // Reallocate the storage - self.storage = try alloc.alloc(Cell, rows * cols); + self.storage = try alloc.alloc(Cell, (rows + self.max_scrollback) * cols); self.top = 0; self.bottom = rows - 1; self.rows = rows; diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index c2aeef4bd..d92a00abe 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -73,7 +73,8 @@ pub fn init(alloc: Allocator, cols: usize, rows: usize) !Terminal { return Terminal{ .cols = cols, .rows = rows, - .screen = try Screen.init(alloc, rows, cols, 0), + // TODO: configurable scrollback + .screen = try Screen.init(alloc, rows, cols, 1000), .cursor = .{}, .saved_cursor = .{}, .tabstops = try Tabstops.init(alloc, cols, TABSTOP_INTERVAL), @@ -276,7 +277,7 @@ pub fn index(self: *Terminal) void { if (self.cursor.y < self.scrolling_region.top or self.cursor.y > self.scrolling_region.bottom) return; - self.scrollUp(); + self.screen.scroll(.{ .delta = 1 }); return; } @@ -673,16 +674,6 @@ pub fn deleteLines(self: *Terminal, count: usize) void { } } -/// Scroll the text up by one row. -pub fn scrollUp(self: *Terminal) void { - const tracy = trace(@src()); - defer tracy.end(); - - self.screen.scroll(.{ .delta = 1 }); - const last = self.screen.getRow(self.rows - 1); - for (last) |*cell| cell.char = 0; -} - /// Scroll the text down by one row. /// TODO: test pub fn scrollDown(self: *Terminal, count: usize) void { @@ -698,6 +689,18 @@ pub fn scrollDown(self: *Terminal, count: usize) void { self.insertLines(count); } +/// Options for scrolling the viewport of the terminal grid. +pub const ScrollViewport = union(enum) { + delta: isize, +}; + +/// Scroll the viewport of the terminal grid. +pub fn scrollViewport(self: *Terminal, behavior: ScrollViewport) void { + self.screen.scroll(switch (behavior) { + .delta => |delta| .{ .delta_no_grow = delta }, + }); +} + /// Set Top and Bottom Margins If bottom is not specified, 0 or bigger than /// the number of the bottom-most row, it is adjusted to the number of the /// bottom most row.