diff --git a/src/Core.zig b/src/Core.zig index 3e6bed3a..dfff3f33 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -156,6 +156,8 @@ descriptor: gpu.SwapChain.Descriptor, // Internal state events: EventQueue, +input_state: InputState, +oom: std.Thread.ResetEvent = .{}, // TODO: this needs to be removed. pub const InitOptions = struct { @@ -208,6 +210,7 @@ fn init(core: *Mod, entities: *mach.Entities.Mod) !void { .allocator = allocator, .main_window = main_window, .events = events, + .input_state = .{}, // TODO: remove undefined initialization (disgusting!) .platform = undefined, @@ -617,6 +620,41 @@ pub inline fn nextEvent(core: *@This()) ?Event { return core.events.readItem(); } +/// Push an event onto the event queue, or set OOM if no space is available. +/// +/// Updates the input_state tracker. +pub inline fn pushEvent(core: *@This(), event: Event) void { + // Write event + core.events.writeItem(event) catch { + core.oom.set(); + return; + }; + + // Update input state + switch (event) { + .key_press => |ev| core.input_state.keys.setValue(@intFromEnum(ev.key), true), + .key_release => |ev| core.input_state.keys.setValue(@intFromEnum(ev.key), false), + .mouse_press => |ev| core.input_state.mouse_buttons.setValue(@intFromEnum(ev.button), true), + .mouse_release => |ev| core.input_state.mouse_buttons.setValue(@intFromEnum(ev.button), false), + .mouse_motion => |ev| core.input_state.mouse_position = ev.pos, + .focus_lost => { + // Clear input state that may be 'stuck' when focus is regained. + core.input_state.keys = InputState.KeyBitSet.initEmpty(); + core.input_state.mouse_buttons = InputState.MouseButtonSet.initEmpty(); + }, + else => {}, + } +} + +/// Reports whether mach.Core ran out of memory, indicating events may have been dropped. +/// +/// Once called, the OOM flag is reset and mach.Core will continue operating normally. +pub fn outOfMemory(core: *@This()) bool { + if (!core.oom.isSet()) return false; + core.oom.reset(); + return true; +} + /// Sets the window title. The string must be owned by Core, and will not be copied or freed. It is /// advised to use the `core.title` buffer for this purpose, e.g.: /// @@ -672,6 +710,26 @@ pub inline fn headless(core: *@This()) bool { return core.platform.headless; } +pub fn keyPressed(core: *@This(), key: Key) bool { + return core.input_state.isKeyPressed(key); +} + +pub fn keyReleased(core: *@This(), key: Key) bool { + return core.input_state.isKeyReleased(key); +} + +pub fn mousePressed(core: *@This(), button: MouseButton) bool { + return core.input_state.isMouseButtonPressed(button); +} + +pub fn mouseReleased(core: *@This(), button: MouseButton) bool { + return core.input_state.isMouseButtonReleased(button); +} + +pub fn mousePosition(core: *@This()) Position { + return core.input_state.mouse_position; +} + pub const VSyncMode = enum { /// Potential screen tearing. /// No synchronization with monitor, render frames as fast as possible. @@ -784,31 +842,11 @@ pub inline fn cursorShape(core: *@This()) CursorShape { return core.platform.cursorShape(); } -pub inline fn keyPressed(core: *@This(), key: Key) bool { - return core.platform.keyPressed(key); -} - -pub inline fn keyReleased(core: *@This(), key: Key) bool { - return core.platform.keyReleased(key); -} - -pub inline fn mousePressed(core: *@This(), button: MouseButton) bool { - return core.platform.mousePressed(button); -} - -pub inline fn mouseReleased(core: *@This(), button: MouseButton) bool { - return core.platform.mouseReleased(button); -} - pub const Position = struct { x: f64, y: f64, }; -pub inline fn mousePosition(core: *@This()) Position { - return core.platform.mousePosition(); -} - /// Sets the minimum target frequency of the input handling thread. /// /// Input handling (the main thread) runs at a variable frequency. The thread blocks until there are @@ -1100,12 +1138,6 @@ comptime { assertHasDecl(Platform, "setCursorShape"); assertHasField(Platform, "cursor_shape"); - - assertHasDecl(Platform, "keyPressed"); - assertHasDecl(Platform, "keyReleased"); - assertHasDecl(Platform, "mousePressed"); - assertHasDecl(Platform, "mouseReleased"); - assertHasDecl(Platform, "mousePosition"); } fn assertHasDecl(comptime T: anytype, comptime decl_name: []const u8) void { diff --git a/src/core/Darwin.zig b/src/core/Darwin.zig index 3b9496ff..2d0e17d8 100644 --- a/src/core/Darwin.zig +++ b/src/core/Darwin.zig @@ -23,7 +23,6 @@ pub const Darwin = @This(); allocator: std.mem.Allocator, core: *Core, -input_state: Core.InputState, title: [:0]const u8, display_mode: DisplayMode, vsync_mode: VSyncMode, @@ -109,7 +108,6 @@ pub fn init( darwin.* = .{ .allocator = options.allocator, .core = @fieldParentPtr("platform", darwin), - .input_state = .{}, .title = options.title, .display_mode = options.display_mode, .vsync_mode = .none, @@ -168,23 +166,3 @@ pub fn setCursorMode(_: *Darwin, _: CursorMode) void { pub fn setCursorShape(_: *Darwin, _: CursorShape) void { return; } - -pub fn keyPressed(_: *Darwin, _: Key) bool { - return false; -} - -pub fn keyReleased(_: *Darwin, _: Key) bool { - return true; -} - -pub fn mousePressed(_: *Darwin, _: MouseButton) bool { - return false; -} - -pub fn mouseReleased(_: *Darwin, _: MouseButton) bool { - return true; -} - -pub fn mousePosition(_: *Darwin) Position { - return Position{ .x = 0, .y = 0 }; -} diff --git a/src/core/Null.zig b/src/core/Null.zig index 81a644f1..aa753223 100644 --- a/src/core/Null.zig +++ b/src/core/Null.zig @@ -25,7 +25,6 @@ pub const Null = @This(); allocator: std.mem.Allocator, core: *Core, -input_state: Core.InputState, modifiers: KeyMods, title: [:0]u8, display_mode: DisplayMode, @@ -92,23 +91,3 @@ pub fn setCursorMode(_: *Null, _: CursorMode) void { pub fn setCursorShape(_: *Null, _: CursorShape) void { return; } - -pub fn keyPressed(_: *Null, _: Key) bool { - return false; -} - -pub fn keyReleased(_: *Null, _: Key) bool { - return true; -} - -pub fn mousePressed(_: *Null, _: MouseButton) bool { - return false; -} - -pub fn mouseReleased(_: *Null, _: MouseButton) bool { - return true; -} - -pub fn mousePosition(_: *Null) Position { - return Position{ .x = 0, .y = 0 }; -} diff --git a/src/core/Windows.zig b/src/core/Windows.zig index 5857ffc7..d22ccfea 100644 --- a/src/core/Windows.zig +++ b/src/core/Windows.zig @@ -45,8 +45,6 @@ dinput: *w.IDirectInput8W, saved_window_rect: w.RECT, surface_descriptor_from_hwnd: gpu.Surface.DescriptorFromWindowsHWND, state: *Core, -input_state: Core.InputState, -oom: std.Thread.ResetEvent = .{}, // ------------------------------ // Platform interface @@ -60,7 +58,6 @@ pub fn init( self.allocator = options.allocator; self.core = @fieldParentPtr("platform", self); self.size = options.size; - self.input_state = .{}; self.saved_window_rect = .{ .top = 0, .left = 0, .right = 0, .bottom = 0 }; const hInstance = w.GetModuleHandleW(null); @@ -153,7 +150,7 @@ pub fn update(self: *Win32) !void { pub fn setTitle(self: *Win32, title: [:0]const u8) void { const wtitle = std.unicode.utf8ToUtf16LeAllocZ(self.allocator, title) catch { - self.oom.set(); + self.state.oom.set(); return; }; defer self.allocator.free(wtitle); @@ -258,26 +255,6 @@ pub fn setCursorShape(self: *Win32, shape: CursorShape) void { self.cursor_shape = shape; } -pub fn keyPressed(self: *Win32, key: Key) bool { - return self.input_state.isKeyPressed(key); -} - -pub fn keyReleased(self: *Win32, key: Key) bool { - return self.input_state.isKeyReleased(key); -} - -pub fn mousePressed(self: *Win32, button: MouseButton) bool { - return self.input_state.isMouseButtonPressed(button); -} - -pub fn mouseReleased(self: *Win32, button: MouseButton) bool { - return self.input_state.isMouseButtonReleased(button); -} - -pub fn mousePosition(self: *Win32) Position { - return self.input_state.mouse_position; -} - pub fn nativeWindowWin32(self: *Win32) w.HWND { return self.window; } @@ -303,18 +280,6 @@ fn restoreWindowPosition(self: *Win32) void { } } -pub fn outOfMemory(self: *Win32) bool { - if (self.oom.isSet()) { - self.oom.reset(); - return true; - } - return false; -} - -fn pushEvent(self: *Win32, event: Event) void { - self.state.events.writeItem(event) catch self.oom.set(); -} - fn getKeyboardModifiers() mach.Core.KeyMods { return .{ .shift = w.GetKeyState(@as(i32, @intFromEnum(w.VK_SHIFT))) < 0, //& 0x8000 == 0x8000, @@ -336,7 +301,7 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w switch (msg) { w.WM_CLOSE => { - self.pushEvent(.close); + self.state.pushEvent(.close); return 0; }, w.WM_SIZE => { @@ -358,7 +323,7 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w if (vkey == w.VK_PROCESSKEY) return 0; if (msg == w.WM_SYSKEYDOWN and vkey == w.VK_F4) { - self.pushEvent(.close); + self.state.pushEvent(.close); return 0; } @@ -381,16 +346,26 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w const mods = getKeyboardModifiers(); const key = keyFromScancode(scancode); if (msg == w.WM_KEYDOWN or msg == w.WM_SYSKEYDOWN) { - if (flags & w.KF_REPEAT == 0) { - self.pushEvent(.{ .key_press = .{ .key = key, .mods = mods } }); - self.input_state.keys.setValue(@intFromEnum(key), true); - } else { - self.pushEvent(.{ .key_repeat = .{ .key = key, .mods = mods } }); - } - } else { - self.pushEvent(.{ .key_release = .{ .key = key, .mods = mods } }); - self.input_state.keys.setValue(@intFromEnum(key), false); - } + if (flags & w.KF_REPEAT == 0) + self.state.pushEvent(.{ + .key_press = .{ + .key = key, + .mods = mods, + }, + }) + else + self.state.pushEvent(.{ + .key_repeat = .{ + .key = key, + .mods = mods, + }, + }); + } else self.state.pushEvent(.{ + .key_release = .{ + .key = key, + .mods = mods, + }, + }); return 0; }, @@ -408,7 +383,7 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w } var iter = std.unicode.Utf16LeIterator.init(chars); if (iter.nextCodepoint()) |codepoint| { - self.pushEvent(.{ .char_input = .{ .codepoint = codepoint.? } }); + self.state.pushEvent(.{ .char_input = .{ .codepoint = codepoint.? } }); } else |err| { err catch {}; } @@ -439,14 +414,20 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w w.WM_MBUTTONDOWN, w.WM_RBUTTONDOWN, w.WM_XBUTTONDOWN, - => { - self.pushEvent(.{ .mouse_press = .{ .button = button, .mods = mods, .pos = .{ .x = x, .y = y } } }); - self.input_state.mouse_buttons.setValue(@intFromEnum(button), true); - }, - else => { - self.pushEvent(.{ .mouse_release = .{ .button = button, .mods = mods, .pos = .{ .x = x, .y = y } } }); - self.input_state.mouse_buttons.setValue(@intFromEnum(button), false); - }, + => self.state.pushEvent(.{ + .mouse_press = .{ + .button = button, + .mods = mods, + .pos = .{ .x = x, .y = y }, + }, + }), + else => self.state.pushEvent(.{ + .mouse_release = .{ + .button = button, + .mods = mods, + .pos = .{ .x = x, .y = y }, + }, + }), } return if (msg == w.WM_XBUTTONDOWN or msg == w.WM_XBUTTONUP) w.TRUE else 0; @@ -454,8 +435,7 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w w.WM_MOUSEMOVE => { const x: f64 = @floatFromInt(@as(i16, @truncate(lParam & 0xFFFF))); const y: f64 = @floatFromInt(@as(i16, @truncate((lParam >> 16) & 0xFFFF))); - - self.pushEvent(.{ + self.state.pushEvent(.{ .mouse_motion = .{ .pos = .{ .x = x, @@ -463,8 +443,6 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w }, }, }); - self.input_state.mouse_position = .{ .x = x, .y = y }; - return 0; }, w.WM_MOUSEWHEEL => { @@ -472,7 +450,7 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w const wheel_high_word: u16 = @truncate((wParam >> 16) & 0xffff); const delta_y: f32 = @as(f32, @floatFromInt(@as(i16, @bitCast(wheel_high_word)))) / WHEEL_DELTA; - self.pushEvent(.{ + self.state.pushEvent(.{ .mouse_scroll = .{ .xoffset = 0, .yoffset = delta_y, @@ -481,13 +459,11 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w return 0; }, w.WM_SETFOCUS => { - self.pushEvent(.{ .focus_gained = {} }); + self.state.pushEvent(.{ .focus_gained = {} }); return 0; }, w.WM_KILLFOCUS => { - self.pushEvent(.{ .focus_lost = {} }); - // Clear input state when focus is lost to avoid "stuck" button when focus is regained. - self.input_state = .{}; + self.state.pushEvent(.{ .focus_lost = {} }); return 0; }, else => return w.DefWindowProcW(wnd, msg, wParam, lParam),