zigwin32/win32/zig.zig

164 lines
6.5 KiB
Zig

//! This module is maintained by hand and is copied to the generated code directory
const std = @import("std");
const builtin = @import("builtin");
const testing = std.testing;
const root = @import("root");
pub const UnicodeMode = enum { ansi, wide, unspecified };
// WORKAROUND: https://github.com/ziglang/zig/issues/7979
// using root.UNICODE causes an erroneous dependency loop, so I'm hardcoding to .wide for now
pub const unicode_mode = UnicodeMode.wide;
//pub const unicode_mode : UnicodeMode = if (@hasDecl(root, "UNICODE")) (if (root.UNICODE) .wide else .ansi) else .unspecified;
pub const L = std.unicode.utf8ToUtf16LeStringLiteral;
pub usingnamespace switch (unicode_mode) {
.ansi => struct {
pub const TCHAR = u8;
pub fn _T(comptime str: []const u8) *const [str.len:0]u8 {
return str;
}
},
.wide => struct {
pub const TCHAR = u16;
pub const _T = L;
},
.unspecified => if (builtin.is_test) struct {} else struct {
pub const TCHAR = @compileError("'TCHAR' requires that UNICODE be set to true or false in the root module");
pub const _T = @compileError("'_T' requires that UNICODE be set to true or false in the root module");
},
};
pub const Arch = enum { X86, X64, Arm64 };
pub const arch: Arch = switch (builtin.target.cpu.arch) {
.x86 => .X86,
.x86_64 => .X64,
.arm, .armeb, .aarch64 => .Arm64,
else => @compileError("unhandled arch " ++ @tagName(builtin.target.cpu.arch)),
};
// TODO: this should probably be in the standard lib somewhere?
pub const Guid = extern union {
Ints: extern struct {
a: u32,
b: u16,
c: u16,
d: [8]u8,
},
Bytes: [16]u8,
const big_endian_hex_offsets = [16]u6{ 0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34 };
const little_endian_hex_offsets = [16]u6{ 6, 4, 2, 0, 11, 9, 16, 14, 19, 21, 24, 26, 28, 30, 32, 34 };
const hex_offsets = switch (builtin.target.cpu.arch.endian()) {
.Big => big_endian_hex_offsets,
.Little => little_endian_hex_offsets,
};
pub fn initString(s: []const u8) Guid {
var guid = Guid{ .Bytes = undefined };
for (hex_offsets, 0..) |hex_offset, i| {
//guid.Bytes[i] = decodeHexByte(s[offset..offset+2]);
guid.Bytes[i] = decodeHexByte([2]u8{ s[hex_offset], s[hex_offset + 1] });
}
return guid;
}
};
comptime {
std.debug.assert(@sizeOf(Guid) == 16);
}
// TODO: is this in the standard lib somewhere?
fn hexVal(c: u8) u4 {
if (c <= '9') return @as(u4, @intCast(c - '0'));
if (c >= 'a') return @as(u4, @intCast(c + 10 - 'a'));
return @as(u4, @intCast(c + 10 - 'A'));
}
// TODO: is this in the standard lib somewhere?
fn decodeHexByte(hex: [2]u8) u8 {
return @as(u8, @intCast(hexVal(hex[0]))) << 4 | hexVal(hex[1]);
}
test "Guid" {
try testing.expect(std.mem.eql(u8, switch (builtin.target.cpu.arch.endian()) {
.Big => "\x01\x23\x45\x67\x89\xAB\xEF\x10\x32\x54\x76\x98\xba\xdc\xfe\x91",
.Little => "\x67\x45\x23\x01\xAB\x89\x10\xEF\x32\x54\x76\x98\xba\xdc\xfe\x91",
}, &Guid.initString("01234567-89AB-EF10-3254-7698badcfe91").Bytes));
}
pub const PropertyKey = extern struct {
fmtid: Guid,
pid: u32,
pub fn init(fmtid: []const u8, pid: u32) PropertyKey {
return .{
.fmtid = Guid.initString(fmtid),
.pid = pid,
};
}
};
pub fn FAILED(hr: @import("foundation.zig").HRESULT) bool {
return hr < 0;
}
pub fn SUCCEEDED(hr: @import("foundation.zig").HRESULT) bool {
return hr >= 0;
}
// These constants were removed from the metadata to allow each projection
// to define them however they like (see https://github.com/microsoft/win32metadata/issues/530)
pub const FALSE: @import("foundation.zig").BOOL = 0;
pub const TRUE: @import("foundation.zig").BOOL = 1;
/// Converts comptime values to the given type.
/// Note that this function is called at compile time rather than converting constant values earlier at code generation time.
/// The reason for doing it a compile time is because genzig.zig generates all constants as they are encountered which can
/// be before it knows the constant's type definition, so we delay the convession to compile-time where the compiler knows
/// all type definition.
pub fn typedConst(comptime T: type, comptime value: anytype) T {
return typedConst2(T, T, value);
}
pub fn typedConst2(comptime ReturnType: type, comptime SwitchType: type, comptime value: anytype) ReturnType {
const target_type_error = @as([]const u8, "typedConst cannot convert to " ++ @typeName(ReturnType));
const value_type_error = @as([]const u8, "typedConst cannot convert " ++ @typeName(@TypeOf(value)) ++ " to " ++ @typeName(ReturnType));
switch (@typeInfo(SwitchType)) {
.Int => |target_type_info| {
if (value >= std.math.maxInt(SwitchType)) {
if (target_type_info.signedness == .signed) {
const UnsignedT = @Type(std.builtin.Type{ .Int = .{ .signedness = .unsigned, .bits = target_type_info.bits } });
return @as(SwitchType, @bitCast(@as(UnsignedT, value)));
}
}
return value;
},
.Pointer => |target_type_info| switch (target_type_info.size) {
.One, .Many, .C => {
switch (@typeInfo(@TypeOf(value))) {
.ComptimeInt, .Int => {
const usize_value = if (value >= 0) value else @as(usize, @bitCast(@as(isize, value)));
return @as(ReturnType, @ptrFromInt(usize_value));
},
else => @compileError(value_type_error),
}
},
else => target_type_error,
},
.Optional => |target_type_info| switch (@typeInfo(target_type_info.child)) {
.Pointer => return typedConst2(ReturnType, target_type_info.child, value),
else => target_type_error,
},
.Enum => |_| switch (@typeInfo(@TypeOf(value))) {
.Int => return @as(ReturnType, @enumFromInt(value)),
else => target_type_error,
},
else => @compileError(target_type_error),
}
}
test "typedConst" {
try testing.expectEqual(@as(usize, @bitCast(@as(isize, -1))), @intFromPtr(typedConst(?*opaque {}, -1)));
try testing.expectEqual(@as(usize, @bitCast(@as(isize, -12))), @intFromPtr(typedConst(?*opaque {}, -12)));
try testing.expectEqual(@as(u32, 0xffffffff), typedConst(u32, 0xffffffff));
try testing.expectEqual(@as(i32, @bitCast(@as(u32, 0x80000000))), typedConst(i32, 0x80000000));
}