🔍 Module Scanned
src/engine/input/ (automated audit scan)
📝 Summary
The setMouseCapture function in Input has inconsistent type handling between its public API (which accepts anytype and passes directly to SDL) and the vtable implementation (which properly casts from ?*anyopaque to *c.SDL_Window). This creates a type safety gap where the public API could accept incompatible window types without compile-time verification.
📍 Location
- File:
src/engine/input/input.zig:184-187
- Function/Scope:
Input.setMouseCapture (public method)
- Secondary Location:
src/engine/input/input.zig:301-306 (vtable implementation)
🔴 Severity: Medium
- Medium: Inconsistent API design, potential for misuse, silent type coercion
💥 Impact
The public setMouseCapture method accepts anytype for the window parameter and passes it directly to SDL_SetWindowRelativeMouseMode without explicit type checking. While callers in this codebase consistently pass *c.SDL_Window or properly cast ?*anyopaque, the API design allows passing any pointer type. If an incompatible type is passed, this could cause undefined behavior or a segfault at runtime rather than a compile-time error.
Additionally, the vtable implementation at line 301-306 does proper type casting (@as(*c.SDL_Window, @ptrCast(@alignCast(w)))) with a null check before calling back into self.setMouseCapture, creating an inconsistent pattern where the safer code path goes through the vtable while the direct public API lacks the same safety.
🔎 Evidence
Public API (input.zig:184-187):
pub fn setMouseCapture(self: *Input, window: anytype, captured: bool) void {
self.mouse_captured = captured;
_ = c.SDL_SetWindowRelativeMouseMode(window, captured);
}
VTable implementation (input.zig:301-306):
fn impl_setMouseCapture(ptr: *anyopaque, window: ?*anyopaque, captured: bool) void {
const self: *Input = @ptrCast(@alignCast(ptr));
if (window) |w| {
self.setMouseCapture(@as(*c.SDL_Window, @ptrCast(@alignCast(w))), captured);
}
}
The vtable:
- Receives
window: ?*anyopaque from the interface
- Performs a null check with
if (window) |w|
- Explicitly casts
w from *anyopaque to *c.SDL_Window using @ptrCast(@alignCast())
- Calls
self.setMouseCapture(...) which then passes directly to SDL again
Interface definition (interfaces.zig:34):
setMouseCapture: *const fn (ptr: *anyopaque, window: ?*anyopaque, captured: bool) void,
🛠️ Proposed Fix
Refactor setMouseCapture to ensure consistent type handling. The public API should either:
Option 1 (Recommended): Make the public API accept the correct typed pointer and remove the redundant SDL call from the vtable:
pub fn setMouseCapture(self: *Input, window: *c.SDL_Window, captured: bool) void {
self.mouse_captured = captured;
_ = c.SDL_SetWindowRelativeMouseMode(window, captured);
}
And update the vtable implementation to not call back into the public method:
fn impl_setMouseCapture(ptr: *anyopaque, window: ?*anyopaque, captured: bool) void {
const self: *Input = @ptrCast(@alignCast(ptr));
if (window) |w| {
self.mouse_captured = captured;
_ = c.SDL_SetWindowRelativeMouseMode(@as(*c.SDL_Window, @ptrCast(@alignCast(w))), captured);
}
}
Option 2: If anytype is intentional for API flexibility, add explicit type validation:
pub fn setMouseCapture(self: *Input, window: anytype, captured: bool) void {
self.mouse_captured = captured;
const typed_window = @as(*c.SDL_Window, @ptrCast(@alignCast(window)));
_ = c.SDL_SetWindowRelativeMouseMode(typed_window, captured);
}
✅ Acceptance Criteria
📚 References
- Ziglang documentation on
anytype: https://ziglang.org/documentation/master/#Type-System
- Related:
src/engine/core/window.zig:6 defines WindowManager.window as *c.SDL_Window
- Callers:
src/game/map_controller.zig:38-42, src/game/screens/world.zig:505,510, src/game/screens/home.zig:100
🔍 Module Scanned
src/engine/input/(automated audit scan)📝 Summary
The
setMouseCapturefunction in Input has inconsistent type handling between its public API (which acceptsanytypeand passes directly to SDL) and the vtable implementation (which properly casts from?*anyopaqueto*c.SDL_Window). This creates a type safety gap where the public API could accept incompatible window types without compile-time verification.📍 Location
src/engine/input/input.zig:184-187Input.setMouseCapture(public method)src/engine/input/input.zig:301-306(vtable implementation)🔴 Severity: Medium
💥 Impact
The public
setMouseCapturemethod acceptsanytypefor the window parameter and passes it directly toSDL_SetWindowRelativeMouseModewithout explicit type checking. While callers in this codebase consistently pass*c.SDL_Windowor properly cast?*anyopaque, the API design allows passing any pointer type. If an incompatible type is passed, this could cause undefined behavior or a segfault at runtime rather than a compile-time error.Additionally, the vtable implementation at line 301-306 does proper type casting (
@as(*c.SDL_Window, @ptrCast(@alignCast(w)))) with a null check before calling back intoself.setMouseCapture, creating an inconsistent pattern where the safer code path goes through the vtable while the direct public API lacks the same safety.🔎 Evidence
Public API (input.zig:184-187):
VTable implementation (input.zig:301-306):
The vtable:
window: ?*anyopaquefrom the interfaceif (window) |w|wfrom*anyopaqueto*c.SDL_Windowusing@ptrCast(@alignCast())self.setMouseCapture(...)which then passes directly to SDL againInterface definition (interfaces.zig:34):
🛠️ Proposed Fix
Refactor
setMouseCaptureto ensure consistent type handling. The public API should either:Option 1 (Recommended): Make the public API accept the correct typed pointer and remove the redundant SDL call from the vtable:
And update the vtable implementation to not call back into the public method:
Option 2: If
anytypeis intentional for API flexibility, add explicit type validation:✅ Acceptance Criteria
setMouseCapturepublic API has explicit type signature matching SDL's expectationsrc/game/map_controller.zig,src/game/screens/*.zigcontinue to worknix develop --command zig build test📚 References
anytype: https://ziglang.org/documentation/master/#Type-Systemsrc/engine/core/window.zig:6definesWindowManager.windowas*c.SDL_Windowsrc/game/map_controller.zig:38-42,src/game/screens/world.zig:505,510,src/game/screens/home.zig:100