362 lines
12 KiB
Zig
362 lines
12 KiB
Zig
const std = @import("std");
|
|
const core = @import("core");
|
|
const platform = @import("platform.zig");
|
|
|
|
const nfd = @import("nfd");
|
|
|
|
const RingQueue = core.RingQueue;
|
|
pub const sdl3 = @import("sdl3");
|
|
const tracy = core.tracy;
|
|
|
|
pub const InputListenerError = error{
|
|
UnknownError,
|
|
};
|
|
|
|
pub fn convertEvent(event: *const sdl3.Event) ?core.inputs.IOEvent {
|
|
switch (event.type) {
|
|
sdl3.events.key_down, sdl3.events.key_up => {
|
|
const scan: core.inputs.Key = @enumFromInt(event.key.scancode);
|
|
|
|
if (event.key.repeat) {
|
|
return null;
|
|
}
|
|
|
|
return IOEvent{
|
|
.key = .{
|
|
.key = scan,
|
|
.scancode = scan,
|
|
.action = if (event.key.down) .keyDown else .keyUp,
|
|
.mods = 0,
|
|
},
|
|
};
|
|
},
|
|
sdl3.events.mouse_button_down, sdl3.events.mouse_button_up => {
|
|
const scan: core.inputs.Key = @enumFromInt(@as(u32, @intCast(event.button.button)) + 10000);
|
|
|
|
return IOEvent{
|
|
.key = .{
|
|
.key = scan,
|
|
.scancode = scan,
|
|
.action = if (event.button.down) .keyDown else .keyUp,
|
|
.mods = 0,
|
|
},
|
|
};
|
|
},
|
|
sdl3.events.mouse_motion => {
|
|
const motion = core.Vector2f{ .x = event.motion.xrel, .y = event.motion.yrel };
|
|
|
|
return .{ .mouseRelative = motion };
|
|
},
|
|
else => {
|
|
return null;
|
|
},
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
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 },
|
|
resizeable: bool = true,
|
|
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,
|
|
imguiMouseConsumed: bool = false,
|
|
|
|
windowDestroyed: bool = false,
|
|
|
|
processFuncs: std.ArrayListUnmanaged(*const fn (*sdl3.Event) void) = .{},
|
|
|
|
platformRequests: std.ArrayListUnmanaged(PlatformRequest) = .{},
|
|
|
|
cursorPos: core.Vector2f = .{},
|
|
|
|
nfdRuntime: *nfd.NFDRuntime,
|
|
|
|
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "platform.Instance");
|
|
|
|
pub const PlatformRequest = union(enum(u32)) {
|
|
setMouseRelativeMode: struct { relativeMode: bool },
|
|
setMousePosition: struct { pos: core.Vector2f },
|
|
setCursorVisible: struct { visible: bool },
|
|
setWindowSize: struct { size: core.Vector2u },
|
|
maximizeWindow: struct {},
|
|
setWindowPosition: struct { pos: core.Vector2u },
|
|
};
|
|
|
|
pub fn pushRequest(self: *@This(), req: PlatformRequest) void {
|
|
self.platformRequests.append(self.allocator, req) catch {
|
|
core.engine_err("unable to issue request", .{});
|
|
};
|
|
}
|
|
|
|
pub fn handlePlatformRequests(self: *@This()) void {
|
|
for (self.platformRequests.items) |req| {
|
|
switch (req) {
|
|
.setMouseRelativeMode => |p| {
|
|
self.setMouseRelativeMode(p.relativeMode);
|
|
},
|
|
.setMousePosition => |p| {
|
|
self.setMousePosition(p.pos);
|
|
},
|
|
.setCursorVisible => |p| {
|
|
self.setCursorVisible(p.visible);
|
|
},
|
|
.setWindowSize => |p| {
|
|
self.setWindowSize(p.size);
|
|
},
|
|
.maximizeWindow => {
|
|
self.maximizeWindow();
|
|
},
|
|
.setWindowPosition => |p| {
|
|
self.setWindowPosition(p.pos);
|
|
},
|
|
}
|
|
}
|
|
|
|
self.platformRequests.clearRetainingCapacity();
|
|
}
|
|
|
|
pub fn deinit(self: *@This()) void {
|
|
self.allocator.free(self.windowName);
|
|
self.nfdRuntime.destroy();
|
|
self.allocator.free(self.iconPath);
|
|
self.processFuncs.deinit(self.allocator);
|
|
self.platformRequests.deinit(self.allocator);
|
|
}
|
|
|
|
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(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
|
if (!first)
|
|
return;
|
|
|
|
self.* = .{
|
|
.allocator = allocator,
|
|
.nfdRuntime = try nfd.NFDRuntime.create(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,
|
|
};
|
|
}
|
|
|
|
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, sdl3.c.SDL_WINDOW_RESIZABLE).?; // 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 {
|
|
const self = core.cast(*@This(), opaqueSelf);
|
|
self.nfdRuntime.processMessages() catch {};
|
|
// 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));
|
|
|
|
self.nfdRuntime.processCallbacks();
|
|
|
|
self.handlePlatformRequests();
|
|
|
|
// 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 (event.type == sdl3.events.window_resized) {
|
|
self.extent.x = @floatFromInt(event.window.data1);
|
|
self.extent.y = @floatFromInt(event.window.data2);
|
|
core.engine_log("engine resized {d}x{d}", self.extent);
|
|
}
|
|
|
|
for (self.processFuncs.items) |func| {
|
|
func(@ptrCast(event));
|
|
}
|
|
|
|
if (convertEvent(event)) |converted| {
|
|
var shouldSkip: bool = false;
|
|
if (self.imguiMouseConsumed and (event.type == sdl3.events.mouse_button_down or event.type == sdl3.events.mouse_button_up)) {
|
|
shouldSkip = true;
|
|
}
|
|
if (!shouldSkip) {
|
|
inputStack.routeEvent(converted);
|
|
|
|
if (event.type == sdl3.events.mouse_motion) {
|
|
inputStack.setMousePosition(self.cursorPos.x, self.cursorPos.y);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 fn setWindowSize(self: *@This(), size: core.Vector2u) void {
|
|
if (sdl3.c.SDL_SetWindowSize(self.window, @intCast(size.x), @intCast(size.y))) {
|
|
_ = sdl3.c.SDL_SyncWindow(self.window);
|
|
} else {
|
|
core.engine_log("unable to set window size {s}", .{sdl3.getError()});
|
|
}
|
|
}
|
|
|
|
pub fn maximizeWindow(self: *@This()) void {
|
|
_ = sdl3.c.SDL_MaximizeWindow(self.window);
|
|
}
|
|
|
|
pub fn setWindowPosition(self: *@This(), pos: core.Vector2u) void {
|
|
_ = sdl3.c.SDL_SetWindowPosition(self.window, @intCast(pos.x), @intCast(pos.y));
|
|
}
|
|
};
|
|
|
|
pub const IOEvent = core.IOEvent;
|