const std = @import("std"); const core = @import("core"); const platform = @import("platform.zig"); const RingQueue = core.RingQueue; const sdl3 = @import("sdl3"); const tracy = core.tracy; pub const InputListenerError = error{ UnknownError, }; pub const RawInputListenerInterface = struct { // required functions OnIoEvent: *const fn (*anyopaque, event: IOEvent) InputListenerError!void, pub fn from(comptime TargetType: type) @This() { const Wrapped = struct { pub fn OnIoEvent(pointer: *anyopaque, event: IOEvent) InputListenerError!void { var ptr = @as(*TargetType, @ptrCast(@alignCast(pointer))); try ptr.OnIoEvent(event); } }; return .{ .OnIoEvent = Wrapped.OnIoEvent, }; } }; pub const RawInputObjectRef = struct { ptr: *anyopaque, vtable: *const RawInputListenerInterface, pub fn from(target: anytype) @This() { const vtable = &(@TypeOf(target.*)).RawInputListenerVTable; return .{ .ptr = target, .vtable = vtable, }; } }; // We are having some serious issues with this events pump. pub const PlatformParams = struct { extent: core.Vector2c = .{ .x = 1600, .y = 900 }, windowName: []const u8 = "sample window", icon: []const u8 = "textures/icon.png", hasVideo: bool = true, }; pub var gPlatformSettings: struct { pollLockoutTime: ?f32 = null, decoratedWindow: bool = true, transparentFrameBuffer: bool = false, } = .{}; // For windows and linux computers this is a glfw instance pub const PlatformInstance = struct { allocator: std.mem.Allocator, cursorEnabled: bool = true, newCursorEnabled: bool = true, hasVideo: bool = false, window: *sdl3.Window = undefined, windowExtent: core.Vector2c, windowName: [:0]u8, iconPath: [:0]u8, windowDestroyed: bool = false, pub fn deinit(self: *@This()) void { self.allocator.free(self.windowName); self.allocator.free(self.iconPath); // shutdown } pub fn onExitSignal(self: *@This()) void { sdl3.c.SDL_DestroyWindow(self.window); self.windowDestroyed = true; } pub fn readyToExit(self: *@This()) bool { return self.windowDestroyed; } pub fn init( allocator: std.mem.Allocator, params: PlatformParams, ) !@This() { const self: @This() = .{ .allocator = allocator, .windowName = try allocator.dupeZ(u8, params.windowName), .iconPath = try allocator.dupeZ(u8, params.icon), .windowExtent = params.extent, .hasVideo = params.hasVideo, }; return self; } pub fn setupWindow(opaqueSelf: *anyopaque) core.EngineDataEventError!void { const self: *@This() = @alignCast(@ptrCast(opaqueSelf)); sdl3.init(.{ .video = true, .gamepad = true }) catch return error.BadInit; self.window = sdl3.c.SDL_CreateWindow(self.windowName, self.windowExtent.x, self.windowExtent.y, 0).?; // resizeable } pub fn registerSetupFuncs(self: *@This()) !void { core.setupEnginePlatform(self, setupWindow, enginePoll, processEvents); } // runs pinned on io thread. // old glfw needed to do it's polling here. pub fn enginePoll(opaqueSelf: *anyopaque) core.EngineDataEventError!void { _ = opaqueSelf; // this polling event should probably be used for IO, now that we have a free thread for doing w.e // var self: *@This() = @alignCast(@ptrCast(opaqueSelf)); // self.pollEvents(); } pub fn installListener(self: *@This(), listener: anytype) !void { try self.listeners.append(RawInputObjectRef.from(listener)); } pub fn processEvents(ptr: *anyopaque, frameNumber: u64) core.EngineDataEventError!void { _ = frameNumber; _ = ptr; // const self: *@This() = @ptrCast(@alignCast(ptr)); var t1 = tracy.ZoneN(@src(), "Pumping Events"); defer t1.End(); const inputStack = core.getInputStack(); inputStack.updatePreviousInputs(); while (sdl3.pollEvent()) |event| { if (core.inputs.convertEvent(event)) |converted| { inputStack.routeEvent(converted); } switch (event.type) { sdl3.events.quit => { core.exitNow(); }, else => {}, } } } pub fn getCursorPosition(self: *@This()) core.Vector2f { return self.cursorPos; } pub fn setCursorEnabled(self: *@This(), cursorEnabled: bool) void { self.newCursorEnabled = cursorEnabled; } pub fn isCursorEnabled(self: @This()) bool { return self.cursorEnabled; } }; pub const IOEvent = core.IOEvent;