Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions src/Loop.zig
Original file line number Diff line number Diff line change
Expand Up @@ -364,11 +364,8 @@ pub fn handleEventGeneric(self: anytype, vx: *Vaxis, cache: *GraphemeCache, Even
},
.winsize => |winsize| {
vx.state.in_band_resize = true;
switch (builtin.os.tag) {
.windows => {},
// Reset the signal handler if we are receiving in_band_resize
else => Tty.resetSignalHandler(),
}
// Reset the signal handler if we are receiving in_band_resize
Tty.resetSignalHandler();
if (@hasField(Event, "winsize")) {
return self.postEvent(.{ .winsize = winsize });
}
Expand Down
5 changes: 5 additions & 0 deletions src/Vaxis.zig
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub const Options = struct {
system_clipboard_allocator: ?std.mem.Allocator = null,
};


/// the screen we write to
screen: Screen,
/// The last screen we drew. We keep this so we can efficiently update on
Expand Down Expand Up @@ -190,6 +191,10 @@ pub fn resize(
tty: *IoWriter,
winsize: Winsize,
) !void {
_ = std.math.mul(u16, winsize.cols, winsize.rows) catch {
log.warn("Invalid Screen Size: width={d} height={d}", .{ winsize.cols, winsize.rows });
return;
};
log.debug("resizing screen: width={d} height={d}", .{ winsize.cols, winsize.rows });
self.screen.deinit(alloc);
self.screen = try Screen.init(alloc, winsize);
Expand Down
45 changes: 35 additions & 10 deletions src/vxfw/App.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const std = @import("std");
const log = std.log.scoped(.vx_app);
const vaxis = @import("../main.zig");
const vxfw = @import("vxfw.zig");

Expand Down Expand Up @@ -27,8 +28,9 @@ pub const Options = struct {
/// Create an application. We require stable pointers to do the set up, so this will create an App
/// object on the heap. Call destroy when the app is complete to reset terminal state and release
/// resources
pub fn init(allocator: Allocator) !App {
var app: App = .{
pub fn init(allocator: Allocator) !*App {
const app = try allocator.create(App);
app.* = .{
.allocator = allocator,
.tty = undefined,
.vx = try vaxis.init(allocator, .{
Expand All @@ -37,7 +39,8 @@ pub fn init(allocator: Allocator) !App {
.report_events = true,
},
}),
.timers = std.ArrayList(vxfw.Tick){},
//.timers = std.ArrayList(vxfw.Tick){},
.timers = .empty,
.wants_focus = null,
.buffer = undefined,
};
Expand All @@ -49,6 +52,7 @@ pub fn deinit(self: *App) void {
self.timers.deinit(self.allocator);
self.vx.deinit(self.allocator, self.tty.writer());
self.tty.deinit();
self.allocator.destroy(self);
}

pub fn run(self: *App, widget: vxfw.Widget, opts: Options) anyerror!void {
Expand Down Expand Up @@ -116,8 +120,29 @@ pub fn run(self: *App, widget: vxfw.Widget, opts: Options) anyerror!void {
// Deadline exceeded. Schedule the next frame
next_frame_ms = now_ms + tick_ms;
} else {
const diff_ms = diffMS: {
const diff = std.math.sub(u64, next_frame_ms, now_ms) catch {
log.warn("Invalid Time Diff: {d}ms(next) - {d}ms(now)", .{ next_frame_ms, now_ms });
next_frame_ms = now_ms;
continue;
};
break :diffMS @min(tick_ms, diff);
};
//log.info(
// \\Vx App Sleep:
// \\- Now: {d}ms
// \\- Tick: {d}ms
// \\- Next: {d}ms
// \\- Diff: {d}ns
// , .{
// now_ms,
// tick_ms,
// next_frame_ms,
// diff_ms *| std.time.ns_per_ms,
// },
//);
// Sleep until the deadline
std.Thread.sleep((next_frame_ms - now_ms) * std.time.ns_per_ms);
std.Thread.sleep(diff_ms *| std.time.ns_per_ms);
next_frame_ms += tick_ms;
}

Expand Down Expand Up @@ -463,8 +488,8 @@ const MouseHandler = struct {
ctx.phase = .capturing;
for (hits.items) |item| {
var m_local = mouse;
m_local.col = item.local.col;
m_local.row = item.local.row;
m_local.col = @intCast(item.local.col);
m_local.row = @intCast(item.local.row);
try item.widget.captureEvent(ctx, .{ .mouse = m_local });
try app.handleCommand(&ctx.cmds);

Expand All @@ -475,8 +500,8 @@ const MouseHandler = struct {
ctx.phase = .at_target;
{
var m_local = mouse;
m_local.col = target.local.col;
m_local.row = target.local.row;
m_local.col = @intCast(target.local.col);
m_local.row = @intCast(target.local.row);
try target.widget.handleEvent(ctx, .{ .mouse = m_local });
try app.handleCommand(&ctx.cmds);

Expand All @@ -487,8 +512,8 @@ const MouseHandler = struct {
ctx.phase = .bubbling;
while (hits.pop()) |item| {
var m_local = mouse;
m_local.col = item.local.col;
m_local.row = item.local.row;
m_local.col = @intCast(item.local.col);
m_local.row = @intCast(item.local.row);
try item.widget.handleEvent(ctx, .{ .mouse = m_local });
try app.handleCommand(&ctx.cmds);

Expand Down
67 changes: 40 additions & 27 deletions src/vxfw/vxfw.zig
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,32 @@ pub const Event = union(enum) {
key_press: vaxis.Key,
key_release: vaxis.Key,
mouse: vaxis.Mouse,
focus_in, // window has gained focus
focus_out, // window has lost focus
paste_start, // bracketed paste start
paste_end, // bracketed paste end
paste: []const u8, // osc 52 paste, caller must free
color_report: vaxis.Color.Report, // osc 4, 10, 11, 12 response
color_scheme: vaxis.Color.Scheme, // light / dark OS theme changes
winsize: vaxis.Winsize, // the window size has changed. This event is always sent when the loop is started
app: UserEvent, // A custom event from the app
tick, // An event from a Tick command
init, // sent when the application starts
mouse_leave, // The mouse has left the widget
mouse_enter, // The mouse has enterred the widget
/// window has gained focus
focus_in,
/// window has lost focus
focus_out,
/// bracketed paste start
paste_start,
/// bracketed paste end
paste_end,
/// osc 52 paste, caller must free
paste: []const u8,
/// osc 4, 10, 11, 12 response
color_report: vaxis.Color.Report,
/// light / dark OS theme changes
color_scheme: vaxis.Color.Scheme,
/// the window size has changed. This event is always sent when the loop is started
winsize: vaxis.Winsize,
/// A custom event from the app
app: UserEvent,
/// An event from a Tick command
tick,
/// sent when the application starts
init,
/// The mouse has left the widget
mouse_leave,
/// The mouse has enterred the widget
mouse_enter,
};

pub const Tick = struct {
Expand Down Expand Up @@ -142,14 +155,14 @@ pub const EventContext = struct {
}

/// Copy content to clipboard.
/// content is duplicated using self.alloc.
/// content is duplicated using `self.alloc`.
/// Caller retains ownership of their copy of content.
pub fn copyToClipboard(self: *EventContext, content: []const u8) Allocator.Error!void {
try self.addCmd(.{ .copy_to_clipboard = try self.alloc.dupe(u8, content) });
}

/// Set window title.
/// title is duplicated using self.alloc.
/// title is duplicated using `self.alloc`.
/// Caller retains ownership of their copy of title.
pub fn setTitle(self: *EventContext, title: []const u8) Allocator.Error!void {
try self.addCmd(.{ .set_title = try self.alloc.dupe(u8, title) });
Expand All @@ -160,7 +173,7 @@ pub const EventContext = struct {
self.redraw = true;
}

/// Send a system notification. This function dupes title and body using it's own allocator.
/// Send a system notification. This function dupes title and body using its own allocator.
/// They will be freed once the notification has been sent
pub fn sendNotification(
self: *EventContext,
Expand Down Expand Up @@ -298,7 +311,7 @@ pub const Widget = struct {

pub const FlexItem = struct {
widget: Widget,
/// A value of zero means the child will have it's inherent size. Any value greater than zero
/// A value of zero means the child will have its inherent size. Any value greater than zero
/// and the remaining space will be proportioned to each item
flex: u8 = 1,

Expand Down Expand Up @@ -354,9 +367,9 @@ pub const Surface = struct {
};
}

/// Creates a slice of vaxis.Cell's equal to size.width * size.height
/// Creates a slice of `vaxis.Cell`s equal to `size.width` * `size.height`
pub fn createBuffer(allocator: Allocator, size: Size) Allocator.Error![]vaxis.Cell {
const buffer = try allocator.alloc(vaxis.Cell, size.width * size.height);
const buffer = try allocator.alloc(vaxis.Cell, size.width *| size.height);
@memset(buffer, .{ .default = true });
return buffer;
}
Expand Down Expand Up @@ -387,14 +400,14 @@ pub const Surface = struct {
pub fn writeCell(self: Surface, col: u16, row: u16, cell: vaxis.Cell) void {
if (self.size.width <= col) return;
if (self.size.height <= row) return;
const i = (row * self.size.width) + col;
const i = @min((row *| self.size.width) +| col, self.buffer.len);
assert(i < self.buffer.len);
self.buffer[i] = cell;
}

pub fn readCell(self: Surface, col: usize, row: usize) vaxis.Cell {
assert(col < self.size.width and row < self.size.height);
const i = (row * self.size.width) + col;
const i = (row *| self.size.width) +| col;
assert(i < self.buffer.len);
return self.buffer[i];
}
Expand All @@ -405,7 +418,7 @@ pub const Surface = struct {
return .{
.size = .{ .width = self.size.width, .height = height },
.widget = self.widget,
.buffer = self.buffer[0 .. self.size.width * height],
.buffer = self.buffer[0..self.size.width *| height],
.children = self.children,
};
}
Expand All @@ -420,8 +433,8 @@ pub const Surface = struct {
for (self.children) |child| {
if (!child.containsPoint(point)) continue;
const child_point: Point = .{
.row = @intCast(point.row - child.origin.row),
.col = @intCast(point.col - child.origin.col),
.row = @intCast(point.row -| child.origin.row),
.col = @intCast(point.col -| child.origin.col),
};
try child.surface.hitTest(allocator, list, child_point);
}
Expand Down Expand Up @@ -486,8 +499,8 @@ pub const SubSurface = struct {
pub fn containsPoint(self: SubSurface, point: Point) bool {
return point.col >= self.origin.col and
point.row >= self.origin.row and
point.col < (self.origin.col + self.surface.size.width) and
point.row < (self.origin.row + self.surface.size.height);
point.col < (self.origin.col +| self.surface.size.width) and
point.row < (self.origin.row +| self.surface.size.height);
}
};

Expand Down Expand Up @@ -563,7 +576,7 @@ test "All widgets have a doctest and refAllDecls test" {
const decl = ast.nodes.get(@intFromEnum(root_decl));
switch (decl.tag) {
.test_decl => {
const test_name = ast.tokenSlice(decl.main_token + 1);
const test_name = ast.tokenSlice(decl.main_token +| 1);
if (std.mem.eql(u8, "\"refAllDecls\"", test_name))
has_refAllDecls = true
else if (std.mem.eql(u8, container_name, test_name))
Expand Down
Loading