feat: change event system

this fixes a common use-after-free footgun and removes the need for a
global allocator
This commit is contained in:
LordMZTE 2022-09-02 21:03:17 +02:00
parent 97be1d863a
commit a62e77483b
Signed by: LordMZTE
GPG key ID: B64802DC33A64FF6
4 changed files with 30 additions and 31 deletions

View file

@ -22,7 +22,9 @@ pub fn init() void {
// do initialization stuff // do initialization stuff
} }
pub fn update(ev: zz.Event) void { pub fn update() void {
var event = zz.getEvent(my_allocator) catch return;
defer event.deinit();
// handle events // handle events
} }

View file

@ -16,10 +16,6 @@ var mode: ?zz.types.InputMode = null;
// called on startup // called on startup
pub fn init() void { pub fn init() void {
// set zellzig's allocator
// This is required to receive events.
zz.allocator = gpa.allocator();
// This is required to make zellij close once everything but our plugin is gone. // This is required to make zellij close once everything but our plugin is gone.
zz.api.setSelectable(false); zz.api.setSelectable(false);
@ -28,8 +24,11 @@ pub fn init() void {
} }
// called on every event // called on every event
pub fn update(ev: zz.Event) void { pub fn update() void {
switch (ev) { var ev = zz.getEvent(gpa.allocator()) catch return;
defer ev.deinit();
switch (ev.data) {
.ModeUpdate => |mode_info| mode = mode_info.mode, .ModeUpdate => |mode_info| mode = mode_info.mode,
else => {}, else => {},
} }

View file

@ -15,17 +15,17 @@ pub fn sendObj(data: anytype) !void {
} }
/// Receives a JSON object from zellij /// Receives a JSON object from zellij
pub fn recvObj(comptime T: type) !zz.OwnedDeserData(T) { pub fn recvObj(comptime T: type, alloc: std.mem.Allocator) !zz.OwnedDeserData(T) {
var stdin = std.io.getStdIn(); var stdin = std.io.getStdIn();
const data = (try stdin.reader().readUntilDelimiterOrEofAlloc( const data = (try stdin.reader().readUntilDelimiterOrEofAlloc(
zz.allocator.?, alloc,
'\n', '\n',
std.math.maxInt(usize), std.math.maxInt(usize),
)) orelse unreachable; )) orelse unreachable;
defer zz.allocator.?.free(data); defer alloc.free(data);
return zz.OwnedDeserData(T).deserialize(zz.allocator.?, data); return zz.OwnedDeserData(T).deserialize(alloc, data);
} }
/// Subscribes to the given events. /// Subscribes to the given events.
@ -49,15 +49,15 @@ pub fn setSelectable(selectable: bool) void {
} }
/// Returns the ID of this plugin, and zellij's PID. /// Returns the ID of this plugin, and zellij's PID.
pub fn getPluginIds() !zz.OwnedDeserData(types.PluginIds) { pub fn getPluginIds(alloc: std.mem.Allocator) !zz.OwnedDeserData(types.PluginIds) {
zapi.host_get_plugin_ids(); zapi.host_get_plugin_ids();
return try recvObj(types.PluginIds); return try recvObj(types.PluginIds, alloc);
} }
/// Returns zellij's version string. /// Returns zellij's version string.
pub fn getZellijVersion() !zz.OwnedDeserData([]const u8) { pub fn getZellijVersion(alloc: std.mem.Allocator) !zz.OwnedDeserData([]const u8) {
zapi.host_get_zellij_version(); zapi.host_get_zellij_version();
return try recvObj([]const u8); return try recvObj([]const u8, alloc);
} }
pub fn openFile(path: []const u8) !void { pub fn openFile(path: []const u8) !void {

View file

@ -19,22 +19,26 @@ test {
/// ```zig /// ```zig
/// // main.zig /// // main.zig
/// const zz = @import("zellzig"); /// const zz = @import("zellzig");
///
/// const gpa = std.heap.GeneralPurposeAllocator(.{}){};
///
/// comptime { /// comptime {
/// zz.createPlugin(@This()); /// zz.createPlugin(@This());
/// } /// }
/// ///
/// pub fn init() void { /// pub fn init() void {}
/// const alloc = std.heap.GeneralPurposeAllocator(.{}){}; /// pub fn update(event: zz.Event) void {
/// zz.allocator = alloc.allocator(); /// var event = zz.getEvent(gpa.allocator()) catch return;
/// defer event.deinit();
/// // use event
/// } /// }
/// pub fn update(event: zz.Event) void {}
/// pub fn render(rows: i32, cols: i32) void {} /// pub fn render(rows: i32, cols: i32) void {}
/// ``` /// ```
pub fn createPlugin(comptime Plugin: type) void { pub fn createPlugin(comptime Plugin: type) void {
if (@TypeOf(Plugin.init) != fn () void) if (@TypeOf(Plugin.init) != fn () void)
@compileError("Function 'init' has invalid signature!"); @compileError("Function 'init' has invalid signature!");
if (@TypeOf(Plugin.update) != fn (Event) void) if (@TypeOf(Plugin.update) != fn () void)
@compileError("Function 'update' has invalid signature!"); @compileError("Function 'update' has invalid signature!");
if (@TypeOf(Plugin.render) != fn (i32, i32) void) if (@TypeOf(Plugin.render) != fn (i32, i32) void)
@ -50,17 +54,11 @@ pub fn createPlugin(comptime Plugin: type) void {
} }
export fn update() void { export fn update() void {
if (allocator == null) { Plugin.update();
@panic("Got event while allocator is null! Did you forget to set it?");
}
var ev = api.recvObj(types.Event) catch |err| {
std.log.err("Deserialize error: {}", .{err});
return;
};
defer ev.deinit();
Plugin.update(ev.data);
} }
}; };
} }
pub fn getEvent(alloc: std.mem.Allocator) !OwnedDeserData(types.Event) {
return try api.recvObj(types.Event, alloc);
}