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
}
pub fn update(ev: zz.Event) void {
pub fn update() void {
var event = zz.getEvent(my_allocator) catch return;
defer event.deinit();
// handle events
}

View File

@ -16,10 +16,6 @@ var mode: ?zz.types.InputMode = null;
// called on startup
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.
zz.api.setSelectable(false);
@ -28,8 +24,11 @@ pub fn init() void {
}
// called on every event
pub fn update(ev: zz.Event) void {
switch (ev) {
pub fn update() void {
var ev = zz.getEvent(gpa.allocator()) catch return;
defer ev.deinit();
switch (ev.data) {
.ModeUpdate => |mode_info| mode = mode_info.mode,
else => {},
}

View File

@ -15,17 +15,17 @@ pub fn sendObj(data: anytype) !void {
}
/// 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();
const data = (try stdin.reader().readUntilDelimiterOrEofAlloc(
zz.allocator.?,
alloc,
'\n',
std.math.maxInt(usize),
)) 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.
@ -49,15 +49,15 @@ pub fn setSelectable(selectable: bool) void {
}
/// 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();
return try recvObj(types.PluginIds);
return try recvObj(types.PluginIds, alloc);
}
/// 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();
return try recvObj([]const u8);
return try recvObj([]const u8, alloc);
}
pub fn openFile(path: []const u8) !void {

View File

@ -19,22 +19,26 @@ test {
/// ```zig
/// // main.zig
/// const zz = @import("zellzig");
///
/// const gpa = std.heap.GeneralPurposeAllocator(.{}){};
///
/// comptime {
/// zz.createPlugin(@This());
/// }
///
/// pub fn init() void {
/// const alloc = std.heap.GeneralPurposeAllocator(.{}){};
/// zz.allocator = alloc.allocator();
/// pub fn init() void {}
/// pub fn update(event: zz.Event) void {
/// 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 createPlugin(comptime Plugin: type) void {
if (@TypeOf(Plugin.init) != fn () void)
@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!");
if (@TypeOf(Plugin.render) != fn (i32, i32) void)
@ -50,17 +54,11 @@ pub fn createPlugin(comptime Plugin: type) void {
}
export fn update() void {
if (allocator == null) {
@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);
Plugin.update();
}
};
}
pub fn getEvent(alloc: std.mem.Allocator) !OwnedDeserData(types.Event) {
return try api.recvObj(types.Event, alloc);
}