core: windows: refactor everything
This commit is contained in:
parent
1e2cbc4d71
commit
f9e1a9087f
5 changed files with 821 additions and 203 deletions
|
|
@ -402,6 +402,7 @@ fn buildExamples(
|
|||
.root_source_file = b.path(b.fmt("examples/{s}/main.zig", .{example.name})),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.win32_manifest = b.path("src/core/windows/win32.manifest"),
|
||||
});
|
||||
exe.root_module.addImport("mach", mach_mod);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ const w = @import("../win32.zig");
|
|||
const mach = @import("../main.zig");
|
||||
const Core = @import("../Core.zig");
|
||||
|
||||
const windowmsg = @import("windowmsg.zig");
|
||||
|
||||
const gpu = mach.gpu;
|
||||
const Event = Core.Event;
|
||||
const KeyEvent = Core.KeyEvent;
|
||||
|
|
@ -17,20 +19,19 @@ const Position = Core.Position;
|
|||
const Key = Core.Key;
|
||||
const KeyMods = Core.KeyMods;
|
||||
|
||||
const log = std.log.scoped(.mach);
|
||||
const EventQueue = std.fifo.LinearFifo(Event, .Dynamic);
|
||||
const Win32 = @This();
|
||||
|
||||
pub const Native = struct {
|
||||
window: w.HWND = undefined,
|
||||
surrogate: u16 = 0,
|
||||
dinput: *w.IDirectInput8W = undefined,
|
||||
saved_window_rect: w.RECT = undefined,
|
||||
surface_descriptor_from_hwnd: gpu.Surface.DescriptorFromWindowsHWND = undefined,
|
||||
const window_ex_style: w.WINDOW_EX_STYLE = .{
|
||||
.APPWINDOW = 1,
|
||||
.NOREDIRECTIONBITMAP = 1,
|
||||
};
|
||||
|
||||
pub const Context = struct {
|
||||
core: *Core,
|
||||
window_id: mach.ObjectID,
|
||||
pub const Native = struct {
|
||||
hwnd: w.HWND,
|
||||
surrogate: u16 = 0,
|
||||
// dinput: *w.IDirectInput8W = undefined,
|
||||
};
|
||||
|
||||
pub fn run(comptime on_each_update_fn: anytype, args_tuple: std.meta.ArgsTuple(@TypeOf(on_each_update_fn))) void {
|
||||
|
|
@ -38,140 +39,250 @@ pub fn run(comptime on_each_update_fn: anytype, args_tuple: std.meta.ArgsTuple(@
|
|||
}
|
||||
|
||||
pub fn tick(core: *Core) !void {
|
||||
{
|
||||
var windows = core.windows.slice();
|
||||
while (windows.next()) |window_id| {
|
||||
const native_opt: ?Native = core.windows.get(window_id, .native);
|
||||
if (core.windows.get(window_id, .native) != null) {
|
||||
// TODO: propagate window.decorated and all others
|
||||
// Handle resizing the window when the user changes width or height
|
||||
if (core.windows.updated(window_id, .width) or core.windows.updated(window_id, .height)) {
|
||||
setWindowSize(
|
||||
core.windows.get(window_id, .native).?.hwnd,
|
||||
.{
|
||||
.width = core.windows.get(window_id, .width),
|
||||
.height = core.windows.get(window_id, .height),
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
try initWindow(core, window_id);
|
||||
std.debug.assert(core.windows.getValue(window_id).native != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (native_opt) |native| {
|
||||
_ = native; // autofix
|
||||
var msg: w.MSG = undefined;
|
||||
while (w.PeekMessageW(&msg, null, 0, 0, w.PM_REMOVE) != 0) {
|
||||
while (true) {
|
||||
const result = w.PeekMessageW(&msg, null, 0, 0, w.PM_REMOVE);
|
||||
if (result < 0) fatalWin32("PeekMessage", w.GetLastError());
|
||||
if (result == 0) break;
|
||||
if (msg.message == w.WM_QUIT) {
|
||||
std.log.info("quit (exit code {})", .{msg.wParam});
|
||||
w.ExitProcess(std.math.cast(u32, msg.wParam) orelse 0xffffffff);
|
||||
}
|
||||
_ = w.TranslateMessage(&msg);
|
||||
_ = w.DispatchMessageW(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle resizing the window when the user changes width or height
|
||||
if (core.windows.updated(window_id, .width) or core.windows.updated(window_id, .height)) {}
|
||||
} else {
|
||||
try initWindow(core, window_id);
|
||||
fn setWindowSize(hwnd: w.HWND, size_pt: Size) void {
|
||||
const dpi = w.dpiFromHwnd(hwnd);
|
||||
const style = styleFromHwnd(hwnd);
|
||||
var rect: w.RECT = .{
|
||||
.left = 0,
|
||||
.top = 0,
|
||||
.right = w.pxFromPt(i32, @intCast(size_pt.width), dpi),
|
||||
.bottom = w.pxFromPt(i32, @intCast(size_pt.height), dpi),
|
||||
};
|
||||
if (0 == w.AdjustWindowRectExForDpi(&rect, style, w.FALSE, window_ex_style, dpi)) fatalWin32(
|
||||
"AdjustWindowRectExForDpi",
|
||||
w.GetLastError(),
|
||||
);
|
||||
if (0 == w.SetWindowPos(
|
||||
hwnd,
|
||||
null,
|
||||
undefined,
|
||||
undefined,
|
||||
rect.right - rect.left,
|
||||
rect.bottom - rect.top,
|
||||
.{ .NOZORDER = 1, .NOMOVE = 1 },
|
||||
)) fatalWin32("SetWindowPos", w.GetLastError());
|
||||
}
|
||||
|
||||
fn updateWindowSize(
|
||||
dpi: u32,
|
||||
window_style: w.WINDOW_STYLE,
|
||||
hwnd: w.HWND,
|
||||
requested_client_size: w.SIZE,
|
||||
) void {
|
||||
const monitor = blk: {
|
||||
var rect: w.RECT = undefined;
|
||||
if (0 == w.GetWindowRect(hwnd, &rect)) fatalWin32("GetWindowRect", w.GetLastError());
|
||||
|
||||
break :blk w.MonitorFromPoint(
|
||||
.{ .x = rect.left, .y = rect.top },
|
||||
w.MONITOR_DEFAULTTONULL,
|
||||
) orelse {
|
||||
log.warn("MonitorFromPoint {},{} failed with {}", .{ rect.left, rect.top, w.GetLastError() });
|
||||
return;
|
||||
};
|
||||
};
|
||||
|
||||
const work_rect: w.RECT = blk: {
|
||||
var info: w.MONITORINFO = undefined;
|
||||
info.cbSize = @sizeOf(w.MONITORINFO);
|
||||
if (0 == w.GetMonitorInfoW(monitor, &info)) {
|
||||
log.warn("GetMonitorInfo failed with {}", .{w.GetLastError()});
|
||||
return;
|
||||
}
|
||||
break :blk info.rcWork;
|
||||
};
|
||||
|
||||
const work_size: w.SIZE = .{
|
||||
.cx = work_rect.right - work_rect.left,
|
||||
.cy = work_rect.bottom - work_rect.top,
|
||||
};
|
||||
log.debug(
|
||||
"primary monitor work topleft={},{} size={}x{}",
|
||||
.{ work_rect.left, work_rect.top, work_size.cx, work_size.cy },
|
||||
);
|
||||
|
||||
const wanted_size: w.SIZE = blk: {
|
||||
var rect: w.RECT = .{
|
||||
.left = 0,
|
||||
.top = 0,
|
||||
.right = requested_client_size.cx,
|
||||
.bottom = requested_client_size.cy,
|
||||
};
|
||||
if (0 == w.AdjustWindowRectExForDpi(&rect, window_style, w.FALSE, window_ex_style, dpi)) fatalWin32(
|
||||
"AdjustWindowRectExForDpi",
|
||||
w.GetLastError(),
|
||||
);
|
||||
break :blk .{
|
||||
.cx = rect.right - rect.left,
|
||||
.cy = rect.bottom - rect.top,
|
||||
};
|
||||
};
|
||||
|
||||
const window_size: w.SIZE = .{
|
||||
.cx = @min(wanted_size.cx, work_size.cx),
|
||||
.cy = @min(wanted_size.cy, work_size.cy),
|
||||
};
|
||||
if (0 == w.SetWindowPos(
|
||||
hwnd,
|
||||
null,
|
||||
work_rect.left + @divTrunc(work_size.cx - window_size.cx, 2),
|
||||
work_rect.top + @divTrunc(work_size.cy - window_size.cy, 2),
|
||||
window_size.cx,
|
||||
window_size.cy,
|
||||
.{ .NOZORDER = 1 },
|
||||
)) fatalWin32("SetWindowPos", w.GetLastError());
|
||||
}
|
||||
|
||||
const CreateWindowArgs = struct {
|
||||
window_id: mach.ObjectID,
|
||||
};
|
||||
|
||||
var wndproc_core: *Core = undefined;
|
||||
|
||||
fn initWindow(
|
||||
core: *Core,
|
||||
window_id: mach.ObjectID,
|
||||
) !void {
|
||||
wndproc_core = core;
|
||||
|
||||
var core_window = core.windows.getValue(window_id);
|
||||
|
||||
const hInstance = w.GetModuleHandleW(null);
|
||||
const class_name = w.L("mach");
|
||||
const class = std.mem.zeroInit(w.WNDCLASSW, .{
|
||||
.style = w.CS_OWNDC,
|
||||
{
|
||||
const class: w.WNDCLASSW = .{
|
||||
.style = .{},
|
||||
.lpfnWndProc = wndProc,
|
||||
.cbClsExtra = 0,
|
||||
.cbWndExtra = @sizeOf(mach.ObjectID),
|
||||
.hInstance = hInstance,
|
||||
.hIcon = w.LoadIconW(null, @as([*:0]align(1) const u16, @ptrFromInt(@as(u32, w.IDI_APPLICATION)))),
|
||||
.hCursor = w.LoadCursorW(null, @as([*:0]align(1) const u16, @ptrFromInt(@as(u32, w.IDC_ARROW)))),
|
||||
.hIcon = w.LoadIconW(null, w.IDI_APPLICATION),
|
||||
.hCursor = w.LoadCursorW(null, w.IDC_ARROW),
|
||||
.hbrBackground = null,
|
||||
.lpszMenuName = null,
|
||||
.lpszClassName = class_name,
|
||||
});
|
||||
if (w.RegisterClassW(&class) == 0) return error.Unexpected;
|
||||
};
|
||||
if (w.RegisterClassW(&class) == 0) fatalWin32("RegisterClass", w.GetLastError());
|
||||
}
|
||||
|
||||
const title = try std.unicode.utf8ToUtf16LeAllocZ(core.allocator, core_window.title);
|
||||
defer core.allocator.free(title);
|
||||
|
||||
var request_window_width: i32 = @bitCast(core_window.width);
|
||||
var request_window_height: i32 = @bitCast(core_window.height);
|
||||
|
||||
const window_ex_style: w.WINDOW_EX_STYLE = .{ .APPWINDOW = 1 };
|
||||
const window_style: w.WINDOW_STYLE = if (core_window.decorated) w.WS_OVERLAPPEDWINDOW else w.WS_POPUPWINDOW; // w.WINDOW_STYLE{.POPUP = 1};
|
||||
|
||||
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)) {
|
||||
request_window_width = rect.right - rect.left;
|
||||
request_window_height = rect.bottom - rect.top;
|
||||
}
|
||||
|
||||
const native_window = w.CreateWindowExW(
|
||||
const create_args: CreateWindowArgs = .{
|
||||
.window_id = window_id,
|
||||
};
|
||||
const hwnd = w.CreateWindowExW(
|
||||
window_ex_style,
|
||||
class_name,
|
||||
title,
|
||||
window_style,
|
||||
w.CW_USEDEFAULT,
|
||||
w.CW_USEDEFAULT,
|
||||
request_window_width,
|
||||
request_window_height,
|
||||
w.CW_USEDEFAULT,
|
||||
w.CW_USEDEFAULT,
|
||||
null,
|
||||
null,
|
||||
hInstance,
|
||||
null,
|
||||
@constCast(@ptrCast(&create_args)),
|
||||
) orelse return error.Unexpected;
|
||||
|
||||
var native: Native = .{};
|
||||
const dpi = w.dpiFromHwnd(hwnd);
|
||||
|
||||
var dinput: ?*w.IDirectInput8W = undefined;
|
||||
const ptr: ?*?*anyopaque = @ptrCast(&dinput);
|
||||
if (w.DirectInput8Create(hInstance, w.DIRECTINPUT_VERSION, w.IID_IDirectInput8W, ptr, null) != w.DI_OK) {
|
||||
return error.Unexpected;
|
||||
}
|
||||
native.dinput = dinput.?;
|
||||
updateWindowSize(dpi, window_style, hwnd, .{
|
||||
.cx = @bitCast(core_window.width),
|
||||
.cy = @bitCast(core_window.height),
|
||||
});
|
||||
|
||||
native.surface_descriptor_from_hwnd = .{
|
||||
.hinstance = std.os.windows.kernel32.GetModuleHandleW(null).?,
|
||||
.hwnd = native_window,
|
||||
};
|
||||
// const dinput = blk: {
|
||||
// var dinput: ?*w.IDirectInput8W = undefined;
|
||||
// const ptr: ?*?*anyopaque = @ptrCast(&dinput);
|
||||
// if (w.DirectInput8Create(hInstance, w.DIRECTINPUT_VERSION, w.IID_IDirectInput8W, ptr, null) != w.DI_OK) {
|
||||
// return error.Unexpected;
|
||||
// }
|
||||
// break :blk dinput;
|
||||
// };
|
||||
|
||||
core_window.surface_descriptor = .{ .next_in_chain = .{
|
||||
.from_windows_hwnd = &native.surface_descriptor_from_hwnd,
|
||||
} };
|
||||
|
||||
const context = try core.allocator.create(Context);
|
||||
context.* = .{ .core = core, .window_id = window_id };
|
||||
|
||||
_ = w.SetWindowLongPtrW(native_window, w.GWLP_USERDATA, @bitCast(@intFromPtr(context)));
|
||||
|
||||
restoreWindowPosition(core, window_id);
|
||||
|
||||
const size = getClientRect(core, window_id);
|
||||
const size = getClientSize(hwnd);
|
||||
core_window.width = size.width;
|
||||
core_window.height = size.height;
|
||||
|
||||
_ = w.GetWindowRect(native.window, &native.saved_window_rect);
|
||||
_ = w.ShowWindow(hwnd, w.SW_SHOW);
|
||||
|
||||
core_window.native = native;
|
||||
core.windows.setValueRaw(window_id, core_window);
|
||||
// try some things to bring our window to the top
|
||||
const HWND_TOP: ?w.HWND = null;
|
||||
_ = w.SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, .{ .NOMOVE = 1, .NOSIZE = 1 });
|
||||
_ = w.SetForegroundWindow(hwnd);
|
||||
_ = w.BringWindowToTop(hwnd);
|
||||
|
||||
{
|
||||
// TODO: make this lifetime better
|
||||
var surface_descriptor_from_hwnd: gpu.Surface.DescriptorFromWindowsHWND = .{
|
||||
.hinstance = std.os.windows.kernel32.GetModuleHandleW(null).?,
|
||||
.hwnd = hwnd,
|
||||
};
|
||||
core.windows.setRaw(window_id, .surface_descriptor, .{ .next_in_chain = .{
|
||||
.from_windows_hwnd = &surface_descriptor_from_hwnd,
|
||||
} });
|
||||
try core.initWindow(window_id);
|
||||
_ = w.ShowWindow(native_window, w.SW_SHOW);
|
||||
}
|
||||
}
|
||||
|
||||
fn windowIdFromHwnd(hwnd: w.HWND) mach.ObjectID {
|
||||
const userdata: usize = @bitCast(w.GetWindowLongPtrW(hwnd, @enumFromInt(0)));
|
||||
if (userdata == 0) unreachable;
|
||||
return @bitCast(userdata - 1);
|
||||
}
|
||||
fn styleFromHwnd(hwnd: w.HWND) w.WINDOW_STYLE {
|
||||
return @bitCast(@as(u32, @truncate(@as(usize, @bitCast(w.GetWindowLongPtrW(hwnd, w.GWL_EXSTYLE))))));
|
||||
}
|
||||
// -----------------------------
|
||||
// Internal functions
|
||||
// -----------------------------
|
||||
fn getClientRect(core: *Core, window_id: mach.ObjectID) Size {
|
||||
const window = core.windows.getValue(window_id);
|
||||
|
||||
if (window.native) |native| {
|
||||
fn getClientSize(hwnd: w.HWND) Size {
|
||||
var rect: w.RECT = undefined;
|
||||
_ = w.GetClientRect(native.window, &rect);
|
||||
|
||||
const width: u32 = @intCast(rect.right - rect.left);
|
||||
const height: u32 = @intCast(rect.bottom - rect.top);
|
||||
|
||||
return .{ .width = width, .height = height };
|
||||
}
|
||||
|
||||
return .{ .width = 0, .height = 0 };
|
||||
}
|
||||
|
||||
fn restoreWindowPosition(core: *Core, window_id: mach.ObjectID) void {
|
||||
const window = core.windows.getValue(window_id);
|
||||
if (window.native) |native| {
|
||||
if (native.saved_window_rect.right - native.saved_window_rect.left == 0) {
|
||||
_ = w.ShowWindow(native.window, w.SW_RESTORE);
|
||||
} else {
|
||||
_ = w.SetWindowPos(native.window, null, native.saved_window_rect.left, native.saved_window_rect.top, native.saved_window_rect.right - native.saved_window_rect.left, native.saved_window_rect.bottom - native.saved_window_rect.top, w.SWP_SHOWWINDOW);
|
||||
}
|
||||
}
|
||||
if (0 == w.GetClientRect(hwnd, &rect))
|
||||
fatalWin32("GetClientRect", w.GetLastError());
|
||||
std.debug.assert(rect.left == 0);
|
||||
std.debug.assert(rect.top == 0);
|
||||
return .{ .width = @intCast(rect.right), .height = @intCast(rect.bottom) };
|
||||
}
|
||||
|
||||
fn getKeyboardModifiers() mach.Core.KeyMods {
|
||||
|
|
@ -186,52 +297,79 @@ fn getKeyboardModifiers() mach.Core.KeyMods {
|
|||
};
|
||||
}
|
||||
|
||||
fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w.WINAPI) w.LRESULT {
|
||||
const context = blk: {
|
||||
const userdata: usize = @bitCast(w.GetWindowLongPtrW(wnd, w.GWLP_USERDATA));
|
||||
const ptr: ?*Context = @ptrFromInt(userdata);
|
||||
break :blk ptr orelse return w.DefWindowProcW(wnd, msg, wParam, lParam);
|
||||
const debug_wndproc_log = false;
|
||||
var global_msg_tail: ?*windowmsg.MessageNode = null;
|
||||
|
||||
fn wndProc(hwnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w.WINAPI) w.LRESULT {
|
||||
var msg_node: windowmsg.MessageNode = undefined;
|
||||
if (debug_wndproc_log) msg_node.init(&global_msg_tail, hwnd, msg, wParam, lParam);
|
||||
defer if (debug_wndproc_log) msg_node.deinit();
|
||||
if (debug_wndproc_log) switch (msg) {
|
||||
w.WM_MOUSEMOVE => {},
|
||||
else => std.log.info("{}", .{msg_node.fmtPath()}),
|
||||
};
|
||||
|
||||
const core = context.core;
|
||||
const window_id = context.window_id;
|
||||
|
||||
var core_window = core.windows.getValue(window_id);
|
||||
|
||||
const core = wndproc_core;
|
||||
switch (msg) {
|
||||
w.WM_CLOSE => {
|
||||
core.pushEvent(.{ .close = .{ .window_id = window_id } });
|
||||
w.WM_CREATE => {
|
||||
const create_struct: *w.CREATESTRUCTW = @ptrFromInt(@as(usize, @bitCast(lParam)));
|
||||
const create_args: *CreateWindowArgs = @alignCast(@ptrCast(create_struct.lpCreateParams));
|
||||
const window_id = create_args.window_id;
|
||||
|
||||
core.windows.setRaw(window_id, .native, .{ .hwnd = hwnd });
|
||||
// we add 1 to distinguish between a valid window id and an uninitialized slot
|
||||
std.debug.assert(0 == w.SetWindowLongPtrW(hwnd, @enumFromInt(0), @bitCast(create_args.window_id + 1)));
|
||||
std.debug.assert(create_args.window_id == windowIdFromHwnd(hwnd));
|
||||
return 0;
|
||||
},
|
||||
w.WM_SIZE => {
|
||||
const width: u32 = @as(u32, @intCast(lParam & 0xFFFF));
|
||||
const height: u32 = @as(u32, @intCast((lParam >> 16) & 0xFFFF));
|
||||
w.WM_DESTROY => @panic("Mach doesn't support destroying windows yet"),
|
||||
w.WM_CLOSE => {
|
||||
core.pushEvent(.{ .close = .{ .window_id = windowIdFromHwnd(hwnd) } });
|
||||
return 0;
|
||||
},
|
||||
w.WM_DPICHANGED, w.WM_WINDOWPOSCHANGED => {
|
||||
const client_size_px = getClientSize(hwnd);
|
||||
|
||||
if (core_window.width != width or core_window.height != height) {
|
||||
const window_id = windowIdFromHwnd(hwnd);
|
||||
var core_window = core.windows.getValue(window_id);
|
||||
|
||||
var change = false;
|
||||
if (core_window.framebuffer_width != client_size_px.width or core_window.framebuffer_height != client_size_px.height) {
|
||||
change = true;
|
||||
// Recreate the swap_chain
|
||||
core_window.swap_chain.release();
|
||||
core_window.swap_chain_descriptor.width = width;
|
||||
core_window.swap_chain_descriptor.height = height;
|
||||
core_window.swap_chain_descriptor.width = client_size_px.width;
|
||||
core_window.swap_chain_descriptor.height = client_size_px.height;
|
||||
core_window.swap_chain = core_window.device.createSwapChain(core_window.surface, &core_window.swap_chain_descriptor);
|
||||
|
||||
core_window.width = width;
|
||||
core_window.height = height;
|
||||
core_window.framebuffer_width = width;
|
||||
core_window.framebuffer_height = height;
|
||||
|
||||
core.pushEvent(.{ .window_resize = .{ .window_id = window_id, .size = .{ .width = width, .height = height } } });
|
||||
|
||||
core.windows.setValueRaw(window_id, core_window);
|
||||
core_window.framebuffer_width = client_size_px.width;
|
||||
core_window.framebuffer_height = client_size_px.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.
|
||||
const dpi = w.dpiFromHwnd(hwnd);
|
||||
const client_size_pt: Size = .{
|
||||
.width = w.ptFromPx(u32, client_size_px.width, dpi),
|
||||
.height = w.ptFromPx(u32, client_size_px.height, dpi),
|
||||
};
|
||||
if (core_window.width != client_size_pt.width or core_window.height != client_size_pt.height) {
|
||||
change = true;
|
||||
core_window.width = client_size_pt.width;
|
||||
core_window.height = client_size_pt.height;
|
||||
}
|
||||
|
||||
if (change) {
|
||||
core.pushEvent(.{ .window_resize = .{
|
||||
.window_id = window_id,
|
||||
.size = .{ .width = client_size_pt.width, .height = client_size_pt.height },
|
||||
} });
|
||||
core.windows.setValueRaw(window_id, core_window);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
w.WM_KEYDOWN, w.WM_KEYUP, w.WM_SYSKEYDOWN, w.WM_SYSKEYUP => {
|
||||
// ScanCode: Unique Identifier for a physical button.
|
||||
// Virtulkey: A key with a name, multiple physical buttons can produce the same virtual key.
|
||||
const window_id = windowIdFromHwnd(hwnd);
|
||||
|
||||
const vkey: w.VIRTUAL_KEY = @enumFromInt(wParam);
|
||||
if (vkey == w.VK_PROCESSKEY) return 0;
|
||||
|
||||
|
|
@ -241,15 +379,23 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w
|
|||
return 0;
|
||||
}
|
||||
|
||||
const flags = lParam >> 16;
|
||||
const scancode: u9 = @intCast(flags & 0x1FF);
|
||||
const WinKeyFlags = packed struct(u32) {
|
||||
repeat_count: u16,
|
||||
scancode: u8,
|
||||
extended: bool,
|
||||
reserved: u4,
|
||||
context: bool,
|
||||
previous: bool,
|
||||
transition: bool,
|
||||
};
|
||||
|
||||
const flags: WinKeyFlags = @bitCast(@as(u32, @truncate(@as(usize, @bitCast(lParam)))));
|
||||
const scancode: u9 = flags.scancode | (@as(u9, if (flags.extended) 1 else 0) << 8);
|
||||
if (scancode == 0x1D) {
|
||||
// right alt sends left control first
|
||||
var next: w.MSG = undefined;
|
||||
const time = w.GetMessageTime();
|
||||
if (core_window.native) |native| {
|
||||
if (w.PeekMessageW(&next, native.window, 0, 0, w.PM_NOREMOVE) != 0 and
|
||||
if (w.PeekMessageW(&next, hwnd, 0, 0, w.PM_NOREMOVE) != 0 and
|
||||
next.time == time and
|
||||
(next.message == msg or (msg == w.WM_SYSKEYDOWN and next.message == w.WM_KEYUP)) and
|
||||
((next.lParam >> 16) & 0x1FF) == 0x138)
|
||||
|
|
@ -257,22 +403,18 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mods = getKeyboardModifiers();
|
||||
const key = keyFromScancode(scancode);
|
||||
if (msg == w.WM_KEYDOWN or msg == w.WM_SYSKEYDOWN) {
|
||||
if (flags & w.KF_REPEAT == 0)
|
||||
core.pushEvent(.{
|
||||
.key_press = .{
|
||||
if (flags.previous) core.pushEvent(.{
|
||||
.key_repeat = .{
|
||||
.window_id = window_id,
|
||||
.key = key,
|
||||
.mods = mods,
|
||||
},
|
||||
})
|
||||
else
|
||||
core.pushEvent(.{
|
||||
.key_repeat = .{
|
||||
}) else core.pushEvent(.{
|
||||
.key_press = .{
|
||||
.window_id = window_id,
|
||||
.key = key,
|
||||
.mods = mods,
|
||||
|
|
@ -289,29 +431,32 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w
|
|||
return 0;
|
||||
},
|
||||
w.WM_CHAR => {
|
||||
if (core_window.native) |*native| {
|
||||
const char: u16 = @truncate(wParam);
|
||||
var chars: []const u16 = undefined;
|
||||
if (native.surrogate != 0) {
|
||||
chars = &.{ native.surrogate, char };
|
||||
native.surrogate = 0;
|
||||
} else if (std.unicode.utf16IsHighSurrogate(char)) {
|
||||
native.surrogate = char;
|
||||
const window_id = windowIdFromHwnd(hwnd);
|
||||
|
||||
const chars: [2]u16 = blk: {
|
||||
var native = core.windows.get(window_id, .native).?;
|
||||
defer core.windows.setRaw(window_id, .native, native);
|
||||
const chars = [2]u16{ native.surrogate, @truncate(wParam) };
|
||||
if (std.unicode.utf16IsHighSurrogate(chars[1])) {
|
||||
native.surrogate = chars[1];
|
||||
return 0;
|
||||
} else {
|
||||
chars = &.{char};
|
||||
}
|
||||
var iter = std.unicode.Utf16LeIterator.init(chars);
|
||||
if (iter.nextCodepoint()) |codepoint| {
|
||||
native.surrogate = 0;
|
||||
break :blk chars;
|
||||
};
|
||||
const codepoint: u21 = blk: {
|
||||
if (std.unicode.utf16IsHighSurrogate(chars[0])) {
|
||||
if (std.unicode.utf16DecodeSurrogatePair(&chars)) |c| break :blk c else |e| switch (e) {
|
||||
error.ExpectedSecondSurrogateHalf => {},
|
||||
}
|
||||
}
|
||||
break :blk chars[1];
|
||||
};
|
||||
core.pushEvent(.{ .char_input = .{
|
||||
.window_id = window_id,
|
||||
.codepoint = codepoint.?,
|
||||
.codepoint = codepoint,
|
||||
} });
|
||||
} else |err| {
|
||||
err catch {};
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
w.WM_LBUTTONDOWN,
|
||||
w.WM_LBUTTONUP,
|
||||
|
|
@ -322,15 +467,26 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w
|
|||
w.WM_XBUTTONDOWN,
|
||||
w.WM_XBUTTONUP,
|
||||
=> {
|
||||
const window_id = windowIdFromHwnd(hwnd);
|
||||
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 point = w.pointFromLparam(lParam);
|
||||
|
||||
const MouseFlags = packed struct(u8) {
|
||||
left_down: bool,
|
||||
right_down: bool,
|
||||
shift_down: bool,
|
||||
control_down: bool,
|
||||
middle_down: bool,
|
||||
xbutton1_down: bool,
|
||||
xbutton2_down: bool,
|
||||
_: bool,
|
||||
};
|
||||
const flags: MouseFlags = @bitCast(@as(u8, @truncate(wParam)));
|
||||
const button: MouseButton = switch (msg) {
|
||||
w.WM_LBUTTONDOWN, w.WM_LBUTTONUP => .left,
|
||||
w.WM_RBUTTONDOWN, w.WM_RBUTTONUP => .right,
|
||||
w.WM_MBUTTONDOWN, w.WM_MBUTTONUP => .middle,
|
||||
else => if (xbutton == @as(u32, @bitCast(w.XBUTTON1))) .four else .five,
|
||||
else => if (flags.xbutton1_down) .four else .five,
|
||||
};
|
||||
|
||||
switch (msg) {
|
||||
|
|
@ -343,7 +499,7 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w
|
|||
.window_id = window_id,
|
||||
.button = button,
|
||||
.mods = mods,
|
||||
.pos = .{ .x = x, .y = y },
|
||||
.pos = .{ .x = @floatFromInt(point.x), .y = @floatFromInt(point.y) },
|
||||
},
|
||||
}),
|
||||
else => core.pushEvent(.{
|
||||
|
|
@ -351,7 +507,7 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w
|
|||
.window_id = window_id,
|
||||
.button = button,
|
||||
.mods = mods,
|
||||
.pos = .{ .x = x, .y = y },
|
||||
.pos = .{ .x = @floatFromInt(point.x), .y = @floatFromInt(point.y) },
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
|
@ -359,20 +515,21 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w
|
|||
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 window_id = windowIdFromHwnd(hwnd);
|
||||
const point = w.pointFromLparam(lParam);
|
||||
core.pushEvent(.{
|
||||
.mouse_motion = .{
|
||||
.window_id = window_id,
|
||||
.pos = .{
|
||||
.x = x,
|
||||
.y = y,
|
||||
.x = @floatFromInt(point.x),
|
||||
.y = @floatFromInt(point.y),
|
||||
},
|
||||
},
|
||||
});
|
||||
return 0;
|
||||
},
|
||||
w.WM_MOUSEWHEEL => {
|
||||
const window_id = windowIdFromHwnd(hwnd);
|
||||
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;
|
||||
|
|
@ -387,17 +544,17 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w
|
|||
return 0;
|
||||
},
|
||||
w.WM_SETFOCUS => {
|
||||
const window_id = windowIdFromHwnd(hwnd);
|
||||
core.pushEvent(.{ .focus_gained = .{ .window_id = window_id } });
|
||||
return 0;
|
||||
},
|
||||
w.WM_KILLFOCUS => {
|
||||
const window_id = windowIdFromHwnd(hwnd);
|
||||
core.pushEvent(.{ .focus_lost = .{ .window_id = window_id } });
|
||||
return 0;
|
||||
},
|
||||
else => return w.DefWindowProcW(wnd, msg, wParam, lParam),
|
||||
else => return w.DefWindowProcW(hwnd, msg, wParam, lParam),
|
||||
}
|
||||
|
||||
return w.DefWindowProcW(wnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
fn keyFromScancode(scancode: u9) Key {
|
||||
|
|
@ -542,7 +699,10 @@ fn keyFromScancode(scancode: u9) Key {
|
|||
return if (scancode > 0 and scancode <= table.len) table[scancode - 1] else .unknown;
|
||||
}
|
||||
|
||||
fn fatalWin32(what: []const u8, last_error: u32) noreturn {
|
||||
std.debug.panic("{s} failed, error={}", .{ what, last_error });
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
|
|
|||
349
src/core/windowmsg.zig
Normal file
349
src/core/windowmsg.zig
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
const std = @import("std");
|
||||
const win32 = @import("../win32.zig");
|
||||
|
||||
pub fn pointFromLparam(lparam: win32.LPARAM) win32.POINT {
|
||||
return .{
|
||||
.x = @as(i16, @bitCast(win32.loword(lparam))),
|
||||
.y = @as(i16, @bitCast(win32.hiword(lparam))),
|
||||
};
|
||||
}
|
||||
|
||||
pub const MessageNode = struct {
|
||||
tail_ref: *?*MessageNode,
|
||||
hwnd: win32.HWND,
|
||||
msg: u32,
|
||||
wparam: win32.WPARAM,
|
||||
lparam: win32.LPARAM,
|
||||
old_tail: ?*MessageNode,
|
||||
pub fn init(
|
||||
self: *MessageNode,
|
||||
tail_ref: *?*MessageNode,
|
||||
hwnd: win32.HWND,
|
||||
msg: u32,
|
||||
wparam: win32.WPARAM,
|
||||
lparam: win32.LPARAM,
|
||||
) void {
|
||||
if (tail_ref.*) |old_tail| {
|
||||
std.debug.assert(old_tail.hwnd == hwnd);
|
||||
}
|
||||
self.* = .{
|
||||
.tail_ref = tail_ref,
|
||||
.hwnd = hwnd,
|
||||
.msg = msg,
|
||||
.wparam = wparam,
|
||||
.lparam = lparam,
|
||||
.old_tail = tail_ref.*,
|
||||
};
|
||||
tail_ref.* = self;
|
||||
}
|
||||
pub fn deinit(self: *MessageNode) void {
|
||||
std.debug.assert(self.tail_ref.* == self);
|
||||
self.tail_ref.* = self.old_tail;
|
||||
}
|
||||
pub fn fmtPath(self: *MessageNode) FmtPath {
|
||||
return .{ .node = self };
|
||||
}
|
||||
};
|
||||
|
||||
fn writeMessageNodePath(
|
||||
writer: anytype,
|
||||
node: *MessageNode,
|
||||
) !void {
|
||||
if (node.old_tail) |old_tail| {
|
||||
try writeMessageNodePath(writer, old_tail);
|
||||
try writer.writeAll(" > ");
|
||||
}
|
||||
try writer.print("{s}:{}", .{ msg_name(node.msg) orelse "?", node.msg });
|
||||
switch (node.msg) {
|
||||
win32.WM_CAPTURECHANGED => {
|
||||
try writer.print("({})", .{node.lparam});
|
||||
},
|
||||
win32.WM_SYSCOMMAND => {
|
||||
try writer.print("(type=0x{x})", .{0xfff0 & node.wparam});
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
const FmtPath = struct {
|
||||
node: *MessageNode,
|
||||
const Self = @This();
|
||||
pub fn format(
|
||||
self: Self,
|
||||
comptime fmt: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) @TypeOf(writer).Error!void {
|
||||
_ = fmt;
|
||||
_ = options;
|
||||
try writeMessageNodePath(writer, self.node);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn msg_name(msg: u32) ?[]const u8 {
|
||||
return switch (msg) {
|
||||
0 => "WM_NULL",
|
||||
1 => "WM_CREATE",
|
||||
2 => "WM_DESTROY",
|
||||
3 => "WM_MOVE",
|
||||
5 => "WM_SIZE",
|
||||
6 => "WM_ACTIVATE",
|
||||
7 => "WM_SETFOCUS",
|
||||
8 => "WM_KILLFOCUS",
|
||||
10 => "WM_ENABLE",
|
||||
11 => "WM_SETREDRAW",
|
||||
12 => "WM_SETTEXT",
|
||||
13 => "WM_GETTEXT",
|
||||
14 => "WM_GETTEXTLENGTH",
|
||||
15 => "WM_PAINT",
|
||||
16 => "WM_CLOSE",
|
||||
17 => "WM_QUERYENDSESSION",
|
||||
18 => "WM_QUIT",
|
||||
19 => "WM_QUERYOPEN",
|
||||
20 => "WM_ERASEBKGND",
|
||||
21 => "WM_SYSCOLORCHANGE",
|
||||
22 => "WM_ENDSESSION",
|
||||
24 => "WM_SHOWWINDOW",
|
||||
25 => "WM_CTLCOLOR",
|
||||
26 => "WM_WININICHANGE",
|
||||
27 => "WM_DEVMODECHANGE",
|
||||
28 => "WM_ACTIVATEAPP",
|
||||
29 => "WM_FONTCHANGE",
|
||||
30 => "WM_TIMECHANGE",
|
||||
31 => "WM_CANCELMODE",
|
||||
32 => "WM_SETCURSOR",
|
||||
33 => "WM_MOUSEACTIVATE",
|
||||
34 => "WM_CHILDACTIVATE",
|
||||
35 => "WM_QUEUESYNC",
|
||||
36 => "WM_GETMINMAXINFO",
|
||||
38 => "WM_PAINTICON",
|
||||
39 => "WM_ICONERASEBKGND",
|
||||
40 => "WM_NEXTDLGCTL",
|
||||
42 => "WM_SPOOLERSTATUS",
|
||||
43 => "WM_DRAWITEM",
|
||||
44 => "WM_MEASUREITEM",
|
||||
45 => "WM_DELETEITEM",
|
||||
46 => "WM_VKEYTOITEM",
|
||||
47 => "WM_CHARTOITEM",
|
||||
48 => "WM_SETFONT",
|
||||
49 => "WM_GETFONT",
|
||||
50 => "WM_SETHOTKEY",
|
||||
51 => "WM_GETHOTKEY",
|
||||
55 => "WM_QUERYDRAGICON",
|
||||
57 => "WM_COMPAREITEM",
|
||||
61 => "WM_GETOBJECT",
|
||||
65 => "WM_COMPACTING",
|
||||
68 => "WM_COMMNOTIFY",
|
||||
70 => "WM_WINDOWPOSCHANGING",
|
||||
71 => "WM_WINDOWPOSCHANGED",
|
||||
72 => "WM_POWER",
|
||||
73 => "WM_COPYGLOBALDATA",
|
||||
74 => "WM_COPYDATA",
|
||||
75 => "WM_CANCELJOURNAL",
|
||||
78 => "WM_NOTIFY",
|
||||
80 => "WM_INPUTLANGCHANGEREQUEST",
|
||||
81 => "WM_INPUTLANGCHANGE",
|
||||
82 => "WM_TCARD",
|
||||
83 => "WM_HELP",
|
||||
84 => "WM_USERCHANGED",
|
||||
85 => "WM_NOTIFYFORMAT",
|
||||
123 => "WM_CONTEXTMENU",
|
||||
124 => "WM_STYLECHANGING",
|
||||
125 => "WM_STYLECHANGED",
|
||||
126 => "WM_DISPLAYCHANGE",
|
||||
127 => "WM_GETICON",
|
||||
128 => "WM_SETICON",
|
||||
129 => "WM_NCCREATE",
|
||||
130 => "WM_NCDESTROY",
|
||||
131 => "WM_NCCALCSIZE",
|
||||
132 => "WM_NCHITTEST",
|
||||
133 => "WM_NCPAINT",
|
||||
134 => "WM_NCACTIVATE",
|
||||
135 => "WM_GETDLGCODE",
|
||||
136 => "WM_SYNCPAINT",
|
||||
160 => "WM_NCMOUSEMOVE",
|
||||
161 => "WM_NCLBUTTONDOWN",
|
||||
162 => "WM_NCLBUTTONUP",
|
||||
163 => "WM_NCLBUTTONDBLCLK",
|
||||
164 => "WM_NCRBUTTONDOWN",
|
||||
165 => "WM_NCRBUTTONUP",
|
||||
166 => "WM_NCRBUTTONDBLCLK",
|
||||
167 => "WM_NCMBUTTONDOWN",
|
||||
168 => "WM_NCMBUTTONUP",
|
||||
169 => "WM_NCMBUTTONDBLCLK",
|
||||
171 => "WM_NCXBUTTONDOWN",
|
||||
172 => "WM_NCXBUTTONUP",
|
||||
173 => "WM_NCXBUTTONDBLCLK",
|
||||
255 => "WM_INPUT",
|
||||
256 => "WM_KEYDOWN",
|
||||
257 => "WM_KEYUP",
|
||||
258 => "WM_CHAR",
|
||||
259 => "WM_DEADCHAR",
|
||||
260 => "WM_SYSKEYDOWN",
|
||||
261 => "WM_SYSKEYUP",
|
||||
262 => "WM_SYSCHAR",
|
||||
263 => "WM_SYSDEADCHAR",
|
||||
265 => "WM_UNICHAR",
|
||||
266 => "WM_CONVERTREQUEST",
|
||||
267 => "WM_CONVERTRESULT",
|
||||
268 => "WM_INTERIM",
|
||||
269 => "WM_IME_STARTCOMPOSITION",
|
||||
270 => "WM_IME_ENDCOMPOSITION",
|
||||
271 => "WM_IME_COMPOSITION",
|
||||
272 => "WM_INITDIALOG",
|
||||
273 => "WM_COMMAND",
|
||||
274 => "WM_SYSCOMMAND",
|
||||
275 => "WM_TIMER",
|
||||
276 => "WM_HSCROLL",
|
||||
277 => "WM_VSCROLL",
|
||||
278 => "WM_INITMENU",
|
||||
279 => "WM_INITMENUPOPUP",
|
||||
280 => "WM_SYSTIMER",
|
||||
287 => "WM_MENUSELECT",
|
||||
288 => "WM_MENUCHAR",
|
||||
289 => "WM_ENTERIDLE",
|
||||
290 => "WM_MENURBUTTONUP",
|
||||
291 => "WM_MENUDRAG",
|
||||
292 => "WM_MENUGETOBJECT",
|
||||
293 => "WM_UNINITMENUPOPUP",
|
||||
294 => "WM_MENUCOMMAND",
|
||||
295 => "WM_CHANGEUISTATE",
|
||||
296 => "WM_UPDATEUISTATE",
|
||||
297 => "WM_QUERYUISTATE",
|
||||
305 => "WM_LBTRACKPOINT",
|
||||
306 => "WM_CTLCOLORMSGBOX",
|
||||
307 => "WM_CTLCOLOREDIT",
|
||||
308 => "WM_CTLCOLORLISTBOX",
|
||||
309 => "WM_CTLCOLORBTN",
|
||||
310 => "WM_CTLCOLORDLG",
|
||||
311 => "WM_CTLCOLORSCROLLBAR",
|
||||
312 => "WM_CTLCOLORSTATIC",
|
||||
512 => "WM_MOUSEMOVE",
|
||||
513 => "WM_LBUTTONDOWN",
|
||||
514 => "WM_LBUTTONUP",
|
||||
515 => "WM_LBUTTONDBLCLK",
|
||||
516 => "WM_RBUTTONDOWN",
|
||||
517 => "WM_RBUTTONUP",
|
||||
518 => "WM_RBUTTONDBLCLK",
|
||||
519 => "WM_MBUTTONDOWN",
|
||||
520 => "WM_MBUTTONUP",
|
||||
521 => "WM_MBUTTONDBLCLK",
|
||||
522 => "WM_MOUSEWHEEL",
|
||||
523 => "WM_XBUTTONDOWN",
|
||||
524 => "WM_XBUTTONUP",
|
||||
525 => "WM_XBUTTONDBLCLK",
|
||||
526 => "WM_MOUSEHWHEEL",
|
||||
528 => "WM_PARENTNOTIFY",
|
||||
529 => "WM_ENTERMENULOOP",
|
||||
530 => "WM_EXITMENULOOP",
|
||||
531 => "WM_NEXTMENU",
|
||||
532 => "WM_SIZING",
|
||||
533 => "WM_CAPTURECHANGED",
|
||||
534 => "WM_MOVING",
|
||||
536 => "WM_POWERBROADCAST",
|
||||
537 => "WM_DEVICECHANGE",
|
||||
544 => "WM_MDICREATE",
|
||||
545 => "WM_MDIDESTROY",
|
||||
546 => "WM_MDIACTIVATE",
|
||||
547 => "WM_MDIRESTORE",
|
||||
548 => "WM_MDINEXT",
|
||||
549 => "WM_MDIMAXIMIZE",
|
||||
550 => "WM_MDITILE",
|
||||
551 => "WM_MDICASCADE",
|
||||
552 => "WM_MDIICONARRANGE",
|
||||
553 => "WM_MDIGETACTIVE",
|
||||
560 => "WM_MDISETMENU",
|
||||
561 => "WM_ENTERSIZEMOVE",
|
||||
562 => "WM_EXITSIZEMOVE",
|
||||
563 => "WM_DROPFILES",
|
||||
564 => "WM_MDIREFRESHMENU",
|
||||
640 => "WM_IME_REPORT",
|
||||
641 => "WM_IME_SETCONTEXT",
|
||||
642 => "WM_IME_NOTIFY",
|
||||
643 => "WM_IME_CONTROL",
|
||||
644 => "WM_IME_COMPOSITIONFULL",
|
||||
645 => "WM_IME_SELECT",
|
||||
646 => "WM_IME_CHAR",
|
||||
648 => "WM_IME_REQUEST",
|
||||
656 => "WM_IME_KEYDOWN",
|
||||
657 => "WM_IME_KEYUP",
|
||||
672 => "WM_NCMOUSEHOVER",
|
||||
673 => "WM_MOUSEHOVER",
|
||||
674 => "WM_NCMOUSELEAVE",
|
||||
675 => "WM_MOUSELEAVE",
|
||||
768 => "WM_CUT",
|
||||
769 => "WM_COPY",
|
||||
770 => "WM_PASTE",
|
||||
771 => "WM_CLEAR",
|
||||
772 => "WM_UNDO",
|
||||
773 => "WM_RENDERFORMAT",
|
||||
774 => "WM_RENDERALLFORMATS",
|
||||
775 => "WM_DESTROYCLIPBOARD",
|
||||
776 => "WM_DRAWCLIPBOARD",
|
||||
777 => "WM_PAINTCLIPBOARD",
|
||||
778 => "WM_VSCROLLCLIPBOARD",
|
||||
779 => "WM_SIZECLIPBOARD",
|
||||
780 => "WM_ASKCBFORMATNAME",
|
||||
781 => "WM_CHANGECBCHAIN",
|
||||
782 => "WM_HSCROLLCLIPBOARD",
|
||||
783 => "WM_QUERYNEWPALETTE",
|
||||
784 => "WM_PALETTEISCHANGING",
|
||||
785 => "WM_PALETTECHANGED",
|
||||
786 => "WM_HOTKEY",
|
||||
791 => "WM_PRINT",
|
||||
792 => "WM_PRINTCLIENT",
|
||||
793 => "WM_APPCOMMAND",
|
||||
799 => "WM_DWMNCRENDERINGCHANGED",
|
||||
856 => "WM_HANDHELDFIRST",
|
||||
863 => "WM_HANDHELDLAST",
|
||||
864 => "WM_AFXFIRST",
|
||||
895 => "WM_AFXLAST",
|
||||
896 => "WM_PENWINFIRST",
|
||||
897 => "WM_RCRESULT",
|
||||
898 => "WM_HOOKRCRESULT",
|
||||
899 => "WM_GLOBALRCCHANGE",
|
||||
900 => "WM_SKB",
|
||||
901 => "WM_PENCTL",
|
||||
902 => "WM_PENMISC",
|
||||
903 => "WM_CTLINIT",
|
||||
904 => "WM_PENEVENT",
|
||||
911 => "WM_PENWINLAST",
|
||||
1024 => "WM_USER+0",
|
||||
1025 => "WM_USER+1",
|
||||
1026 => "WM_USER+2",
|
||||
1027 => "WM_USER+3",
|
||||
1028 => "WM_USER+4",
|
||||
1029 => "WM_USER+5",
|
||||
1030 => "WM_USER+6",
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getHitName(hit: win32.LRESULT) ?[]const u8 {
|
||||
return switch (hit) {
|
||||
win32.HTERROR => "err",
|
||||
win32.HTTRANSPARENT => "transprnt",
|
||||
win32.HTNOWHERE => "nowhere",
|
||||
win32.HTCLIENT => "client",
|
||||
win32.HTCAPTION => "caption",
|
||||
win32.HTSYSMENU => "sysmnu",
|
||||
win32.HTSIZE => "size",
|
||||
win32.HTMENU => "menu",
|
||||
win32.HTHSCROLL => "hscroll",
|
||||
win32.HTVSCROLL => "vscroll",
|
||||
win32.HTMINBUTTON => "minbtn",
|
||||
win32.HTMAXBUTTON => "max",
|
||||
win32.HTLEFT => "left",
|
||||
win32.HTRIGHT => "right",
|
||||
win32.HTTOP => "top",
|
||||
win32.HTTOPLEFT => "topleft",
|
||||
win32.HTTOPRIGHT => "topright",
|
||||
win32.HTBOTTOM => "bottom",
|
||||
win32.HTBOTTOMLEFT => "botmleft",
|
||||
win32.HTBOTTOMRIGHT => "botmright",
|
||||
win32.HTBORDER => "border",
|
||||
win32.HTCLOSE => "close",
|
||||
win32.HTHELP => "help",
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
14
src/core/windows/win32.manifest
Normal file
14
src/core/windows/win32.manifest
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<asmv3:application>
|
||||
<asmv3:windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
</assembly>
|
||||
116
src/win32.zig
116
src/win32.zig
|
|
@ -27,6 +27,13 @@ pub const WINAPI = w.WINAPI;
|
|||
pub const TRUE = w.TRUE;
|
||||
pub const FALSE = w.FALSE;
|
||||
|
||||
pub const SIZE = extern struct {
|
||||
cx: i32,
|
||||
cy: i32,
|
||||
};
|
||||
|
||||
pub extern "kernel32" fn GetLastError() callconv(w.WINAPI) u32;
|
||||
|
||||
//pub const GetModuleHandleW = w.kernel32.GetModuleHandleW;
|
||||
// TODO: this type is limited to platform 'windows5.1.2600'
|
||||
pub extern "kernel32" fn GetModuleHandleW(
|
||||
|
|
@ -99,19 +106,12 @@ pub extern "user32" fn GetWindowRect(
|
|||
lpRect: ?*RECT,
|
||||
) callconv(@import("std").os.windows.WINAPI) BOOL;
|
||||
|
||||
// TODO: this type is limited to platform 'windows5.0'
|
||||
pub extern "user32" fn AdjustWindowRect(
|
||||
lpRect: ?*RECT,
|
||||
dwStyle: WINDOW_STYLE,
|
||||
bMenu: BOOL,
|
||||
) callconv(@import("std").os.windows.WINAPI) BOOL;
|
||||
|
||||
// TODO: this type is limited to platform 'windows5.0'
|
||||
pub extern "user32" fn AdjustWindowRectEx(
|
||||
pub extern "user32" fn AdjustWindowRectExForDpi(
|
||||
lpRect: ?*RECT,
|
||||
dwStyle: WINDOW_STYLE,
|
||||
bMenu: BOOL,
|
||||
dwExStyle: WINDOW_EX_STYLE,
|
||||
dpi: u32,
|
||||
) callconv(@import("std").os.windows.WINAPI) BOOL;
|
||||
|
||||
// TODO: this type is limited to platform 'windows5.0'
|
||||
|
|
@ -271,6 +271,14 @@ pub extern "user32" fn SetWindowLongW(
|
|||
dwNewLong: i32,
|
||||
) callconv(@import("std").os.windows.WINAPI) i32;
|
||||
|
||||
pub extern "user32" fn BringWindowToTop(
|
||||
hWnd: ?HWND,
|
||||
) callconv(@import("std").os.windows.WINAPI) BOOL;
|
||||
|
||||
pub extern "user32" fn SetForegroundWindow(
|
||||
hWnd: ?HWND,
|
||||
) callconv(@import("std").os.windows.WINAPI) BOOL;
|
||||
|
||||
//pub extern "user32" fn SetWindowPos(hWnd: HWND, hWndInsertAfter: HWND, X: i32, Y: i32, cx: i32, cy: i32, uFlags: u32) callconv(WINAPI) BOOL;
|
||||
pub extern "user32" fn SetWindowPos(
|
||||
hWnd: ?HWND,
|
||||
|
|
@ -1206,13 +1214,13 @@ pub const LPDIENUMDEVICEOBJECTSCALLBACKW = switch (@import("builtin").zig_backen
|
|||
) callconv(@import("std").os.windows.WINAPI) BOOL,
|
||||
};
|
||||
|
||||
pub const IDI_APPLICATION = 32512;
|
||||
pub const IDI_APPLICATION: [*:0]align(1) const u16 = @ptrFromInt(32512);
|
||||
pub extern "user32" fn LoadIconW(
|
||||
hInstance: ?HINSTANCE,
|
||||
lpIconName: ?[*:0]align(1) const u16,
|
||||
) callconv(@import("std").os.windows.WINAPI) ?HICON;
|
||||
|
||||
pub const IDC_ARROW = 32512;
|
||||
pub const IDC_ARROW: [*:0]align(1) const u16 = @ptrFromInt(32512);
|
||||
pub const IDC_HAND = 32649;
|
||||
pub const IDC_HELP = 32651;
|
||||
pub const IDC_IBEAM = 32513;
|
||||
|
|
@ -1636,6 +1644,7 @@ pub const WM_QUERYOPEN = @as(u32, 19);
|
|||
pub const WM_ENDSESSION = @as(u32, 22);
|
||||
pub const WM_QUIT = @as(u32, 18);
|
||||
pub const WM_GETMINMAXINFO = @as(u32, 36);
|
||||
pub const WM_WINDOWPOSCHANGED = @as(u32, 71);
|
||||
pub const WM_KEYFIRST = @as(u32, 256);
|
||||
pub const WM_KEYDOWN = @as(u32, 256);
|
||||
pub const WM_KEYUP = @as(u32, 257);
|
||||
|
|
@ -1712,6 +1721,7 @@ pub const WM_SIZING = @as(u32, 532);
|
|||
pub const WM_CAPTURECHANGED = @as(u32, 533);
|
||||
pub const WM_MOVING = @as(u32, 534);
|
||||
pub const WM_POWERBROADCAST = @as(u32, 536);
|
||||
pub const WM_DPICHANGED = @as(u32, 736);
|
||||
|
||||
pub const KEYBD_EVENT_FLAGS = packed struct(u32) {
|
||||
EXTENDEDKEY: u1 = 0,
|
||||
|
|
@ -2604,6 +2614,10 @@ pub extern "kernel32" fn GetProcAddress(
|
|||
hModule: ?*anyopaque,
|
||||
lpProcName: ?[*:0]const u8,
|
||||
) callconv(WINAPI) ?*const fn () callconv(WINAPI) isize;
|
||||
pub extern "kernel32" fn ExitProcess(
|
||||
uExitCode: u32,
|
||||
) callconv(@import("std").os.windows.WINAPI) noreturn;
|
||||
|
||||
pub const INFINITE = 4294967295;
|
||||
pub const SECURITY_ATTRIBUTES = extern struct {
|
||||
nLength: u32,
|
||||
|
|
@ -4157,3 +4171,83 @@ pub const AUDCLNT_E_HEADTRACKING_ENABLED = -2004287440;
|
|||
pub const AUDCLNT_E_HEADTRACKING_UNSUPPORTED = -2004287424;
|
||||
pub const AUDCLNT_E_EFFECT_NOT_AVAILABLE = -2004287423;
|
||||
pub const AUDCLNT_E_EFFECT_STATE_READ_ONLY = -2004287422;
|
||||
|
||||
pub extern "user32" fn GetDpiForWindow(
|
||||
hwnd: ?HWND,
|
||||
) callconv(@import("std").os.windows.WINAPI) u32;
|
||||
|
||||
pub fn dpiFromHwnd(hwnd: HWND) u32 {
|
||||
const value = GetDpiForWindow(hwnd);
|
||||
if (value == 0) std.debug.panic(
|
||||
"GetDpiForWindow failed, error={}",
|
||||
.{GetLastError()},
|
||||
);
|
||||
return value;
|
||||
}
|
||||
|
||||
fn getDpiFactor(dpi: u32) f32 {
|
||||
std.debug.assert(dpi >= 96);
|
||||
return @as(f32, @floatFromInt(dpi)) / 96.0;
|
||||
}
|
||||
|
||||
pub fn pxFromPt(comptime T: type, value: T, dpi: u32) T {
|
||||
switch (@typeInfo(T)) {
|
||||
.float => return value * getDpiFactor(dpi),
|
||||
.int => return @intFromFloat(@round(@as(f32, @floatFromInt(value)) * getDpiFactor(dpi))),
|
||||
else => @compileError("scaleDpi does not support type " ++ @typeName(@TypeOf(value))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ptFromPx(comptime T: type, value: T, dpi: u32) T {
|
||||
switch (@typeInfo(T)) {
|
||||
.float => return value / getDpiFactor(dpi),
|
||||
.int => return @intFromFloat(@round(@as(f32, @floatFromInt(value)) / getDpiFactor(dpi))),
|
||||
else => @compileError("scaleDpi does not support type " ++ @typeName(@TypeOf(value))),
|
||||
}
|
||||
}
|
||||
|
||||
pub const CREATESTRUCTW = extern struct {
|
||||
lpCreateParams: ?*anyopaque,
|
||||
hInstance: ?HINSTANCE,
|
||||
hMenu: ?HMENU,
|
||||
hwndParent: ?HWND,
|
||||
cy: i32,
|
||||
cx: i32,
|
||||
y: i32,
|
||||
x: i32,
|
||||
style: i32,
|
||||
lpszName: ?[*:0]const u16,
|
||||
lpszClass: ?[*:0]const u16,
|
||||
dwExStyle: u32,
|
||||
};
|
||||
|
||||
pub fn loword(value: anytype) u16 {
|
||||
switch (@typeInfo(@TypeOf(value))) {
|
||||
.int => |int| switch (int.signedness) {
|
||||
.signed => return loword(@as(@Type(.{ .int = .{ .signedness = .unsigned, .bits = int.bits } }), @bitCast(value))),
|
||||
.unsigned => return if (int.bits <= 16) value else @intCast(0xffff & value),
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
@compileError("unsupported type " ++ @typeName(@TypeOf(value)));
|
||||
}
|
||||
pub fn hiword(value: anytype) u16 {
|
||||
switch (@typeInfo(@TypeOf(value))) {
|
||||
.int => |int| switch (int.signedness) {
|
||||
.signed => return hiword(@as(@Type(.{ .int = .{ .signedness = .unsigned, .bits = int.bits } }), @bitCast(value))),
|
||||
.unsigned => return @intCast(0xffff & (value >> 16)),
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
@compileError("unsupported type " ++ @typeName(@TypeOf(value)));
|
||||
}
|
||||
|
||||
fn xFromLparam(lparam: LPARAM) i16 {
|
||||
return @bitCast(loword(lparam));
|
||||
}
|
||||
fn yFromLparam(lparam: LPARAM) i16 {
|
||||
return @bitCast(hiword(lparam));
|
||||
}
|
||||
pub fn pointFromLparam(lparam: LPARAM) POINT {
|
||||
return POINT{ .x = xFromLparam(lparam), .y = yFromLparam(lparam) };
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue