diff --git a/src/Core.zig b/src/Core.zig index 80db0dbddc..5e48156a5d 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -181,8 +181,8 @@ pub const InitOptions = struct { }; fn update(core: *Mod, entities: *mach.Entities.Mod) !void { - _ = core; // autofix - _ = entities; // autofix + _ = core; + _ = entities; } fn init(core: *Mod, entities: *mach.Entities.Mod) !void { @@ -517,6 +517,7 @@ pub const Key = enum { kp_8, kp_9, kp_decimal, + kp_comma, kp_equal, kp_enter, @@ -561,6 +562,9 @@ pub const Key = enum { slash, grave, + iso_backslash, + international1, + unknown, pub const max = Key.unknown; @@ -968,17 +972,14 @@ pub fn printTitle( comptime fmt: []const u8, args: anytype, ) !void { - _ = core; _ = window_id; - _ = fmt; - _ = args; - // TODO: NO OP - // // Free any previous window title slice - // if (core.get(window_id, .title)) |slice| core.state().allocator.free(slice); - - // // Allocate and assign a new window title slice. - // const slice = try std.fmt.allocPrintZ(core.state().allocator, fmt, args); - // try core.set(window_id, .title, slice); + // Allocate and assign a new window title slice. + const slice = try std.fmt.allocPrintZ(core.allocator, fmt, args); + defer core.allocator.free(slice); + core.setTitle(slice); + + // TODO: This function does not have access to *core.Mod to update + // try core.Mod.set(window_id, .title, slice); } fn exit(core: *Mod) void { @@ -1054,6 +1055,7 @@ pub fn deinitLinuxGamemode() void { comptime { // Core assertHasField(Platform, "surface_descriptor"); + assertHasField(Platform, "refresh_rate"); assertHasDecl(Platform, "init"); assertHasDecl(Platform, "deinit"); diff --git a/src/core/win32.zig b/src/core/win32.zig index cb93654244..7faadc594e 100644 --- a/src/core/win32.zig +++ b/src/core/win32.zig @@ -22,74 +22,39 @@ const Key = Core.Key; const KeyMods = Core.KeyMods; const Joystick = Core.Joystick; -const log = std.log.scoped(.mach); - const EventQueue = std.fifo.LinearFifo(Event, .Dynamic); const Win32 = @This(); -///////////////////////// +// -------------------------- // Module state -//////////////////////// +// -------------------------- allocator: std.mem.Allocator, core: *Core, // Core platform interface -title: [:0]u8, +surface_descriptor: gpu.Surface.Descriptor, display_mode: DisplayMode, vsync_mode: VSyncMode, cursor_mode: CursorMode, cursor_shape: CursorShape, border: bool, headless: bool, -refresh_rate: u32, size: Size, -surface_descriptor: gpu.Surface.Descriptor, -surface_descriptor_from_hwnd: gpu.Surface.DescriptorFromWindowsHWND, // Internals window: w.HWND, +refresh_rate: u32, surrogate: u16 = 0, dinput: *w.IDirectInput8W, - +saved_window_rect: w.RECT, +surface_descriptor_from_hwnd: gpu.Surface.DescriptorFromWindowsHWND, events: EventQueue, +input_state: InputState, oom: std.Thread.ResetEvent = .{}, -//////////////////////////// -/// Internals -//////////////////////////// - -const RequestAdapterResponse = struct { - status: gpu.RequestAdapterStatus, - adapter: ?*gpu.Adapter, - message: ?[*:0]const u8, -}; - -inline fn requestAdapterCallback( - context: *RequestAdapterResponse, - status: gpu.RequestAdapterStatus, - adapter: ?*gpu.Adapter, - message: ?[*:0]const u8, -) void { - context.* = RequestAdapterResponse{ - .status = status, - .adapter = adapter, - .message = message, - }; -} - -inline fn printUnhandledErrorCallback(_: void, ty: gpu.ErrorType, message: [*:0]const u8) void { - switch (ty) { - .validation => std.log.err("gpu: validation error: {s}\n", .{message}), - .out_of_memory => std.log.err("gpu: out of memory: {s}\n", .{message}), - .unknown => std.log.err("gpu: unknown error: {s}\n", .{message}), - else => unreachable, - } - std.process.exit(1); -} - -//////////////////////////// -/// Platform interface -//////////////////////////// +// ------------------------------ +// Platform interface +// ------------------------------ pub fn init( self: *Win32, options: InitOptions, @@ -98,6 +63,8 @@ pub fn init( self.core = @fieldParentPtr("platform", self); self.events = EventQueue.init(self.allocator); self.size = options.size; + self.input_state = .{}; + self.saved_window_rect = .{ .top = 0, .left = 0, .right = 0, .bottom = 0 }; const hInstance = w.GetModuleHandleW(null); const class_name = w.L("mach"); @@ -111,23 +78,19 @@ pub fn init( }); if (w.RegisterClassW(&class) == 0) return error.Unexpected; - // TODO set title , copy to self.title const title = try std.unicode.utf8ToUtf16LeAllocZ(self.allocator, options.title); defer self.allocator.free(title); - var request_window_width:i32 = @bitCast(self.size.width); - var request_window_height:i32 = @bitCast(self.size.height); + var request_window_width: i32 = @bitCast(self.size.width); + var request_window_height: i32 = @bitCast(self.size.height); - const window_ex_style: w.WINDOW_EX_STYLE = .{.APPWINDOW = 1, .WINDOWEDGE = 1, .CLIENTEDGE = 1}; - const window_style: w.WINDOW_STYLE = if (options.border) w.WS_OVERLAPPEDWINDOW else w.WS_POPUPWINDOW; + const window_ex_style: w.WINDOW_EX_STYLE = .{ .APPWINDOW = 1 }; + const window_style: w.WINDOW_STYLE = if (options.border) w.WS_OVERLAPPEDWINDOW else w.WS_POPUPWINDOW; // w.WINDOW_STYLE{.POPUP = 1}; + // TODO (win32): should border == false mean borderless display_mode? - var rect: w.RECT = .{ .left = 0, .top = 0, .right = request_window_width, .bottom = request_window_height}; + var rect: w.RECT = .{ .left = 0, .top = 0, .right = request_window_width, .bottom = request_window_height }; - if (w.TRUE == w.AdjustWindowRectEx(&rect, - window_style, - w.FALSE, - window_ex_style)) - { + if (w.TRUE == w.AdjustWindowRectEx(&rect, window_style, w.FALSE, window_ex_style)) { request_window_width = rect.right - rect.left; request_window_height = rect.bottom - rect.top; } @@ -157,32 +120,24 @@ pub fn init( self.dinput = dinput.?; self.surface_descriptor_from_hwnd = .{ - .hinstance = std.os.windows.kernel32.GetModuleHandleW(null).?, - .hwnd = window, - }; - - self.surface_descriptor = .{ - .next_in_chain = .{ - .from_windows_hwnd = &self.surface_descriptor_from_hwnd, - } + .hinstance = std.os.windows.kernel32.GetModuleHandleW(null).?, + .hwnd = window, }; + self.surface_descriptor = .{ .next_in_chain = .{ + .from_windows_hwnd = &self.surface_descriptor_from_hwnd, + } }; + self.border = options.border; + self.headless = options.headless; + self.refresh_rate = 60; // TODO (win32) get monitor refresh rate + _ = w.SetWindowLongPtrW(window, w.GWLP_USERDATA, @bitCast(@intFromPtr(self))); if (!options.headless) { setDisplayMode(self, options.display_mode); } self.size = getClientRect(self); -} - -pub fn getClientRect(self: *Win32) Size { - var rect: w.RECT = undefined; - _ = w.GetClientRect(self.window, &rect); - - const width:u32 = @intCast(rect.right - rect.left); - const height:u32 = @intCast(rect.bottom - rect.top); - - return .{ .width = width, .height = height }; + _ = w.GetWindowRect(self.window, &self.saved_window_rect); } pub fn deinit(self: *Win32) void { @@ -196,7 +151,7 @@ pub fn update(self: *Win32) !void { while (w.PeekMessageW(&msg, null, 0, 0, w.PM_REMOVE) != 0) { _ = w.TranslateMessage(&msg); _ = w.DispatchMessageW(&msg); - } + } } pub const EventIterator = struct { @@ -216,18 +171,57 @@ pub fn setTitle(self: *Win32, title: [:0]const u8) void { self.oom.set(); return; }; - // TODO update self.title defer self.allocator.free(wtitle); _ = w.SetWindowTextW(self.window, wtitle); } pub fn setDisplayMode(self: *Win32, mode: DisplayMode) void { - // TODO update self.displayMode + self.display_mode = mode; + switch (mode) { - .windowed => _ = w.ShowWindow(self.window, w.SW_RESTORE), - // .maximized => _ = w.ShowWindow(self.window, w.SW_MAXIMIZE), - .fullscreen => {}, - .borderless => {}, + .windowed => { + const window_style: w.WINDOW_STYLE = if (self.border) w.WS_OVERLAPPEDWINDOW else w.WS_POPUPWINDOW; + const window_ex_style = w.WINDOW_EX_STYLE{ .APPWINDOW = 1 }; + + _ = w.SetWindowLongW(self.window, w.GWL_STYLE, @bitCast(window_style)); + _ = w.SetWindowLongW(self.window, w.GWL_EXSTYLE, @bitCast(window_ex_style)); + + restoreWindowPosition(self); + }, + .fullscreen => { + // TODO (win32) - change to use exclusive fullscreen using ChangeDisplaySetting + + _ = w.GetWindowRect(self.window, &self.saved_window_rect); + + const window_style = w.WINDOW_STYLE{ .POPUP = 1, .VISIBLE = 1 }; + const window_ex_style = w.WINDOW_EX_STYLE{ .APPWINDOW = 1 }; + + _ = w.SetWindowLongW(self.window, w.GWL_STYLE, @bitCast(window_style)); + _ = w.SetWindowLongW(self.window, w.GWL_EXSTYLE, @bitCast(window_ex_style)); + + const monitor = w.MonitorFromWindow(self.window, w.MONITOR_DEFAULTTONEAREST); + var monitor_info: w.MONITORINFO = undefined; + monitor_info.cbSize = @sizeOf(w.MONITORINFO); + if (w.GetMonitorInfoW(monitor, &monitor_info) == w.TRUE) { + _ = w.SetWindowPos(self.window, null, monitor_info.rcMonitor.left, monitor_info.rcMonitor.top, monitor_info.rcMonitor.right - monitor_info.rcMonitor.left, monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top, w.SWP_NOZORDER); + } + }, + .borderless => { + _ = w.GetWindowRect(self.window, &self.saved_window_rect); + + const window_style = w.WINDOW_STYLE{ .POPUP = 1, .VISIBLE = 1 }; + const window_ex_style = w.WINDOW_EX_STYLE{ .APPWINDOW = 1 }; + + _ = w.SetWindowLongW(self.window, w.GWL_STYLE, @bitCast(window_style)); + _ = w.SetWindowLongW(self.window, w.GWL_EXSTYLE, @bitCast(window_ex_style)); + + const monitor = w.MonitorFromWindow(self.window, w.MONITOR_DEFAULTTONEAREST); + var monitor_info: w.MONITORINFO = undefined; + monitor_info.cbSize = @sizeOf(w.MONITORINFO); + if (w.GetMonitorInfoW(monitor, &monitor_info) == w.TRUE) { + _ = w.SetWindowPos(self.window, null, monitor_info.rcMonitor.left, monitor_info.rcMonitor.top, monitor_info.rcMonitor.right - monitor_info.rcMonitor.left, monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top, w.SWP_NOZORDER); + } + }, } } @@ -248,40 +242,10 @@ pub fn setVSync(self: *Win32, mode: VSyncMode) void { } pub fn setSize(self: *Win32, value: Size) void { - // TODO - use AdjustClientRect to get correct client rect. - _ = w.SetWindowPos(self.window, - null, - 0, - 0, - @as(i32, @intCast(value.width)), - @as(i32, @intCast(value.height)), - w.SET_WINDOW_POS_FLAGS{.NOMOVE = 1, .NOZORDER = 1, .NOACTIVATE = 1} - ); + // TODO (win32) - use AdjustClientRect to get correct client rect. + _ = w.SetWindowPos(self.window, null, 0, 0, @as(i32, @intCast(value.width)), @as(i32, @intCast(value.height)), w.SET_WINDOW_POS_FLAGS{ .NOMOVE = 1, .NOZORDER = 1, .NOACTIVATE = 1 }); self.size = value; } -// pub fn size(self: *Core) core.Size { -// var rect: w.RECT = undefined; -// _ = w.GetClientRect(self.window, &rect); - -// const width:u32 = @intCast(rect.right - rect.left); -// const height:u32 = @intCast(rect.bottom - rect.top); - -// return .{ .width = width, .height = height }; -// } - -// pub fn setSizeLimit(self: *Core, value: core.SizeLimit) void { -// self.mutex.lock(); -// defer self.mutex.unlock(); -// self.limits = value; -// // trigger WM_GETMINMAXINFO -// _ = w.SetWindowPos(self.window, 0, 0, 0, 0, 0, w.SWP_NOMOVE | w.SWP_NOSIZE | w.SWP_NOZORDER | w.SWP_NOACTIVATE); -// } - -// pub fn sizeLimit(self: *Core) core.SizeLimit { -// self.mutex.lock(); -// defer self.mutex.unlock(); -// return self.limits; -// } pub fn setCursorMode(self: *Win32, mode: CursorMode) void { switch (mode) { @@ -310,37 +274,61 @@ pub fn setCursorShape(self: *Win32, shape: CursorShape) void { } pub fn keyPressed(self: *Win32, key: Key) bool { - _ = self; - _ = key; - // TODO: Not implemented - return false; + return self.input_state.isKeyPressed(key); } pub fn keyReleased(self: *Win32, key: Key) bool { - _ = self; - _ = key; - // TODO: Not implemented - return false; + return self.input_state.isKeyReleased(key); } pub fn mousePressed(self: *Win32, button: MouseButton) bool { - _ = self; - _ = button; - // TODO: Not implemented - return false; + return self.input_state.isMouseButtonPressed(button); } pub fn mouseReleased(self: *Win32, button: MouseButton) bool { - _ = self; - _ = button; - // TODO: Not implemented - return false; + return self.input_state.isMouseButtonReleased(button); } pub fn mousePosition(self: *Win32) Position { - _ = self; - // TODO: Not implemented - return Position{.x = 0.0, .y = 0.0}; + return self.input_state.mouse_position; +} + +pub fn joystickPresent(_: *Win32, _: Joystick) bool { + @panic("NOT IMPLEMENTED"); +} +pub fn joystickName(_: *Win32, _: Joystick) ?[:0]const u8 { + @panic("NOT IMPLEMENTED"); +} +pub fn joystickButtons(_: *Win32, _: Joystick) ?[]const bool { + @panic("NOT IMPLEMENTED"); +} +// May be called from any thread. +pub fn joystickAxes(_: *Win32, _: Joystick) ?[]const f32 { + @panic("NOT IMPLEMENTED"); +} +pub fn nativeWindowWin32(self: *Win32) w.HWND { + return self.window; +} + +// ----------------------------- +// Internal functions +// ----------------------------- +fn getClientRect(self: *Win32) Size { + var rect: w.RECT = undefined; + _ = w.GetClientRect(self.window, &rect); + + const width: u32 = @intCast(rect.right - rect.left); + const height: u32 = @intCast(rect.bottom - rect.top); + + return .{ .width = width, .height = height }; +} + +fn restoreWindowPosition(self: *Win32) void { + if (self.saved_window_rect.right - self.saved_window_rect.left == 0) { + _ = w.ShowWindow(self.window, w.SW_RESTORE); + } else { + _ = w.SetWindowPos(self.window, null, self.saved_window_rect.left, self.saved_window_rect.top, self.saved_window_rect.right - self.saved_window_rect.left, self.saved_window_rect.bottom - self.saved_window_rect.top, w.SWP_SHOWWINDOW); + } } pub fn outOfMemory(self: *Win32) bool { @@ -355,6 +343,18 @@ fn pushEvent(self: *Win32, event: Event) void { self.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, + .control = w.GetKeyState(@as(i32, @intFromEnum(w.VK_CONTROL))) < 0, // & 0x8000 == 0x8000, + .alt = w.GetKeyState(@as(i32, @intFromEnum(w.VK_MENU))) < 0, // & 0x8000 == 0x8000, + .super = (w.GetKeyState(@as(i32, @intFromEnum(w.VK_LWIN)))) < 0 // & 0x8000 == 0x8000) + or (w.GetKeyState(@as(i32, @intFromEnum(w.VK_RWIN)))) < 0, // & 0x8000 == 0x8000), + .caps_lock = w.GetKeyState(@as(i32, @intFromEnum(w.VK_CAPITAL))) & 1 == 1, + .num_lock = w.GetKeyState(@as(i32, @intFromEnum(w.VK_NUMLOCK))) & 1 == 1, + }; +} + fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w.WINAPI) w.LRESULT { const self = blk: { const userdata: usize = @bitCast(w.GetWindowLongPtrW(wnd, w.GWLP_USERDATA)); @@ -367,19 +367,18 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w self.pushEvent(.close); return 0; }, - w.WM_GETMINMAXINFO => { - //self.mutex.lock(); - //defer self.mutex.unlock(); - - // TODO: SizeLimit is no longer in mach core or has changed - // const info: *w.MINMAXINFO = blk: { - // const int: usize = @bitCast(lParam); - // break :blk @ptrFromInt(int); - // }; - // if (self.limits.min.width) |width| info.ptMinTrackSize.x = @bitCast(width); - // if (self.limits.min.height) |height| info.ptMinTrackSize.y = @bitCast(height); - // if (self.limits.max.width) |width| info.ptMaxTrackSize.x = @bitCast(width); - // if (self.limits.max.height) |height| info.ptMaxTrackSize.y = @bitCast(height); + w.WM_SIZE => { + const width: u32 = @as(u32, @intCast(lParam & 0xFFFF)); + const height: u32 = @as(u32, @intCast((lParam >> 16) & 0xFFFF)); + self.size = .{ .width = width, .height = height }; + + // TODO (win32): only send resize event when sizing is done. + // the main mach loops does not run while resizing. + // Which means if events are pushed here they will + // queue up until resize is done. + + self.core.swap_chain_update.set(); + return 0; }, w.WM_KEYDOWN, w.WM_KEYUP, w.WM_SYSKEYDOWN, w.WM_SYSKEYUP => { @@ -407,15 +406,18 @@ 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 = undefined } }); + self.pushEvent(.{ .key_press = .{ .key = key, .mods = mods } }); + self.input_state.keys.setValue(@intFromEnum(key), true); } else { - self.pushEvent(.{ .key_repeat = .{ .key = key, .mods = undefined } }); + self.pushEvent(.{ .key_repeat = .{ .key = key, .mods = mods } }); } } else { - self.pushEvent(.{ .key_release = .{ .key = key, .mods = undefined } }); + self.pushEvent(.{ .key_release = .{ .key = key, .mods = mods } }); + self.input_state.keys.setValue(@intFromEnum(key), false); } return 0; @@ -449,8 +451,9 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w w.WM_XBUTTONDOWN, w.WM_XBUTTONUP, => { - const x:f64 = @floatFromInt(@as(i16, @truncate(lParam & 0xFFFF))); - const y:f64 = @floatFromInt(@as(i16, @truncate((lParam >> 16) & 0xFFFF))); + const mods = getKeyboardModifiers(); + const x: f64 = @floatFromInt(@as(i16, @truncate(lParam & 0xFFFF))); + const y: f64 = @floatFromInt(@as(i16, @truncate((lParam >> 16) & 0xFFFF))); const xbutton: u32 = @truncate(wParam >> 16); const button: MouseButton = switch (msg) { w.WM_LBUTTONDOWN, w.WM_LBUTTONUP => .left, @@ -464,15 +467,21 @@ 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 = undefined, .pos = .{.x = x, .y = y }}}), - else => self.pushEvent(.{ .mouse_release = .{ .button = button, .mods = undefined, .pos = .{.x = x, .y = y}}}), + => { + 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); + }, } return if (msg == w.WM_XBUTTONDOWN or msg == w.WM_XBUTTONUP) w.TRUE else 0; }, w.WM_MOUSEMOVE => { - const x:f64 = @floatFromInt(@as(i16, @truncate(lParam & 0xFFFF))); - const y:f64 = @floatFromInt(@as(i16, @truncate((lParam >> 16) & 0xFFFF))); + const x: f64 = @floatFromInt(@as(i16, @truncate(lParam & 0xFFFF))); + const y: f64 = @floatFromInt(@as(i16, @truncate((lParam >> 16) & 0xFFFF))); self.pushEvent(.{ .mouse_motion = .{ @@ -482,17 +491,33 @@ 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 => { + const WHEEL_DELTA = 120.0; + 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(.{ .mouse_scroll = .{ .xoffset = 0, - .yoffset = 0, + .yoffset = delta_y, }, }); return 0; }, + w.WM_SETFOCUS => { + self.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 = .{}; + return 0; + }, else => return w.DefWindowProcW(wnd, msg, wParam, lParam), } } @@ -584,9 +609,13 @@ fn keyFromScancode(scancode: u9) Key { 0x51 => .kp_3, 0x52 => .kp_0, 0x53 => .kp_decimal, + 0x54 => .print, // sysrq + 0x56 => .iso_backslash, //0x56 => .europe2, 0x57 => .f11, 0x58 => .f12, + 0x59 => .kp_equal, + 0x5B => .left_super, // sent by touchpad gestures //0x5C => .international6, 0x64 => .f13, 0x65 => .f14, @@ -602,17 +631,21 @@ fn keyFromScancode(scancode: u9) Key { //0x70 => .international2, //0x73 => .international1, //0x76 => .lang5, + 0x73 => .international1, + 0x76 => .f24, //0x77 => .lang4, //0x78 => .lang3, //0x79 => .international4, //0x7B => .international5, //0x7D => .international3, - //0x7E => .kp_comma, + 0x7E => .kp_comma, //0xF1 => .lang2, //0xF2 => .lang1, - //0x11C => .kp_enter, + 0x11C => .kp_enter, 0x11D => .right_control, - //0x135 => .kp_divide, + 0x135 => .kp_divide, + 0x136 => .right_shift, // sent by IME + 0x137 => .print, 0x138 => .right_alt, 0x145 => .num_lock, 0x146 => .pause, @@ -635,16 +668,7 @@ fn keyFromScancode(scancode: u9) Key { return if (scancode > 0 and scancode <= table.len) table[scancode - 1] else .unknown; } -pub fn joystickPresent(_: *Win32, _: Joystick) bool { - @panic("NOT IMPLEMENTED"); -} -pub fn joystickName(_: *Win32, _: Joystick) ?[:0]const u8 { - @panic("NOT IMPLEMENTED"); -} -pub fn joystickButtons(_: *Win32, _: Joystick) ?[]const bool { - @panic("NOT IMPLEMENTED"); -} -// May be called from any thread. -pub fn joystickAxes(_: *Win32, _: Joystick) ?[]const f32 { - @panic("NOT IMPLEMENTED"); -} +// TODO (win32) Implement consistent error handling when interfacing with the Windows API. +// TODO (win32) Support High DPI awareness +// TODO (win32) Consider to add support for mouse capture +// TODO (win32) Change to using WM_INPUT for mouse movement. diff --git a/src/core/win32/win32.zig b/src/core/win32/win32.zig index 75621caaec..1b64193632 100644 --- a/src/core/win32/win32.zig +++ b/src/core/win32/win32.zig @@ -21,6 +21,8 @@ pub const HICON = w.HICON; pub const HCURSOR = w.HCURSOR; pub const HBRUSH = w.HBRUSH; pub const HMENU = w.HMENU; +pub const HMONITOR = *opaque {}; +pub const HDC = w.HDC; pub const WINAPI = w.WINAPI; pub const TRUE = w.TRUE; pub const FALSE = w.FALSE; @@ -648,7 +650,6 @@ pub const IDirectInput8W = extern struct { pub usingnamespace MethodMixin(@This()); }; - pub const IID_IDirectInputDevice8W = GUID{ .Data1 = 1423184001, .Data2 = 56341, .Data3 = 18483, .Data4 = .{ 164, 27, 116, 143, 115, 163, 129, 121 } }; pub const IDirectInputDevice8W = extern struct { lpVtbl: *VTable, @@ -1346,11 +1347,11 @@ pub const WS_EX_WINDOWEDGE = WINDOW_EX_STYLE{ .WINDOWEDGE = 1 }; pub const WS_EX_CLIENTEDGE = WINDOW_EX_STYLE{ .CLIENTEDGE = 1 }; pub const WS_EX_CONTEXTHELP = WINDOW_EX_STYLE{ .CONTEXTHELP = 1 }; pub const WS_EX_RIGHT = WINDOW_EX_STYLE{ .RIGHT = 1 }; -pub const WS_EX_LEFT = WINDOW_EX_STYLE{ }; +pub const WS_EX_LEFT = WINDOW_EX_STYLE{}; pub const WS_EX_RTLREADING = WINDOW_EX_STYLE{ .RTLREADING = 1 }; -pub const WS_EX_LTRREADING = WINDOW_EX_STYLE{ }; +pub const WS_EX_LTRREADING = WINDOW_EX_STYLE{}; pub const WS_EX_LEFTSCROLLBAR = WINDOW_EX_STYLE{ .LEFTSCROLLBAR = 1 }; -pub const WS_EX_RIGHTSCROLLBAR = WINDOW_EX_STYLE{ }; +pub const WS_EX_RIGHTSCROLLBAR = WINDOW_EX_STYLE{}; pub const WS_EX_CONTROLPARENT = WINDOW_EX_STYLE{ .CONTROLPARENT = 1 }; pub const WS_EX_STATICEDGE = WINDOW_EX_STYLE{ .STATICEDGE = 1 }; pub const WS_EX_APPWINDOW = WINDOW_EX_STYLE{ .APPWINDOW = 1 }; @@ -2297,4 +2298,102 @@ pub const VK_PLAY = VIRTUAL_KEY.PLAY; pub const VK_ZOOM = VIRTUAL_KEY.ZOOM; pub const VK_NONAME = VIRTUAL_KEY.NONAME; pub const VK_PA1 = VIRTUAL_KEY.PA1; -pub const VK_OEM_CLEAR = VIRTUAL_KEY.OEM_CLEAR; \ No newline at end of file +pub const VK_OEM_CLEAR = VIRTUAL_KEY.OEM_CLEAR; + +// --------------------------- +// Keyboard and mouse +// --------------------------- +// TODO: this type is limited to platform 'windows5.0' +pub extern "user32" fn GetFocus() callconv(@import("std").os.windows.WINAPI) ?HWND; + +// TODO: this type is limited to platform 'windows5.0' +pub extern "user32" fn GetKBCodePage() callconv(@import("std").os.windows.WINAPI) u32; + +// TODO: this type is limited to platform 'windows5.0' +pub extern "user32" fn GetKeyState( + nVirtKey: i32, +) callconv(@import("std").os.windows.WINAPI) i16; + +// TODO: this type is limited to platform 'windows5.0' +pub extern "user32" fn GetAsyncKeyState( + vKey: i32, +) callconv(@import("std").os.windows.WINAPI) i16; + +// TODO: this type is limited to platform 'windows5.0' +pub extern "user32" fn GetKeyboardState( + lpKeyState: *[256]u8, +) callconv(@import("std").os.windows.WINAPI) BOOL; + +pub extern "user32" fn GetCapture() callconv(@import("std").os.windows.WINAPI) ?HWND; + +// TODO: this type is limited to platform 'windows5.0' +pub extern "user32" fn SetCapture( + hWnd: ?HWND, +) callconv(@import("std").os.windows.WINAPI) ?HWND; + +// TODO: this type is limited to platform 'windows5.0' +pub extern "user32" fn ReleaseCapture() callconv(@import("std").os.windows.WINAPI) BOOL; + +// -------------------------- +// GDI +// -------------------------- +pub const MONITORENUMPROC = *const fn ( + param0: ?HMONITOR, + param1: ?HDC, + param2: ?*RECT, + param3: LPARAM, +) callconv(@import("std").os.windows.WINAPI) BOOL; + +pub const MONITORINFO = extern struct { + cbSize: u32, + rcMonitor: RECT, + rcWork: RECT, + dwFlags: u32, +}; + +pub const MONITOR_FROM_FLAGS = enum(u32) { + NEAREST = 2, + NULL = 0, + PRIMARY = 1, +}; +pub const MONITOR_DEFAULTTONEAREST = MONITOR_FROM_FLAGS.NEAREST; +pub const MONITOR_DEFAULTTONULL = MONITOR_FROM_FLAGS.NULL; +pub const MONITOR_DEFAULTTOPRIMARY = MONITOR_FROM_FLAGS.PRIMARY; + +// TODO: this type is limited to platform 'windows5.0' +pub extern "user32" fn MonitorFromPoint( + pt: POINT, + dwFlags: MONITOR_FROM_FLAGS, +) callconv(@import("std").os.windows.WINAPI) ?HMONITOR; + +// TODO: this type is limited to platform 'windows5.0' +pub extern "user32" fn MonitorFromRect( + lprc: ?*RECT, + dwFlags: MONITOR_FROM_FLAGS, +) callconv(@import("std").os.windows.WINAPI) ?HMONITOR; + +// TODO: this type is limited to platform 'windows5.0' +pub extern "user32" fn MonitorFromWindow( + hwnd: ?HWND, + dwFlags: MONITOR_FROM_FLAGS, +) callconv(@import("std").os.windows.WINAPI) ?HMONITOR; + +// TODO: this type is limited to platform 'windows5.0' +pub extern "user32" fn GetMonitorInfoA( + hMonitor: ?HMONITOR, + lpmi: ?*MONITORINFO, +) callconv(@import("std").os.windows.WINAPI) BOOL; + +// TODO: this type is limited to platform 'windows5.0' +pub extern "user32" fn GetMonitorInfoW( + hMonitor: ?HMONITOR, + lpmi: ?*MONITORINFO, +) callconv(@import("std").os.windows.WINAPI) BOOL; + +// TODO: this type is limited to platform 'windows5.0' +pub extern "user32" fn EnumDisplayMonitors( + hdc: ?HDC, + lprcClip: ?*RECT, + lpfnEnum: ?MONITORENUMPROC, + dwData: LPARAM, +) callconv(@import("std").os.windows.WINAPI) BOOL; diff --git a/src/gfx/Sprite.zig b/src/gfx/Sprite.zig index 879174dc51..f955a1c0de 100644 --- a/src/gfx/Sprite.zig +++ b/src/gfx/Sprite.zig @@ -110,7 +110,7 @@ fn updatePipeline( // TODO(d3d12): #1217 // changed the uv_transform to 4x4. The 3x3 causes issues with d3d12/hlsl transforms: []Mat4x4, - uv_transforms: []Mat4x4, + uv_transforms: []Mat4x4, sizes: []Vec2, pub fn lessThan(ctx: @This(), a: usize, b: usize) bool { diff --git a/src/sysgpu/d3d12.zig b/src/sysgpu/d3d12.zig index f4f8c96e98..35c571ae4a 100644 --- a/src/sysgpu/d3d12.zig +++ b/src/sysgpu/d3d12.zig @@ -86,13 +86,9 @@ pub const Instance = struct { ); if (hr == c.DXGI_ERROR_INVALID_CALL) { const hr_prev = hr; - hr = c.CreateDXGIFactory2( - 0, - &c.IID_IDXGIFactory4, - @ptrCast(&dxgi_factory)); + hr = c.CreateDXGIFactory2(0, &c.IID_IDXGIFactory4, @ptrCast(&dxgi_factory)); if (hr == c.S_OK) { - log.info("note: D3D12 debug layers disabled (couldn't enable, error: {x}), see https://machengine.org/about/faq/#how-to-enable-direct3d-debug-layers", - .{@as(u32, @bitCast(hr_prev))}); + log.info("note: D3D12 debug layers disabled (couldn't enable, error: {x}), see https://machengine.org/about/faq/#how-to-enable-direct3d-debug-layers", .{@as(u32, @bitCast(hr_prev))}); } else { return error.CreateDXGIFactoryFailed; } @@ -209,8 +205,8 @@ pub const Adapter = struct { description[l] = 0; if ((dxgi_desc.Flags & c.DXGI_ADAPTER_FLAG_SOFTWARE) != 0) { - _ = dxgi_adapter.lpVtbl.*.Release.?(dxgi_adapter); - continue; + _ = dxgi_adapter.lpVtbl.*.Release.?(dxgi_adapter); + continue; } const adapter_type: sysgpu.Adapter.Type = blk: { @@ -231,7 +227,7 @@ pub const Adapter = struct { } else { break :blk .integrated_gpu; } - } + } break :blk .unknown; }; @@ -245,11 +241,10 @@ pub const Adapter = struct { // Select the last discrete_gpu if power preference is high performance. // Select the last integrated_gpu if power preference is not high performance. // TOOD: Other selection criterias? - if ((options.power_preference == .high_performance and adapter_type == .discrete_gpu) - or (options.power_preference == .low_power and adapter_type != .discrete_gpu)) { + if ((options.power_preference == .high_performance and adapter_type == .discrete_gpu) or (options.power_preference == .low_power and adapter_type != .discrete_gpu)) { if (last_dxgi_adapter) |adapter| { _ = adapter.lpVtbl.*.Release.?(adapter); - } + } last_dxgi_adapter = dxgi_adapter; last_dxgi_desc = dxgi_desc; last_adapter_type = adapter_type; @@ -281,7 +276,6 @@ pub const Adapter = struct { } } - return error.NoAdapterFound; } @@ -298,7 +292,7 @@ pub const Adapter = struct { } pub fn getProperties(adapter: *Adapter) sysgpu.Adapter.Properties { - const dxgi_desc = adapter.dxgi_desc; + const dxgi_desc = adapter.dxgi_desc; return .{ .vendor_id = dxgi_desc.VendorId, .vendor_name = "", // TODO @@ -478,7 +472,6 @@ pub const Device = struct { try device.mem_allocator_textures.init(device); - return device; } @@ -768,7 +761,7 @@ pub const MemoryAllocator = struct { .rtv_dsv_texture => c.D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES, .other_texture => c.D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES, }, - }; + }; var heap: ?*c.ID3D12Heap = null; const d3d_device = group.owning_pool.device.d3d_device; const hr = d3d_device.lpVtbl.*.CreateHeap.?( @@ -1428,7 +1421,7 @@ pub const SwapChain = struct { .SampleDesc = .{ .Count = 1, .Quality = 0 }, .BufferUsage = conv.dxgiUsage(desc.usage), .BufferCount = back_buffer_count, - .Scaling = c.DXGI_MODE_SCALING_UNSPECIFIED, + .Scaling = c.DXGI_SCALING_STRETCH, .SwapEffect = c.DXGI_SWAP_EFFECT_FLIP_DISCARD, .AlphaMode = c.DXGI_ALPHA_MODE_UNSPECIFIED, .Flags = if (instance.allow_tearing) c.DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING else 0, @@ -1747,7 +1740,7 @@ pub const Texture = struct { &clear_value else null, - // TODO: #1225: check if different textures need different resource categories. + // TODO: #1225: check if different textures need different resource categories. .resource_category = .other_texture, .initial_state = initial_state, }; diff --git a/src/sysgpu/shader/codegen/hlsl.zig b/src/sysgpu/shader/codegen/hlsl.zig index af0212085c..d359f23d4e 100644 --- a/src/sysgpu/shader/codegen/hlsl.zig +++ b/src/sysgpu/shader/codegen/hlsl.zig @@ -936,7 +936,7 @@ fn emitBinary(hlsl: *Hlsl, inst: Inst.Binary) !void { const rhs_type = hlsl.air.getInst(inst.rhs_type); if (lhs_type == .matrix or rhs_type == .matrix) { - // TODO(d3d12): + // TODO(d3d12): // Changed to column major storage because dxc does not apply same ordering to Storage Buffers. // matrices are in column major storage try hlsl.writeAll("mul");