Skip to content

Commit

Permalink
add safe-guards to event handlers
Browse files Browse the repository at this point in the history
I noticed it ended up being quite common to accidentally use an invalid
function type, which was allowed because the argument type is `anytype`.
Notably, a common mistake is using `void` as a return type instead of
`!void` or `anyerror!void`, which would cause runtime errors that are
hard to understand and debug.
  • Loading branch information
zenith391 committed Nov 2, 2024
1 parent e3d7f41 commit 3d4efcc
Showing 1 changed file with 17 additions and 0 deletions.
17 changes: 17 additions & 0 deletions src/internal.zig
Original file line number Diff line number Diff line change
Expand Up @@ -718,41 +718,58 @@ pub fn Events(comptime T: type) type {
opacityChanged(self.widget_data.atoms.opacity.get(), self); // call it so it's updated
}

fn isValidHandler(comptime U: type) bool {
if (@typeInfo(U) != .pointer) return false;
const child = @typeInfo(U).pointer.child;
if (@typeInfo(child) != .@"fn") return false;
const return_type = @typeInfo(child).@"fn".return_type.?;
return @typeInfo(return_type) == .error_union;
}

pub fn addClickHandler(self: *T, handler: anytype) !void {
comptime std.debug.assert(isValidHandler(@TypeOf(handler)));
try self.widget_data.handlers.clickHandlers.append(@as(Callback, @ptrCast(handler)));
}

pub fn addDrawHandler(self: *T, handler: anytype) !void {
comptime std.debug.assert(isValidHandler(@TypeOf(handler)));
try self.widget_data.handlers.drawHandlers.append(@as(DrawCallback, @ptrCast(handler)));
}

pub fn addMouseButtonHandler(self: *T, handler: anytype) !void {
comptime std.debug.assert(isValidHandler(@TypeOf(handler)));
try self.widget_data.handlers.buttonHandlers.append(@as(ButtonCallback, @ptrCast(handler)));
}

pub fn addMouseMotionHandler(self: *T, handler: anytype) !void {
comptime std.debug.assert(isValidHandler(@TypeOf(handler)));
try self.widget_data.handlers.mouseMoveHandlers.append(@as(MouseMoveCallback, @ptrCast(handler)));
}

pub fn addScrollHandler(self: *T, handler: anytype) !void {
comptime std.debug.assert(isValidHandler(@TypeOf(handler)));
try self.widget_data.handlers.scrollHandlers.append(@as(ScrollCallback, @ptrCast(handler)));
}

pub fn addResizeHandler(self: *T, handler: anytype) !void {
comptime std.debug.assert(isValidHandler(@TypeOf(handler)));
try self.widget_data.handlers.resizeHandlers.append(@as(ResizeCallback, @ptrCast(handler)));
}

pub fn addKeyTypeHandler(self: *T, handler: anytype) !void {
comptime std.debug.assert(isValidHandler(@TypeOf(handler)));
try self.widget_data.handlers.keyTypeHandlers.append(@as(KeyTypeCallback, @ptrCast(handler)));
}

pub fn addKeyPressHandler(self: *T, handler: anytype) !void {
comptime std.debug.assert(isValidHandler(@TypeOf(handler)));
try self.widget_data.handlers.keyPressHandlers.append(@as(KeyPressCallback, @ptrCast(handler)));
}

/// This shouldn't be used by user applications directly.
/// Instead set a change listener to the corresponding atom.
pub fn addPropertyChangeHandler(self: *T, handler: anytype) !void {
comptime std.debug.assert(isValidHandler(@TypeOf(handler)));
try self.widget_data.handlers.propertyChangeHandlers.append(@as(PropertyChangeCallback, @ptrCast(handler)));
}

Expand Down

0 comments on commit 3d4efcc

Please sign in to comment.