Backlog/engine/platform/src/windowing.zig

233 lines
7.2 KiB
Zig

const std = @import("std");
const core = @import("core");
const platform = @import("platform.zig");
const RingQueue = core.RingQueue;
pub 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,
icon: []const u8 = "textures/icon.png",
hasVideo: bool = true,
pub fn init() @This() {
const rv = @This(){
.extent = .{
.x = core.configVar(c_int, "platform.window.width", 1600),
.y = core.configVar(c_int, "platform.window.height", 900),
},
.windowName = core.configVar([]const u8, "platform.window.name", "Backlog Engine"),
.icon = core.configVar([]const u8, "platform.window.icon", "textures/icon.png"),
};
core.engine_log("platform settings {any}", .{rv});
return rv;
}
};
// 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 = undefined,
extent: core.Vector2f = undefined,
windowName: [:0]u8 = undefined,
iconPath: [:0]u8 = undefined,
enableImguiEvents: bool = true,
imguiVisible: bool = true,
windowDestroyed: bool = false,
processFuncs: std.ArrayListUnmanaged(*const fn (*sdl3.Event) void) = .{},
cursorPos: core.Vector2f = .{},
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "platform.Instance");
pub fn deinit(self: *@This()) void {
self.allocator.free(self.windowName);
self.allocator.free(self.iconPath);
self.processFuncs.deinit(self.allocator);
// 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,
) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
//.windowName = try allocator.dupeZ(u8, params.windowName),
//.iconPath = try allocator.dupeZ(u8, params.icon),
//.windowExtent = params.extent,
//.extent = .{ .x = @floatFromInt(params.extent.x), .y = @floatFromInt(params.extent.y) },
//.hasVideo = params.hasVideo,
};
return self;
}
pub fn setParams(self: *@This(), params: PlatformParams) !void {
self.windowName = try self.allocator.dupeZ(u8, params.windowName);
self.iconPath = try self.allocator.dupeZ(u8, params.icon);
self.windowExtent = params.extent;
self.extent = .{ .x = @floatFromInt(params.extent.x), .y = @floatFromInt(params.extent.y) };
self.hasVideo = params.hasVideo;
}
pub fn setMouseRelativeMode(self: *@This(), relativeMode: bool) void {
// _ = sdl3.c.SDL_SetHint(sdl3.c.SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, "1");
_ = sdl3.c.SDL_SetWindowRelativeMouseMode(self.window, relativeMode);
_ = sdl3.c.SDL_SetWindowMouseRect(self.window, null);
self.cursorEnabled = !relativeMode;
self.enableImguiEvents = !relativeMode;
}
pub fn addSDLProcessFunction(self: *@This(), processFunc: *const fn (*sdl3.Event) void) !void {
try self.processFuncs.append(self.allocator, processFunc);
}
pub fn setupWindow(self: *@This()) core.EngineDataEventError!void {
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
self.extent = .{
.x = @floatFromInt(self.windowExtent.x),
.y = @floatFromInt(self.windowExtent.y),
};
}
pub fn registerFuncs(self: *@This()) !void {
core.setupEnginePlatform(self, enginePoll, procEvents);
}
// 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 setMousePosition(self: *@This(), pos: core.Vector2f) void {
sdl3.c.SDL_WarpMouseInWindow(self.window, pos.x, pos.y);
}
pub fn procEvents(ptr: *anyopaque, frameNumber: u64) core.EngineDataEventError!void {
_ = frameNumber;
const self: *@This() = @ptrCast(@alignCast(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 (event.type == sdl3.events.mouse_motion) {
sdl3.getMouseState(&self.cursorPos.x, &self.cursorPos.y);
}
if (core.inputs.convertEvent(event)) |converted| {
inputStack.routeEvent(converted);
}
for (self.processFuncs.items) |func| {
func(@ptrCast(event));
}
switch (event.type) {
sdl3.events.quit => {
core.exitNow();
},
else => {},
}
}
inputStack.sendAxisUpdates();
}
pub fn getCursorPosition(self: *@This()) core.Vector2f {
return self.cursorPos;
}
pub fn isCursorEnabled(self: @This()) bool {
return self.cursorEnabled;
}
pub fn setCursorVisible(self: @This(), visible: bool) void {
self.cursorEnabled = visible;
if (visible) {
sdl3.showCursor();
} else {
sdl3.hideCursor();
}
}
};
pub const IOEvent = core.IOEvent;