├── .gitattributes
├── .gitignore
├── 0.13.0
├── build.zig
├── build.zig.zon
└── common.zig
├── ComOverloads.txt
├── README.md
├── build.zig
├── build.zig.zon
├── examples
├── basewin.zig
├── d2dcircle.zig
├── helloworld-window.zig
├── helloworld.zig
├── net.zig
├── opendialog.zig
├── tests.zig
├── testwindow.zig
├── unionpointers.zig
├── wasapi.zig
└── win32.manifest
├── extra.txt
├── src
├── StringPool.zig
├── cameltosnake.zig
├── common.zig
├── diff.zig
├── extra.zig
├── genzig.zig
├── genzigexports.zig
├── handletypes.zig
├── jsonextra.zig
├── metadata.zig
├── pass1.zig
├── pass1data.zig
└── static
│ ├── .gitignore
│ ├── LICENSE
│ ├── README.md
│ ├── build.zig
│ ├── win32.zig
│ ├── win32
│ ├── windowlongptr.zig
│ └── zig.zig
│ └── zig.mod
├── test
├── badcomoverload.zig
└── comoverload.zig
└── update-merged-branch
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.zig text eol=lf
2 | *.zon text eol=lf
3 | *.txt text eol=lf
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .zig-cache/
2 | zig-out/
3 | /diffrepo/
4 |
--------------------------------------------------------------------------------
/0.13.0/build.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const common = @import("common.zig");
3 |
4 | pub fn build(b: *std.Build) !void {
5 | const optimize = b.standardOptimizeOption(.{});
6 | // NOTE: currently depends on ensuring the bindings
7 | // are already generated/installed from build.zig
8 | const win32 = b.dependency("win32", .{}).module("win32");
9 | common.addExamples(b, optimize, win32, b.dependency("examples", .{}).path("."));
10 | }
11 |
--------------------------------------------------------------------------------
/0.13.0/build.zig.zon:
--------------------------------------------------------------------------------
1 | .{
2 | .name = "zigwin32",
3 | .version = "0.0.0",
4 | .minimum_zig_version = "0.13.0",
5 | .dependencies = .{
6 | .win32 = .{
7 | .path = "../zig-out",
8 | },
9 | .examples = .{
10 | .path = "../examples",
11 | },
12 | },
13 | .paths = .{"."},
14 | }
15 |
--------------------------------------------------------------------------------
/0.13.0/common.zig:
--------------------------------------------------------------------------------
1 | const builtin = @import("builtin");
2 | const std = @import("std");
3 |
4 | pub fn addExamples(
5 | b: *std.Build,
6 | optimize: std.builtin.Mode,
7 | win32: *std.Build.Module,
8 | examples: std.Build.LazyPath,
9 | ) void {
10 | const arches: []const ?[]const u8 = &[_]?[]const u8{
11 | null,
12 | "x86",
13 | "x86_64",
14 | "aarch64",
15 | };
16 | const examples_step = b.step("examples", "Build/run examples. Use -j1 to run one at a time");
17 |
18 | try addExample(b, arches, optimize, win32, examples, "helloworld", .Console, examples_step);
19 | try addExample(b, arches, optimize, win32, examples, "wasapi", .Console, examples_step);
20 | try addExample(b, arches, optimize, win32, examples, "net", .Console, examples_step);
21 | try addExample(b, arches, optimize, win32, examples, "tests", .Console, examples_step);
22 |
23 | try addExample(b, arches, optimize, win32, examples, "helloworld-window", .Windows, examples_step);
24 | try addExample(b, arches, optimize, win32, examples, "d2dcircle", .Windows, examples_step);
25 | try addExample(b, arches, optimize, win32, examples, "opendialog", .Windows, examples_step);
26 | try addExample(b, arches, optimize, win32, examples, "unionpointers", .Windows, examples_step);
27 | try addExample(b, arches, optimize, win32, examples, "testwindow", .Windows, examples_step);
28 | }
29 |
30 | fn addExample(
31 | b: *std.Build,
32 | arches: []const ?[]const u8,
33 | optimize: std.builtin.Mode,
34 | win32: *std.Build.Module,
35 | examples: std.Build.LazyPath,
36 | root: []const u8,
37 | subsystem: std.Target.SubSystem,
38 | examples_step: *std.Build.Step,
39 | ) !void {
40 | const basename = b.fmt("{s}.zig", .{root});
41 | for (arches) |cross_arch_opt| {
42 | const name = if (cross_arch_opt) |arch| b.fmt("{s}-{s}", .{ root, arch }) else root;
43 |
44 | const arch_os_abi = if (cross_arch_opt) |arch| b.fmt("{s}-windows", .{arch}) else "native";
45 | const target_query = std.Target.Query.parse(.{ .arch_os_abi = arch_os_abi }) catch unreachable;
46 | const target = b.resolveTargetQuery(target_query);
47 | const exe = b.addExecutable(.{
48 | .name = name,
49 | .root_source_file = examples.path(b, basename),
50 | .target = target,
51 | .optimize = optimize,
52 | .single_threaded = true,
53 | .win32_manifest = if (subsystem == .Windows) examples.path(b, "win32.manifest") else null,
54 | });
55 | exe.subsystem = subsystem;
56 | exe.root_module.addImport("win32", win32);
57 | examples_step.dependOn(&exe.step);
58 | exe.pie = true;
59 |
60 | const desc_suffix: []const u8 = if (cross_arch_opt) |_| "" else " for the native target";
61 | const build_desc = b.fmt("Build {s}{s}", .{ name, desc_suffix });
62 | b.step(b.fmt("{s}-build", .{name}), build_desc).dependOn(&exe.step);
63 |
64 | const run_cmd = b.addRunArtifact(exe);
65 | const run_desc = b.fmt("Run {s}{s}", .{ name, desc_suffix });
66 | b.step(name, run_desc).dependOn(&run_cmd.step);
67 |
68 | if (builtin.os.tag == .windows) {
69 | if (cross_arch_opt == null) {
70 | examples_step.dependOn(&run_cmd.step);
71 | }
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/ComOverloads.txt:
--------------------------------------------------------------------------------
1 | AI.MachineLearning.WinML IMLOperatorKernelContext GetOutputTensor 1 WithShape
2 | AI.MachineLearning.WinML IMLOperatorKernelContext GetOutputTensor 2 Default
3 | Data.HtmlHelp IITPropList Set 0 String
4 | Data.HtmlHelp IITPropList Set 1 Pointer
5 | Data.HtmlHelp IITPropList Set 2 Dword
6 | Data.HtmlHelp IITPropList SetPersist 6 All
7 | Data.HtmlHelp IITPropList SetPersist 7 One
8 | Data.HtmlHelp IITWordWheel Lookup 5 _TODO_A
9 | Data.HtmlHelp IITWordWheel Lookup 6 _TODO_B
10 | Data.HtmlHelp IITWordWheel Lookup 7 _TODO_C
11 | Data.HtmlHelp IITResultSet Add 3 _TODO_A
12 | Data.HtmlHelp IITResultSet Add 4 _TODO_B
13 | Data.HtmlHelp IITResultSet Add 5 _TODO_C
14 | Data.HtmlHelp IITResultSet Add 6 _TODO_D
15 | Data.HtmlHelp IITResultSet Set 8 _TODO_A
16 | Data.HtmlHelp IITResultSet Set 9 _TODO_B
17 | Data.HtmlHelp IITResultSet Set 10 _TODO_C
18 | Data.HtmlHelp IITResultSet Set 11 _TODO_D
19 | Data.HtmlHelp IITResultSet GetColumn 19 _TODO_A
20 | Data.HtmlHelp IITResultSet GetColumn 20 _TODO_B
21 | Graphics.Direct2D ID2D1SvgStrokeDashArray UpdateDashes 1 Units
22 | Graphics.Direct2D ID2D1SvgStrokeDashArray UpdateDashes 2 Floats
23 | Graphics.Direct2D ID2D1SvgStrokeDashArray GetDashes 3 Units
24 | Graphics.Direct2D ID2D1SvgStrokeDashArray GetDashes 4 Floats
25 | Graphics.Direct2D ID2D1SvgElement SetAttributeValue 23 Obj
26 | Graphics.Direct2D ID2D1SvgElement SetAttributeValue 24 Pod
27 | Graphics.Direct2D ID2D1SvgElement SetAttributeValue 25 String
28 | Graphics.Direct2D ID2D1SvgElement GetAttributeValue 26 Obj
29 | Graphics.Direct2D ID2D1SvgElement GetAttributeValue 27 Pod
30 | Graphics.Direct2D ID2D1SvgElement GetAttributeValue 28 String
31 | Graphics.DirectComposition IDCompositionVisual SetOffsetX 0 _TODO_A
32 | Graphics.DirectComposition IDCompositionVisual SetOffsetX 1 _TODO_B
33 | Graphics.DirectComposition IDCompositionVisual SetOffsetY 2 _TODO_A
34 | Graphics.DirectComposition IDCompositionVisual SetOffsetY 3 _TODO_B
35 | Graphics.DirectComposition IDCompositionVisual SetTransform 4 _TODO_A
36 | Graphics.DirectComposition IDCompositionVisual SetTransform 5 _TODO_B
37 | Graphics.DirectComposition IDCompositionVisual SetClip 10 _TODO_A
38 | Graphics.DirectComposition IDCompositionVisual SetClip 11 _TODO_B
39 | Graphics.DirectComposition IDCompositionTranslateTransform SetOffsetX 0 _TODO_A
40 | Graphics.DirectComposition IDCompositionTranslateTransform SetOffsetX 1 _TODO_B
41 | Graphics.DirectComposition IDCompositionTranslateTransform SetOffsetY 2 _TODO_A
42 | Graphics.DirectComposition IDCompositionTranslateTransform SetOffsetY 3 _TODO_B
43 | Graphics.DirectComposition IDCompositionScaleTransform SetScaleX 0 _TODO_A
44 | Graphics.DirectComposition IDCompositionScaleTransform SetScaleX 1 _TODO_B
45 | Graphics.DirectComposition IDCompositionScaleTransform SetScaleY 2 _TODO_A
46 | Graphics.DirectComposition IDCompositionScaleTransform SetScaleY 3 _TODO_B
47 | Graphics.DirectComposition IDCompositionScaleTransform SetCenterX 4 _TODO_A
48 | Graphics.DirectComposition IDCompositionScaleTransform SetCenterX 5 _TODO_B
49 | Graphics.DirectComposition IDCompositionScaleTransform SetCenterY 6 _TODO_A
50 | Graphics.DirectComposition IDCompositionScaleTransform SetCenterY 7 _TODO_B
51 | Graphics.DirectComposition IDCompositionRotateTransform SetAngle 0 _TODO_A
52 | Graphics.DirectComposition IDCompositionRotateTransform SetAngle 1 _TODO_B
53 | Graphics.DirectComposition IDCompositionRotateTransform SetCenterX 2 _TODO_A
54 | Graphics.DirectComposition IDCompositionRotateTransform SetCenterX 3 _TODO_B
55 | Graphics.DirectComposition IDCompositionRotateTransform SetCenterY 4 _TODO_A
56 | Graphics.DirectComposition IDCompositionRotateTransform SetCenterY 5 _TODO_B
57 | Graphics.DirectComposition IDCompositionSkewTransform SetAngleX 0 _TODO_A
58 | Graphics.DirectComposition IDCompositionSkewTransform SetAngleX 1 _TODO_B
59 | Graphics.DirectComposition IDCompositionSkewTransform SetAngleY 2 _TODO_A
60 | Graphics.DirectComposition IDCompositionSkewTransform SetAngleY 3 _TODO_B
61 | Graphics.DirectComposition IDCompositionSkewTransform SetCenterX 4 _TODO_A
62 | Graphics.DirectComposition IDCompositionSkewTransform SetCenterX 5 _TODO_B
63 | Graphics.DirectComposition IDCompositionSkewTransform SetCenterY 6 _TODO_A
64 | Graphics.DirectComposition IDCompositionSkewTransform SetCenterY 7 _TODO_B
65 | Graphics.DirectComposition IDCompositionMatrixTransform SetMatrixElement 1 _TODO_A
66 | Graphics.DirectComposition IDCompositionMatrixTransform SetMatrixElement 2 _TODO_B
67 | Graphics.DirectComposition IDCompositionEffectGroup SetOpacity 0 _TODO_A
68 | Graphics.DirectComposition IDCompositionEffectGroup SetOpacity 1 _TODO_B
69 | Graphics.DirectComposition IDCompositionTranslateTransform3D SetOffsetX 0 _TODO_A
70 | Graphics.DirectComposition IDCompositionTranslateTransform3D SetOffsetX 1 _TODO_B
71 | Graphics.DirectComposition IDCompositionTranslateTransform3D SetOffsetY 2 _TODO_A
72 | Graphics.DirectComposition IDCompositionTranslateTransform3D SetOffsetY 3 _TODO_B
73 | Graphics.DirectComposition IDCompositionTranslateTransform3D SetOffsetZ 4 _TODO_A
74 | Graphics.DirectComposition IDCompositionTranslateTransform3D SetOffsetZ 5 _TODO_B
75 | Graphics.DirectComposition IDCompositionScaleTransform3D SetScaleX 0 _TODO_A
76 | Graphics.DirectComposition IDCompositionScaleTransform3D SetScaleX 1 _TODO_B
77 | Graphics.DirectComposition IDCompositionScaleTransform3D SetScaleY 2 _TODO_A
78 | Graphics.DirectComposition IDCompositionScaleTransform3D SetScaleY 3 _TODO_B
79 | Graphics.DirectComposition IDCompositionScaleTransform3D SetScaleZ 4 _TODO_A
80 | Graphics.DirectComposition IDCompositionScaleTransform3D SetScaleZ 5 _TODO_B
81 | Graphics.DirectComposition IDCompositionScaleTransform3D SetCenterX 6 _TODO_A
82 | Graphics.DirectComposition IDCompositionScaleTransform3D SetCenterX 7 _TODO_B
83 | Graphics.DirectComposition IDCompositionScaleTransform3D SetCenterY 8 _TODO_A
84 | Graphics.DirectComposition IDCompositionScaleTransform3D SetCenterY 9 _TODO_B
85 | Graphics.DirectComposition IDCompositionScaleTransform3D SetCenterZ 10 _TODO_A
86 | Graphics.DirectComposition IDCompositionScaleTransform3D SetCenterZ 11 _TODO_B
87 | Graphics.DirectComposition IDCompositionRotateTransform3D SetAngle 0 _TODO_A
88 | Graphics.DirectComposition IDCompositionRotateTransform3D SetAngle 1 _TODO_B
89 | Graphics.DirectComposition IDCompositionRotateTransform3D SetAxisX 2 _TODO_A
90 | Graphics.DirectComposition IDCompositionRotateTransform3D SetAxisX 3 _TODO_B
91 | Graphics.DirectComposition IDCompositionRotateTransform3D SetAxisY 4 _TODO_A
92 | Graphics.DirectComposition IDCompositionRotateTransform3D SetAxisY 5 _TODO_B
93 | Graphics.DirectComposition IDCompositionRotateTransform3D SetAxisZ 6 _TODO_A
94 | Graphics.DirectComposition IDCompositionRotateTransform3D SetAxisZ 7 _TODO_B
95 | Graphics.DirectComposition IDCompositionRotateTransform3D SetCenterX 8 _TODO_A
96 | Graphics.DirectComposition IDCompositionRotateTransform3D SetCenterX 9 _TODO_B
97 | Graphics.DirectComposition IDCompositionRotateTransform3D SetCenterY 10 _TODO_A
98 | Graphics.DirectComposition IDCompositionRotateTransform3D SetCenterY 11 _TODO_B
99 | Graphics.DirectComposition IDCompositionRotateTransform3D SetCenterZ 12 _TODO_A
100 | Graphics.DirectComposition IDCompositionRotateTransform3D SetCenterZ 13 _TODO_B
101 | Graphics.DirectComposition IDCompositionMatrixTransform3D SetMatrixElement 1 _TODO_A
102 | Graphics.DirectComposition IDCompositionMatrixTransform3D SetMatrixElement 2 _TODO_B
103 | Graphics.DirectComposition IDCompositionRectangleClip SetLeft 0 _TODO_A
104 | Graphics.DirectComposition IDCompositionRectangleClip SetLeft 1 _TODO_B
105 | Graphics.DirectComposition IDCompositionRectangleClip SetTop 2 _TODO_A
106 | Graphics.DirectComposition IDCompositionRectangleClip SetTop 3 _TODO_B
107 | Graphics.DirectComposition IDCompositionRectangleClip SetRight 4 _TODO_A
108 | Graphics.DirectComposition IDCompositionRectangleClip SetRight 5 _TODO_B
109 | Graphics.DirectComposition IDCompositionRectangleClip SetBottom 6 _TODO_A
110 | Graphics.DirectComposition IDCompositionRectangleClip SetBottom 7 _TODO_B
111 | Graphics.DirectComposition IDCompositionRectangleClip SetTopLeftRadiusX 8 _TODO_A
112 | Graphics.DirectComposition IDCompositionRectangleClip SetTopLeftRadiusX 9 _TODO_B
113 | Graphics.DirectComposition IDCompositionRectangleClip SetTopLeftRadiusY 10 _TODO_A
114 | Graphics.DirectComposition IDCompositionRectangleClip SetTopLeftRadiusY 11 _TODO_B
115 | Graphics.DirectComposition IDCompositionRectangleClip SetTopRightRadiusX 12 _TODO_A
116 | Graphics.DirectComposition IDCompositionRectangleClip SetTopRightRadiusX 13 _TODO_B
117 | Graphics.DirectComposition IDCompositionRectangleClip SetTopRightRadiusY 14 _TODO_A
118 | Graphics.DirectComposition IDCompositionRectangleClip SetTopRightRadiusY 15 _TODO_B
119 | Graphics.DirectComposition IDCompositionRectangleClip SetBottomLeftRadiusX 16 _TODO_A
120 | Graphics.DirectComposition IDCompositionRectangleClip SetBottomLeftRadiusX 17 _TODO_B
121 | Graphics.DirectComposition IDCompositionRectangleClip SetBottomLeftRadiusY 18 _TODO_A
122 | Graphics.DirectComposition IDCompositionRectangleClip SetBottomLeftRadiusY 19 _TODO_B
123 | Graphics.DirectComposition IDCompositionRectangleClip SetBottomRightRadiusX 20 _TODO_A
124 | Graphics.DirectComposition IDCompositionRectangleClip SetBottomRightRadiusX 21 _TODO_B
125 | Graphics.DirectComposition IDCompositionRectangleClip SetBottomRightRadiusY 22 _TODO_A
126 | Graphics.DirectComposition IDCompositionRectangleClip SetBottomRightRadiusY 23 _TODO_B
127 | Graphics.DirectComposition IDCompositionVisual3 SetOffsetZ 1 _TODO_A
128 | Graphics.DirectComposition IDCompositionVisual3 SetOffsetZ 2 _TODO_B
129 | Graphics.DirectComposition IDCompositionVisual3 SetOpacity 3 _TODO_A
130 | Graphics.DirectComposition IDCompositionVisual3 SetOpacity 4 _TODO_B
131 | Graphics.DirectComposition IDCompositionVisual3 SetTransform 5 _TODO_A
132 | Graphics.DirectComposition IDCompositionVisual3 SetTransform 6 _TODO_B
133 | Graphics.DirectComposition IDCompositionGaussianBlurEffect SetStandardDeviation 0 _TODO_A
134 | Graphics.DirectComposition IDCompositionGaussianBlurEffect SetStandardDeviation 1 _TODO_B
135 | Graphics.DirectComposition IDCompositionBrightnessEffect SetWhitePointX 2 _TODO_A
136 | Graphics.DirectComposition IDCompositionBrightnessEffect SetWhitePointX 3 _TODO_B
137 | Graphics.DirectComposition IDCompositionBrightnessEffect SetWhitePointY 4 _TODO_A
138 | Graphics.DirectComposition IDCompositionBrightnessEffect SetWhitePointY 5 _TODO_B
139 | Graphics.DirectComposition IDCompositionBrightnessEffect SetBlackPointX 6 _TODO_A
140 | Graphics.DirectComposition IDCompositionBrightnessEffect SetBlackPointX 7 _TODO_B
141 | Graphics.DirectComposition IDCompositionBrightnessEffect SetBlackPointY 8 _TODO_A
142 | Graphics.DirectComposition IDCompositionBrightnessEffect SetBlackPointY 9 _TODO_B
143 | Graphics.DirectComposition IDCompositionColorMatrixEffect SetMatrixElement 1 _TODO_A
144 | Graphics.DirectComposition IDCompositionColorMatrixEffect SetMatrixElement 2 _TODO_B
145 | Graphics.DirectComposition IDCompositionShadowEffect SetStandardDeviation 0 _TODO_A
146 | Graphics.DirectComposition IDCompositionShadowEffect SetStandardDeviation 1 _TODO_B
147 | Graphics.DirectComposition IDCompositionShadowEffect SetRed 3 _TODO_A
148 | Graphics.DirectComposition IDCompositionShadowEffect SetRed 4 _TODO_B
149 | Graphics.DirectComposition IDCompositionShadowEffect SetGreen 5 _TODO_A
150 | Graphics.DirectComposition IDCompositionShadowEffect SetGreen 6 _TODO_B
151 | Graphics.DirectComposition IDCompositionShadowEffect SetBlue 7 _TODO_A
152 | Graphics.DirectComposition IDCompositionShadowEffect SetBlue 8 _TODO_B
153 | Graphics.DirectComposition IDCompositionShadowEffect SetAlpha 9 _TODO_A
154 | Graphics.DirectComposition IDCompositionShadowEffect SetAlpha 10 _TODO_B
155 | Graphics.DirectComposition IDCompositionHueRotationEffect SetAngle 0 _TODO_A
156 | Graphics.DirectComposition IDCompositionHueRotationEffect SetAngle 1 _TODO_B
157 | Graphics.DirectComposition IDCompositionSaturationEffect SetSaturation 0 _TODO_A
158 | Graphics.DirectComposition IDCompositionSaturationEffect SetSaturation 1 _TODO_B
159 | Graphics.DirectComposition IDCompositionLinearTransferEffect SetRedYIntercept 0 _TODO_A
160 | Graphics.DirectComposition IDCompositionLinearTransferEffect SetRedYIntercept 1 _TODO_B
161 | Graphics.DirectComposition IDCompositionLinearTransferEffect SetRedSlope 2 _TODO_A
162 | Graphics.DirectComposition IDCompositionLinearTransferEffect SetRedSlope 3 _TODO_B
163 | Graphics.DirectComposition IDCompositionLinearTransferEffect SetGreenYIntercept 5 _TODO_A
164 | Graphics.DirectComposition IDCompositionLinearTransferEffect SetGreenYIntercept 6 _TODO_B
165 | Graphics.DirectComposition IDCompositionLinearTransferEffect SetGreenSlope 7 _TODO_A
166 | Graphics.DirectComposition IDCompositionLinearTransferEffect SetGreenSlope 8 _TODO_B
167 | Graphics.DirectComposition IDCompositionLinearTransferEffect SetBlueYIntercept 10 _TODO_A
168 | Graphics.DirectComposition IDCompositionLinearTransferEffect SetBlueYIntercept 11 _TODO_B
169 | Graphics.DirectComposition IDCompositionLinearTransferEffect SetBlueSlope 12 _TODO_A
170 | Graphics.DirectComposition IDCompositionLinearTransferEffect SetBlueSlope 13 _TODO_B
171 | Graphics.DirectComposition IDCompositionLinearTransferEffect SetAlphaYIntercept 15 _TODO_A
172 | Graphics.DirectComposition IDCompositionLinearTransferEffect SetAlphaYIntercept 16 _TODO_B
173 | Graphics.DirectComposition IDCompositionLinearTransferEffect SetAlphaSlope 17 _TODO_A
174 | Graphics.DirectComposition IDCompositionLinearTransferEffect SetAlphaSlope 18 _TODO_B
175 | Graphics.DirectComposition IDCompositionTableTransferEffect SetRedTableValue 9 _TODO_A
176 | Graphics.DirectComposition IDCompositionTableTransferEffect SetRedTableValue 10 _TODO_B
177 | Graphics.DirectComposition IDCompositionTableTransferEffect SetGreenTableValue 11 _TODO_A
178 | Graphics.DirectComposition IDCompositionTableTransferEffect SetGreenTableValue 12 _TODO_B
179 | Graphics.DirectComposition IDCompositionTableTransferEffect SetBlueTableValue 13 _TODO_A
180 | Graphics.DirectComposition IDCompositionTableTransferEffect SetBlueTableValue 14 _TODO_B
181 | Graphics.DirectComposition IDCompositionTableTransferEffect SetAlphaTableValue 15 _TODO_A
182 | Graphics.DirectComposition IDCompositionTableTransferEffect SetAlphaTableValue 16 _TODO_B
183 | Graphics.DirectComposition IDCompositionArithmeticCompositeEffect SetCoefficient1 2 _TODO_A
184 | Graphics.DirectComposition IDCompositionArithmeticCompositeEffect SetCoefficient1 3 _TODO_B
185 | Graphics.DirectComposition IDCompositionArithmeticCompositeEffect SetCoefficient2 4 _TODO_A
186 | Graphics.DirectComposition IDCompositionArithmeticCompositeEffect SetCoefficient2 5 _TODO_B
187 | Graphics.DirectComposition IDCompositionArithmeticCompositeEffect SetCoefficient3 6 _TODO_A
188 | Graphics.DirectComposition IDCompositionArithmeticCompositeEffect SetCoefficient3 7 _TODO_B
189 | Graphics.DirectComposition IDCompositionArithmeticCompositeEffect SetCoefficient4 8 _TODO_A
190 | Graphics.DirectComposition IDCompositionArithmeticCompositeEffect SetCoefficient4 9 _TODO_B
191 | Graphics.DirectComposition IDCompositionAffineTransform2DEffect SetTransformMatrixElement 3 _TODO_A
192 | Graphics.DirectComposition IDCompositionAffineTransform2DEffect SetTransformMatrixElement 4 _TODO_B
193 | Graphics.DirectComposition IDCompositionAffineTransform2DEffect SetSharpness 5 _TODO_A
194 | Graphics.DirectComposition IDCompositionAffineTransform2DEffect SetSharpness 6 _TODO_B
195 | Graphics.DirectWrite IDWriteFactory3 CreateFontFaceReference 2 _TODO_A
196 | Graphics.DirectWrite IDWriteFactory3 CreateFontFaceReference 3 _TODO_B
197 | Graphics.DirectWrite IDWriteFontSet GetPropertyValues 4 _TODO_A
198 | Graphics.DirectWrite IDWriteFontSet GetPropertyValues 5 _TODO_B
199 | Graphics.DirectWrite IDWriteFontSet GetPropertyValues 6 _TODO_C
200 | Graphics.DirectWrite IDWriteFontSet GetMatchingFonts 8 _TODO_A
201 | Graphics.DirectWrite IDWriteFontSet GetMatchingFonts 9 _TODO_B
202 | Graphics.DirectWrite IDWriteFontSetBuilder AddFontFaceReference 0 _TODO_A
203 | Graphics.DirectWrite IDWriteFontSetBuilder AddFontFaceReference 1 _TODO_B
204 | Graphics.DirectWrite IDWriteGdiInterop1 GetFontSignature 1 _TODO_A
205 | Graphics.DirectWrite IDWriteGdiInterop1 GetFontSignature 2 _TODO_B
206 | Graphics.DirectWrite IDWriteFontFace4 GetGlyphImageFormats 0 _TODO_A
207 | Graphics.DirectWrite IDWriteFontFace4 GetGlyphImageFormats 1 _TODO_B
208 | Graphics.DirectWrite IDWriteFactory4 ComputeGlyphOrigins 1 _TODO_A
209 | Graphics.DirectWrite IDWriteFactory4 ComputeGlyphOrigins 2 _TODO_B
210 | Graphics.DirectWrite IDWriteFontSet1 GetFilteredFonts 2 _TODO_A
211 | Graphics.DirectWrite IDWriteFontSet1 GetFilteredFonts 3 _TODO_B
212 | Graphics.DirectWrite IDWriteFontSet1 GetFilteredFonts 4 _TODO_C
213 | Graphics.DirectWrite IDWriteFontSet1 GetFilteredFontIndices 5 _TODO_A
214 | Graphics.DirectWrite IDWriteFontSet1 GetFilteredFontIndices 6 _TODO_C
215 | Graphics.DirectWrite IDWriteFontSet1 GetFontAxisRanges 7 _TODO_A
216 | Graphics.DirectWrite IDWriteFontSet1 GetFontAxisRanges 8 _TODO_B
217 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # zigwin32gen
2 |
3 | Generates Zig bindings for Win32 using https://github.com/marlersoft/win32json. If you just want to use the bindings, see https://github.com/marlersoft/zigwin32.
4 |
5 | ## How to generate the Windows Zig bindings
6 |
7 | Download zig, currently tested on version: 0.12.0-dev.2701+d18f52197
8 |
9 | First, clone the zigwin32 repo from the root of this repository to provide a location for the generated files:
10 |
11 | ```
12 | git clone https://github.com/marlersoft/zigwin32
13 | ```
14 |
15 | Then run the following command to generate the latest bindings:
16 |
17 | ```
18 | zig build
19 | ```
20 |
21 | On the first run, running this command will provide an error message saying that the `win32json` dependency is missing, and will provide you with a git command to clone it and checkout the expected version.
22 |
23 | Once the build task completes, you can view your generated code in the `zigwin32` subrepository that you cloned earlier.
24 |
25 | ## win32json vs C/C++ Win32 SDK
26 |
27 | The win32metadata project from which win32json is generated does not expose the exact same interface as the classic C/C++ header files did. One thing I'd like is to support is to be able to take a Windows C/C++ example and port it to Zig without changing any names/types or the way it interfaces with the Windows API. To support this I'll need to:
28 |
29 | * Support a set of modules with the same structure as the Window headers. For example, importing the `windows` module should have all the same declarations as the `windows.h` header.
30 | * Generate structs with the same field names (hungarian notation)
31 | * Support code that can be compiled for either Ascii or Wide characters. Provide mappings like `CreateFile` to `CreateFileA` or `CreateFileW` based on this configuration.
32 |
33 |
34 | For example, intead of `DWORD` or `UINT32`, I would generate `u32`. Instead of field names with hungarian notation, I would generate them with snake case. These would only be cosmetic changes, where it would look more natural to use the Windows API directly alongside other Zig APIs. The bigger advantage to this is not having to track all the different ways to declare the same type, for example, a developer having to know that `DWORD`, `UINT32`, `ULONG`, `UINT` are all the same as `u32`.
35 | * I may want to be able support different "views" into the api that change things like how to import the api. These extra views should not add to compile-time unless they are being used.
36 | * I may want to consider also generating wrapper APIs? It's clear that wrapping APIs should exist, but I'm not sure if this wrapping API should be created/maintained manually or autogenerated from the Windows API data.
37 | * I may want to make it possible to generate subsets of the complete API. One example of where this could be used it to generate bindings to be included in the standard library, where only a small portion of the complete API is required.
38 |
39 | extra.txt
40 | ================================================================================
41 | This file contains extra metadata, namely,
42 |
43 | * whether pointer types are "union pointers"
44 | * whether pointer types are "not null"
45 |
46 | "Union Pointers" are pointers that in addition to actual pointer values also
47 | accept special reserved values that aren't actually pointers.
48 |
49 | It's important for Zig to know when this is the case because Zig inserts
50 | runtime alignment checks when casting non-pointer values to pointers. To address
51 | this, anytime a pointer type is actually a "union pointer", we annotate that
52 | pointer type with `align(1)` to ellide this runtime alignment check. Note that
53 | the reason for using `align(1)` instead of a union type is because modifying
54 | a parameter type to be a union instead of a pointer can modify the ABI.
55 | See https://github.com/dotnet/coreclr/pull/23974#issuecomment-482961995 for an
56 | example of this.
57 |
58 | > See Issue "CreateWindowEx lpClassName union": https://github.com/microsoft/win32metadata/issues/623
59 |
60 | We also add include extra information about which pointers are "not null". By
61 | default, all pointer types in the metadata are treated as being "optional"
62 | (i.e. `?*T`). We can override this default by providing a "NotNull" modifier
63 | in extra.txt.
64 |
65 | The "NotNull" modifier is a sequence of 0/1 flags. Each flag modifies a pointer
66 | in the type to be "non-optional".
67 | For example, consider the type `?*?*?*T`. The integer value `0` would leave
68 | the type unmodified.
69 | The modifier `1` would modify the first pointer in the type to make it `*?*?*T`.
70 | The modifier `10` would modify the second pointer in the type to become `?**?*T`.
71 | The integer value `111` would modify all 3 pointer types to become `***T`.
72 |
--------------------------------------------------------------------------------
/build.zig:
--------------------------------------------------------------------------------
1 | const builtin = @import("builtin");
2 | const std = @import("std");
3 | const Build = std.Build;
4 | const Step = std.Build.Step;
5 | const CrossTarget = std.zig.CrossTarget;
6 | const buildcommon = @import("0.13.0/common.zig");
7 |
8 | comptime {
9 | const required_zig = "0.14.0";
10 | const v = std.SemanticVersion.parse(required_zig) catch unreachable;
11 | if (builtin.zig_version.order(v) != .eq) @compileError(
12 | "zig version " ++ required_zig ++ " is required to ensure zigwin32 output is always the same",
13 | );
14 | }
15 |
16 | pub fn build(b: *Build) !void {
17 | const default_steps = "install diff test";
18 | b.default_step = b.step(
19 | "default",
20 | "The default step, equivalent to: " ++ default_steps,
21 | );
22 | const test_step = b.step("test", "Run all the tests (except the examples)");
23 | const unittest_step = b.step("unittest", "Unit test the generated bindings");
24 | test_step.dependOn(unittest_step);
25 | const desc_line_prefix = [_]u8{' '} ** 31;
26 | const diff_step = b.step(
27 | "diff",
28 | ("Updates 'diffrepo' then installs the latest generated\n" ++
29 | desc_line_prefix ++
30 | "files so they can be diffed via git."),
31 | );
32 | addDefaultStepDeps(b, default_steps);
33 |
34 | {
35 | const release_step = b.step("release", "Generate the bindings and run tests for a release");
36 | release_step.dependOn(test_step);
37 | release_step.dependOn(b.getInstallStep());
38 | }
39 |
40 | const pass1_step = b.step(
41 | "pass1",
42 | "Only perform pass1 of zig binding generation.\n" ++ desc_line_prefix ++
43 | "(generates pass1.json in .zig-cache)",
44 | );
45 | const gen_step = b.step("gen", "Generate the bindings (in .zig-cache)");
46 | const optimize = b.standardOptimizeOption(.{});
47 |
48 | const win32json_dep = b.dependency("win32json", .{});
49 |
50 | const pass1_out_file = blk: {
51 | const pass1_exe = b.addExecutable(.{
52 | .name = "pass1",
53 | .root_source_file = b.path("src/pass1.zig"),
54 | .optimize = optimize,
55 | .target = b.graph.host,
56 | });
57 |
58 | const run = b.addRunArtifact(pass1_exe);
59 | run.addDirectoryArg(win32json_dep.path(""));
60 | const out_file = run.addOutputFileArg("pass1.json");
61 |
62 | pass1_step.dependOn(&run.step);
63 | break :blk out_file;
64 | };
65 |
66 | const zigexports = blk: {
67 | const exe = b.addExecutable(.{
68 | .name = "genzigexports",
69 | .root_source_file = b.path("src/genzigexports.zig"),
70 | .target = b.graph.host,
71 | });
72 | exe.root_module.addImport("win32_stub", b.createModule(.{
73 | .root_source_file = b.path("src/static/win32.zig"),
74 | }));
75 |
76 | const run = b.addRunArtifact(exe);
77 | break :blk run.addOutputFileArg("zigexports.zig");
78 | };
79 |
80 | const gen_out_dir = blk: {
81 | const exe = b.addExecutable(.{
82 | .name = "genzig",
83 | .root_source_file = b.path("src/genzig.zig"),
84 | .optimize = optimize,
85 | .target = b.graph.host,
86 | });
87 | exe.root_module.addImport(
88 | "zigexports",
89 | b.createModule(.{ .root_source_file = zigexports }),
90 | );
91 | const run = b.addRunArtifact(exe);
92 | run.addFileArg(b.path("extra.txt"));
93 | run.addDirectoryArg(win32json_dep.path(""));
94 | run.addFileArg(pass1_out_file);
95 | run.addFileArg(b.path("ComOverloads.txt"));
96 | const out_dir = run.addOutputDirectoryArg(".");
97 | gen_step.dependOn(&run.step);
98 | break :blk out_dir;
99 | };
100 |
101 | b.step(
102 | "show-path",
103 | "Print the zigwin32 cache directory",
104 | ).dependOn(&PrintLazyPath.create(b, gen_out_dir).step);
105 |
106 | b.installDirectory(.{
107 | .source_dir = gen_out_dir,
108 | .install_dir = .prefix,
109 | .install_subdir = ".",
110 | });
111 |
112 | {
113 | const diff_exe = b.addExecutable(.{
114 | .name = "diff",
115 | .root_source_file = b.path("src/diff.zig"),
116 | .target = b.graph.host,
117 | .optimize = .Debug,
118 | });
119 | const diff = b.addRunArtifact(diff_exe);
120 | // fetches from zigwin32 github and also modifies the contents
121 | // of the 'diffrepo' subdirectory so definitely has side effects
122 | diff.has_side_effects = true;
123 | diff.addArg("--zigbuild");
124 | // make this a normal string arg, we don't want the build system
125 | // trying to hash this as an input or something
126 | diff.addArg(b.pathFromRoot("diffrepo"));
127 | diff.addDirectoryArg(gen_out_dir);
128 | if (b.args) |args| {
129 | diff.addArgs(args);
130 | }
131 | diff_step.dependOn(&diff.step);
132 | }
133 |
134 | {
135 | const unittest = b.addTest(.{
136 | .root_source_file = gen_out_dir.path(b, "win32.zig"),
137 | .target = b.graph.host,
138 | .optimize = optimize,
139 | });
140 | unittest.pie = true;
141 | unittest_step.dependOn(&unittest.step);
142 | }
143 |
144 | const win32 = b.createModule(.{
145 | .root_source_file = gen_out_dir.path(b, "win32.zig"),
146 | });
147 |
148 | buildcommon.addExamples(b, optimize, win32, b.path("examples"));
149 |
150 | {
151 | const exe = b.addExecutable(.{
152 | .name = "comoverload",
153 | .root_source_file = b.path("test/comoverload.zig"),
154 | .target = b.graph.host,
155 | });
156 | exe.root_module.addImport("win32", win32);
157 | const run = b.addRunArtifact(exe);
158 | b.step("comoverload", "").dependOn(&run.step);
159 | test_step.dependOn(&run.step);
160 | }
161 | {
162 | const compile = b.addSystemCommand(&.{
163 | b.graph.zig_exe,
164 | "build-exe",
165 | "--dep",
166 | "win32",
167 | });
168 | compile.addPrefixedFileArg("-Mroot=", b.path("test/badcomoverload.zig"));
169 | compile.addPrefixedFileArg("-Mwin32=", gen_out_dir.path(b, "win32.zig"));
170 | compile.addCheck(.{
171 | .expect_stderr_match = "COM method 'GetAttributeValue' must be called using one of the following overload names: GetAttributeValueString, GetAttributeValueObj, GetAttributeValuePod",
172 | });
173 | test_step.dependOn(&compile.step);
174 | }
175 | }
176 |
177 | const PrintLazyPath = struct {
178 | step: Step,
179 | lazy_path: Build.LazyPath,
180 | pub fn create(
181 | b: *Build,
182 | lazy_path: Build.LazyPath,
183 | ) *PrintLazyPath {
184 | const print = b.allocator.create(PrintLazyPath) catch unreachable;
185 | print.* = .{
186 | .step = Step.init(.{
187 | .id = .custom,
188 | .name = "print the given lazy path",
189 | .owner = b,
190 | .makeFn = make,
191 | }),
192 | .lazy_path = lazy_path,
193 | };
194 | lazy_path.addStepDependencies(&print.step);
195 | return print;
196 | }
197 | fn make(step: *Step, opt: std.Build.Step.MakeOptions) !void {
198 | _ = opt;
199 | const print: *PrintLazyPath = @fieldParentPtr("step", step);
200 | try std.io.getStdOut().writer().print(
201 | "{s}\n",
202 | .{print.lazy_path.getPath(step.owner)},
203 | );
204 | }
205 | };
206 |
207 | fn addDefaultStepDeps(b: *std.Build, default_steps: []const u8) void {
208 | var it = std.mem.tokenizeScalar(u8, default_steps, ' ');
209 | while (it.next()) |step_name| {
210 | const step = b.top_level_steps.get(step_name) orelse std.debug.panic(
211 | "step '{s}' not added yet",
212 | .{step_name},
213 | );
214 | b.default_step.dependOn(&step.step);
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/build.zig.zon:
--------------------------------------------------------------------------------
1 | .{
2 | .name = .zigwin32gen,
3 | .version = "0.0.0",
4 | .fingerprint = 0xab047bbd59488dae,
5 | .minimum_zig_version = "0.14.0",
6 | .dependencies = .{
7 | .win32json = .{
8 | .url = "git+https://github.com/marlersoft/win32json#d1f6a1fba4a7714f65b9c8a2696e7530561366ef",
9 | .hash = "N-V-__8AALasBATgDWZkgeN6G_QBIVFt9gD36O5YqZv6itzS",
10 | },
11 | },
12 | .paths = .{"build.zig.zon"},
13 | }
14 |
--------------------------------------------------------------------------------
/examples/basewin.zig:
--------------------------------------------------------------------------------
1 | //! This example is ported from : https://github.com/microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/begin/LearnWin32/Direct2DCircle/cpp/basewin.h
2 |
3 | const WINAPI = @import("std").os.windows.WINAPI;
4 | const win32 = struct {
5 | usingnamespace @import("win32").zig;
6 | usingnamespace @import("win32").foundation;
7 | usingnamespace @import("win32").system.system_services;
8 | usingnamespace @import("win32").system.library_loader;
9 | usingnamespace @import("win32").ui.windows_and_messaging;
10 | };
11 | const L = win32.L;
12 | const HWND = win32.HWND;
13 |
14 | const windowlongptr = @import("win32").windowlongptr;
15 |
16 | // NOTE: can't do usingnamespace for menu_and_resources because it has conflicts with windows_and_messaging
17 | // I think this particular one is a problem with win32metadata.
18 | // NOTE: should Zig allow symbol conflicts so long as they are not referenced?
19 | const mnr = @import("win32").ui.menus_and_resources;
20 |
21 | pub fn BaseWindow(comptime DERIVED_TYPE: type) type {
22 | return struct {
23 | fn WindowProc(hwnd: HWND, uMsg: u32, wParam: win32.WPARAM, lParam: win32.LPARAM) callconv(WINAPI) win32.LRESULT {
24 | var pThis: ?*DERIVED_TYPE = null;
25 | if (uMsg == win32.WM_NCCREATE) {
26 | const pCreate: *win32.CREATESTRUCT = @ptrFromInt(@as(usize, @bitCast(lParam)));
27 | pThis = @ptrCast(@alignCast(pCreate.lpCreateParams));
28 | _ = windowlongptr.SetWindowLongPtr(hwnd, win32.GWL_USERDATA, @bitCast(@intFromPtr(pThis)));
29 | pThis.?.base.m_hwnd = hwnd;
30 | } else {
31 | //pThis = @intToPtr(?*DERIVED_TYPE, @bitCast(usize, windowlongptr.GetWindowLongPtr(hwnd, win32.GWL_USERDATA)));
32 | pThis = @ptrFromInt(@as(usize, @bitCast(windowlongptr.GetWindowLongPtr(hwnd, win32.GWL_USERDATA))));
33 | }
34 | if (pThis) |this| {
35 | return this.HandleMessage(uMsg, wParam, lParam);
36 | } else {
37 | return win32.DefWindowProc(hwnd, uMsg, wParam, lParam);
38 | }
39 | }
40 |
41 | pub fn Create(
42 | self: *@This(),
43 | lpWindowName: [*:0]const u16,
44 | dwStyle: win32.WINDOW_STYLE,
45 | options: struct {
46 | dwExStyle: win32.WINDOW_EX_STYLE = .{},
47 | x: i32 = win32.CW_USEDEFAULT,
48 | y: i32 = win32.CW_USEDEFAULT,
49 | nWidth: i32 = win32.CW_USEDEFAULT,
50 | nHeight: i32 = win32.CW_USEDEFAULT,
51 | hWndParent: ?HWND = null,
52 | hMenu: ?win32.HMENU = null,
53 | },
54 | ) win32.BOOL {
55 | const wc = win32.WNDCLASS{
56 | .style = .{},
57 | .lpfnWndProc = WindowProc,
58 | .cbClsExtra = 0,
59 | .cbWndExtra = 0,
60 | .hInstance = win32.GetModuleHandle(null),
61 | .hIcon = null,
62 | .hCursor = null,
63 | .hbrBackground = null,
64 | // TODO: autogen bindings don't allow for null, should win32metadata allow Option for fields? Or should all strings allow NULL?
65 | .lpszMenuName = L("Placeholder"),
66 | .lpszClassName = DERIVED_TYPE.ClassName(),
67 | };
68 |
69 | _ = win32.RegisterClass(&wc);
70 |
71 | self.m_hwnd = win32.CreateWindowEx(
72 | options.dwExStyle,
73 | DERIVED_TYPE.ClassName(),
74 | lpWindowName,
75 | dwStyle,
76 | options.x,
77 | options.y,
78 | options.nWidth,
79 | options.nHeight,
80 | options.hWndParent,
81 | options.hMenu,
82 | win32.GetModuleHandle(null),
83 | @ptrCast(self),
84 | );
85 |
86 | return if (self.m_hwnd != null) win32.TRUE else win32.FALSE;
87 | }
88 |
89 | pub fn Window(self: @This()) ?HWND {
90 | return self.m_hwnd;
91 | }
92 |
93 | m_hwnd: ?HWND = null,
94 | };
95 | }
96 |
--------------------------------------------------------------------------------
/examples/d2dcircle.zig:
--------------------------------------------------------------------------------
1 | //! This example is ported from : https://github.com/microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/begin/LearnWin32/Direct2DCircle/cpp/main.cpp
2 | pub const UNICODE = true;
3 |
4 | const WINAPI = @import("std").os.windows.WINAPI;
5 | const win32 = struct {
6 | usingnamespace @import("win32").zig;
7 | usingnamespace @import("win32").foundation;
8 | usingnamespace @import("win32").system.system_services;
9 | usingnamespace @import("win32").ui.windows_and_messaging;
10 | usingnamespace @import("win32").graphics.gdi;
11 | usingnamespace @import("win32").graphics.direct2d;
12 | usingnamespace @import("win32").graphics.direct2d.common;
13 | usingnamespace @import("win32").graphics.direct3d9;
14 | usingnamespace @import("win32").graphics.dxgi.common;
15 | usingnamespace @import("win32").system.com;
16 | };
17 | const L = win32.L;
18 | const FAILED = win32.FAILED;
19 | const SUCCEEDED = win32.SUCCEEDED;
20 | const HRESULT = win32.HRESULT;
21 | const HINSTANCE = win32.HINSTANCE;
22 | const HWND = win32.HWND;
23 | const MSG = win32.MSG;
24 | const WPARAM = win32.WPARAM;
25 | const LPARAM = win32.LPARAM;
26 | const LRESULT = win32.LRESULT;
27 | const RECT = win32.RECT;
28 | const D2D_SIZE_U = win32.D2D_SIZE_U;
29 | const D2D_SIZE_F = win32.D2D_SIZE_F;
30 | const SafeReslease = win32.SafeRelease;
31 |
32 | const basewin = @import("basewin.zig");
33 | const BaseWindow = basewin.BaseWindow;
34 |
35 | fn SafeRelease(ppT: anytype) void {
36 | if (ppT.*) |t| {
37 | _ = t.IUnknown.Release();
38 | ppT.* = null;
39 | }
40 | }
41 |
42 | const MainWindow = struct {
43 | base: BaseWindow(@This()) = .{},
44 | pFactory: ?*win32.ID2D1Factory = null,
45 | pRenderTarget: ?*win32.ID2D1HwndRenderTarget = null,
46 | pBrush: ?*win32.ID2D1SolidColorBrush = null,
47 | ellipse: win32.D2D1_ELLIPSE = undefined,
48 |
49 | pub inline fn CalculateLayout(self: *MainWindow) void {
50 | MainWindowCalculateLayout(self);
51 | }
52 | pub inline fn CreateGraphicsResources(self: *MainWindow) HRESULT {
53 | return MainWindowCreateGraphicsResources(self);
54 | }
55 | pub inline fn DiscardGraphicsResources(self: *MainWindow) void {
56 | MainWindowDiscardGraphicsResources(self);
57 | }
58 | pub inline fn OnPaint(self: *MainWindow) void {
59 | MainWindowOnPaint(self);
60 | }
61 | pub inline fn Resize(self: *MainWindow) void {
62 | MainWindowResize(self);
63 | }
64 |
65 | pub fn ClassName() [*:0]const u16 {
66 | return L("Circle Window Class");
67 | }
68 |
69 | pub fn HandleMessage(self: *MainWindow, uMsg: u32, wParam: WPARAM, lParam: LPARAM) LRESULT {
70 | return MainWindowHandleMessage(self, uMsg, wParam, lParam);
71 | }
72 | };
73 |
74 | // Recalculate drawing layout when the size of the window changes.
75 |
76 | fn MainWindowCalculateLayout(self: *MainWindow) void {
77 | if (self.pRenderTarget) |pRenderTarget| {
78 | // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
79 | // TODO: this call is causing a segfault when we return from this function!!!
80 | // I believe it is caused by this issue: https://github.com/ziglang/zig/issues/1481
81 | // Zig unable to handle a return type of extern struct { x: f32, y: f32 } for WINAPI
82 | _ = pRenderTarget;
83 | //const size: D2D_SIZE_F = pRenderTarget.ID2D1RenderTarget_GetSize();
84 | const size = D2D_SIZE_F{ .width = 300, .height = 300 };
85 | const x: f32 = size.width / 2;
86 | const y: f32 = size.height / 2;
87 | const radius = @min(x, y);
88 | self.ellipse = D2D1.Ellipse(D2D1.Point2F(x, y), radius, radius);
89 | }
90 | }
91 |
92 | fn MainWindowCreateGraphicsResources(self: *MainWindow) HRESULT {
93 | var hr = win32.S_OK;
94 | if (self.pRenderTarget == null) {
95 | var rc: RECT = undefined;
96 | _ = win32.GetClientRect(self.base.m_hwnd.?, &rc);
97 |
98 | const size = D2D_SIZE_U{ .width = @intCast(rc.right), .height = @intCast(rc.bottom) };
99 |
100 | var target: *win32.ID2D1HwndRenderTarget = undefined;
101 | hr = self.pFactory.?.CreateHwndRenderTarget(
102 | &D2D1.RenderTargetProperties(),
103 | &D2D1.HwndRenderTargetProperties(self.base.m_hwnd.?, size),
104 | &target,
105 | );
106 |
107 | if (SUCCEEDED(hr)) {
108 | self.pRenderTarget = target;
109 | const color = D2D1.ColorF(.{ .r = 1, .g = 1, .b = 0 });
110 | var brush: *win32.ID2D1SolidColorBrush = undefined;
111 | hr = self.pRenderTarget.?.ID2D1RenderTarget.CreateSolidColorBrush(&color, null, &brush);
112 |
113 | if (SUCCEEDED(hr)) {
114 | self.pBrush = brush;
115 | self.CalculateLayout();
116 | }
117 | }
118 | }
119 | return hr;
120 | }
121 |
122 | fn MainWindowDiscardGraphicsResources(self: *MainWindow) void {
123 | SafeRelease(&self.pRenderTarget);
124 | SafeRelease(&self.pBrush);
125 | }
126 |
127 | fn MainWindowOnPaint(self: *MainWindow) void {
128 | var hr = self.CreateGraphicsResources();
129 | if (SUCCEEDED(hr)) {
130 | var ps: win32.PAINTSTRUCT = undefined;
131 | _ = win32.BeginPaint(self.base.m_hwnd.?, &ps);
132 |
133 | self.pRenderTarget.?.ID2D1RenderTarget.BeginDraw();
134 |
135 | self.pRenderTarget.?.ID2D1RenderTarget.Clear(&D2D1.ColorFU32(.{ .rgb = D2D1.SkyBlue }));
136 | // TODO: how do I get a COM interface type to convert to a base type without
137 | // an explicit cast like this?
138 | self.pRenderTarget.?.ID2D1RenderTarget.FillEllipse(&self.ellipse, &self.pBrush.?.ID2D1Brush);
139 |
140 | hr = self.pRenderTarget.?.ID2D1RenderTarget.EndDraw(null, null);
141 | if (FAILED(hr) or hr == win32.D2DERR_RECREATE_TARGET) {
142 | self.DiscardGraphicsResources();
143 | }
144 | _ = win32.EndPaint(self.base.m_hwnd.?, &ps);
145 | }
146 | }
147 |
148 | fn MainWindowResize(self: *MainWindow) void {
149 | if (self.pRenderTarget) |renderTarget| {
150 | var rc: RECT = undefined;
151 | _ = win32.GetClientRect(self.base.m_hwnd.?, &rc);
152 |
153 | const size = D2D_SIZE_U{ .width = @intCast(rc.right), .height = @intCast(rc.bottom) };
154 |
155 | _ = renderTarget.Resize(&size);
156 | self.CalculateLayout();
157 | _ = win32.InvalidateRect(self.base.m_hwnd.?, null, win32.FALSE);
158 | }
159 | }
160 |
161 | pub export fn wWinMain(_: HINSTANCE, __: ?HINSTANCE, ___: [*:0]u16, nCmdShow: u32) callconv(WINAPI) c_int {
162 | _ = __;
163 | _ = ___;
164 |
165 | var win = MainWindow{};
166 |
167 | if (win32.TRUE != win.base.Create(L("Circle"), win32.WS_OVERLAPPEDWINDOW, .{})) {
168 | return 0;
169 | }
170 |
171 | _ = win32.ShowWindow(win.base.Window(), @bitCast(nCmdShow));
172 |
173 | // Run the message loop.
174 |
175 | var msg: MSG = undefined;
176 | while (0 != win32.GetMessage(&msg, null, 0, 0)) {
177 | _ = win32.TranslateMessage(&msg);
178 | _ = win32.DispatchMessage(&msg);
179 | }
180 |
181 | return 0;
182 | }
183 |
184 | fn MainWindowHandleMessage(self: *MainWindow, uMsg: u32, wParam: WPARAM, lParam: LPARAM) LRESULT {
185 | switch (uMsg) {
186 | win32.WM_CREATE => {
187 | // TODO: Should I need to case &self.pFactory to **anyopaque? Maybe
188 | // D2D2CreateFactory probably doesn't have the correct type yet?
189 | if (FAILED(win32.D2D1CreateFactory(
190 | win32.D2D1_FACTORY_TYPE_SINGLE_THREADED,
191 | win32.IID_ID2D1Factory,
192 | null,
193 | @ptrCast(&self.pFactory),
194 | ))) {
195 | return -1; // Fail CreateWindowEx.
196 | }
197 | return 0;
198 | },
199 | win32.WM_DESTROY => {
200 | self.DiscardGraphicsResources();
201 | SafeRelease(&self.pFactory);
202 | win32.PostQuitMessage(0);
203 | return 0;
204 | },
205 | win32.WM_PAINT => {
206 | self.OnPaint();
207 | return 0;
208 | },
209 | // Other messages not shown...
210 | win32.WM_SIZE => {
211 | self.Resize();
212 | return 0;
213 | },
214 | else => {},
215 | }
216 | return win32.DefWindowProc(self.base.m_hwnd.?, uMsg, wParam, lParam);
217 | }
218 |
219 | // TODO: tthis D2D1 namespace is referenced in the C++ example but it doesn't exist in win32metadata
220 | const D2D1 = struct {
221 | // TODO: SkyBlue is missing from win32metadata? file an issue?
222 | pub const SkyBlue = 0x87CEEB;
223 |
224 | // TODO: this is missing
225 | pub fn ColorF(o: struct { r: f32, g: f32, b: f32, a: f32 = 1 }) win32.D2D_COLOR_F {
226 | return .{ .r = o.r, .g = o.g, .b = o.b, .a = o.a };
227 | }
228 |
229 | // TODO: this is missing
230 | pub fn ColorFU32(o: struct { rgb: u32, a: f32 = 1 }) win32.D2D_COLOR_F {
231 | return .{
232 | .r = @as(f32, @floatFromInt((o.rgb >> 16) & 0xff)) / 255,
233 | .g = @as(f32, @floatFromInt((o.rgb >> 8) & 0xff)) / 255,
234 | .b = @as(f32, @floatFromInt((o.rgb >> 0) & 0xff)) / 255,
235 | .a = o.a,
236 | };
237 | }
238 |
239 | pub fn Point2F(x: f32, y: f32) win32.D2D_POINT_2F {
240 | return .{ .x = x, .y = y };
241 | }
242 |
243 | pub fn Ellipse(center: win32.D2D_POINT_2F, radiusX: f32, radiusY: f32) win32.D2D1_ELLIPSE {
244 | return .{
245 | .point = center,
246 | .radiusX = radiusX,
247 | .radiusY = radiusY,
248 | };
249 | }
250 |
251 | // TODO: this is missing
252 | pub fn RenderTargetProperties() win32.D2D1_RENDER_TARGET_PROPERTIES {
253 | return .{
254 | .type = win32.D2D1_RENDER_TARGET_TYPE_DEFAULT,
255 | .pixelFormat = PixelFormat(),
256 | .dpiX = 0,
257 | .dpiY = 0,
258 | .usage = win32.D2D1_RENDER_TARGET_USAGE_NONE,
259 | .minLevel = win32.D2D1_FEATURE_LEVEL_DEFAULT,
260 | };
261 | }
262 |
263 | // TODO: this is missing
264 | pub fn PixelFormat() win32.D2D1_PIXEL_FORMAT {
265 | return .{
266 | .format = win32.DXGI_FORMAT_UNKNOWN,
267 | .alphaMode = win32.D2D1_ALPHA_MODE_UNKNOWN,
268 | };
269 | }
270 |
271 | // TODO: this is missing
272 | pub fn HwndRenderTargetProperties(hwnd: HWND, size: D2D_SIZE_U) win32.D2D1_HWND_RENDER_TARGET_PROPERTIES {
273 | return .{
274 | .hwnd = hwnd,
275 | .pixelSize = size,
276 | .presentOptions = win32.D2D1_PRESENT_OPTIONS_NONE,
277 | };
278 | }
279 | };
280 |
--------------------------------------------------------------------------------
/examples/helloworld-window.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const WINAPI = std.os.windows.WINAPI;
3 |
4 | pub const UNICODE = true;
5 | const win32 = struct {
6 | usingnamespace @import("win32").zig;
7 | usingnamespace @import("win32").foundation;
8 | usingnamespace @import("win32").system.system_services;
9 | usingnamespace @import("win32").ui.windows_and_messaging;
10 | usingnamespace @import("win32").graphics.gdi;
11 | };
12 | const L = win32.L;
13 | const HWND = win32.HWND;
14 |
15 | fn fatal(comptime fmt: []const u8, args: anytype) noreturn {
16 | if (std.fmt.allocPrintZ(std.heap.page_allocator, fmt, args)) |msg| {
17 | _ = win32.MessageBoxA(null, msg, "Fatal Error", .{});
18 | } else |e| switch (e) {
19 | error.OutOfMemory => _ = win32.MessageBoxA(null, "Out of memory", "Fatal Error", .{}),
20 | }
21 | std.process.exit(1);
22 | }
23 |
24 | pub export fn wWinMain(
25 | hInstance: win32.HINSTANCE,
26 | _: ?win32.HINSTANCE,
27 | pCmdLine: [*:0]u16,
28 | nCmdShow: u32,
29 | ) callconv(WINAPI) c_int {
30 | _ = pCmdLine;
31 | _ = nCmdShow;
32 |
33 | const CLASS_NAME = L("Sample Window Class");
34 | const wc = win32.WNDCLASS{
35 | .style = .{},
36 | .lpfnWndProc = WindowProc,
37 | .cbClsExtra = 0,
38 | .cbWndExtra = 0,
39 | .hInstance = hInstance,
40 | .hIcon = null,
41 | .hCursor = null,
42 | .hbrBackground = null,
43 | // TODO: this field is not marked as options so we can't use null atm
44 | .lpszMenuName = L("Some Menu Name"),
45 | .lpszClassName = CLASS_NAME,
46 | };
47 |
48 | if (0 == win32.RegisterClass(&wc))
49 | fatal("RegisterClass failed, error={}", .{win32.GetLastError()});
50 |
51 | const hwnd = win32.CreateWindowEx(.{}, CLASS_NAME, L("Hello Windows"), win32.WS_OVERLAPPEDWINDOW, win32.CW_USEDEFAULT, win32.CW_USEDEFAULT, // Position
52 | 400, 200, // Size
53 | null, // Parent window
54 | null, // Menu
55 | hInstance, // Instance handle
56 | null // Additional application data
57 | ) orelse fatal("CreateWindow failed, error={}", .{win32.GetLastError()});
58 |
59 | _ = win32.ShowWindow(hwnd, .{ .SHOWNORMAL = 1 });
60 |
61 | var msg: win32.MSG = undefined;
62 | while (win32.GetMessage(&msg, null, 0, 0) != 0) {
63 | _ = win32.TranslateMessage(&msg);
64 | _ = win32.DispatchMessage(&msg);
65 | }
66 | return @intCast(msg.wParam);
67 | }
68 |
69 | fn WindowProc(
70 | hwnd: HWND,
71 | uMsg: u32,
72 | wParam: win32.WPARAM,
73 | lParam: win32.LPARAM,
74 | ) callconv(WINAPI) win32.LRESULT {
75 | switch (uMsg) {
76 | win32.WM_DESTROY => {
77 | win32.PostQuitMessage(0);
78 | return 0;
79 | },
80 | win32.WM_PAINT => {
81 | var ps: win32.PAINTSTRUCT = undefined;
82 | const hdc = win32.BeginPaint(hwnd, &ps);
83 | _ = win32.FillRect(hdc, &ps.rcPaint, @ptrFromInt(@intFromEnum(win32.COLOR_WINDOW) + 1));
84 | _ = win32.TextOutA(hdc, 20, 20, "Hello", 5);
85 | _ = win32.EndPaint(hwnd, &ps);
86 | return 0;
87 | },
88 | else => {},
89 | }
90 | return win32.DefWindowProc(hwnd, uMsg, wParam, lParam);
91 | }
92 |
--------------------------------------------------------------------------------
/examples/helloworld.zig:
--------------------------------------------------------------------------------
1 | const win32 = @import("win32").everything;
2 |
3 | pub export fn WinMainCRTStartup() callconv(@import("std").os.windows.WINAPI) noreturn {
4 | const hStdOut = win32.GetStdHandle(win32.STD_OUTPUT_HANDLE);
5 | if (hStdOut == win32.INVALID_HANDLE_VALUE) {
6 | //std.debug.warn("Error: GetStdHandle failed with {}\n", .{GetLastError()});
7 | win32.ExitProcess(255);
8 | }
9 | writeAll(hStdOut, "Hello, World!") catch win32.ExitProcess(255); // fail
10 | win32.ExitProcess(0);
11 | }
12 |
13 | fn writeAll(hFile: win32.HANDLE, buffer: []const u8) !void {
14 | var written: usize = 0;
15 | while (written < buffer.len) {
16 | const next_write = @as(u32, @intCast(0xFFFFFFFF & (buffer.len - written)));
17 | var last_written: u32 = undefined;
18 | if (1 != win32.WriteFile(hFile, buffer.ptr + written, next_write, &last_written, null)) {
19 | // TODO: return from GetLastError
20 | return error.WriteFileFailed;
21 | }
22 | written += last_written;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/net.zig:
--------------------------------------------------------------------------------
1 | //! test basic network functionality
2 | const std = @import("std");
3 |
4 | const win32 = struct {
5 | usingnamespace @import("win32").foundation;
6 | usingnamespace @import("win32").networking.win_sock;
7 | usingnamespace @import("win32").network_management.ip_helper;
8 | };
9 |
10 | pub fn main() void {
11 | const s = win32.socket(@intFromEnum(win32.AF_INET), win32.SOCK_STREAM, @intFromEnum(win32.IPPROTO_TCP));
12 | if (s == win32.INVALID_SOCKET) {
13 | std.log.err("socket failed, error={}", .{win32.GetLastError()});
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/examples/opendialog.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | pub const UNICODE = true;
3 |
4 | const WINAPI = @import("std").os.windows.WINAPI;
5 |
6 | const win32 = @import("win32").everything;
7 |
8 | pub const panic = win32.messageBoxThenPanic(.{ .title = "Opendialog Example Panic" });
9 |
10 | pub export fn wWinMain(__: win32.HINSTANCE, _: ?win32.HINSTANCE, ___: [*:0]u16, ____: u32) callconv(WINAPI) c_int {
11 | _ = __;
12 | _ = ___;
13 | _ = ____;
14 |
15 | {
16 | const hr = win32.CoInitializeEx(null, win32.COINIT{
17 | .APARTMENTTHREADED = 1,
18 | .DISABLE_OLE1DDE = 1,
19 | });
20 | if (win32.FAILED(hr)) win32.panicHresult("CoInitiailizeEx", hr);
21 | }
22 | defer win32.CoUninitialize();
23 |
24 | const dialog = blk: {
25 | var dialog: ?*win32.IFileOpenDialog = undefined;
26 | const hr = win32.CoCreateInstance(
27 | win32.CLSID_FileOpenDialog,
28 | null,
29 | win32.CLSCTX_ALL,
30 | win32.IID_IFileOpenDialog,
31 | @ptrCast(&dialog),
32 | );
33 | if (win32.FAILED(hr)) win32.panicHresult("create FileOpenDialog", hr);
34 | break :blk dialog.?;
35 | };
36 | defer _ = dialog.IUnknown.Release();
37 |
38 | {
39 | const hr = dialog.IModalWindow.Show(null);
40 | if (win32.FAILED(hr)) win32.panicHresult("show dialog", hr);
41 | }
42 |
43 | var pItem: ?*win32.IShellItem = undefined;
44 | {
45 | const hr = dialog.IFileDialog.GetResult(&pItem);
46 | if (win32.FAILED(hr)) win32.panicHresult("get dialog result", hr);
47 | }
48 | defer _ = pItem.?.IUnknown.Release();
49 |
50 | const file_path = blk: {
51 | var file_path: ?[*:0]u16 = undefined;
52 | const hr = pItem.?.GetDisplayName(win32.SIGDN_FILESYSPATH, &file_path);
53 | if (win32.FAILED(hr)) win32.panicHresult("GetDisplayName", hr);
54 | break :blk file_path.?;
55 | };
56 | defer win32.CoTaskMemFree(file_path);
57 | _ = win32.MessageBoxW(null, file_path, win32.L("File Path"), win32.MB_OK);
58 | return 0;
59 | }
60 |
--------------------------------------------------------------------------------
/examples/tests.zig:
--------------------------------------------------------------------------------
1 | const win32 = @import("win32").everything;
2 |
3 | pub fn main() void {
4 | {
5 | const event = win32.CreateEventW(null, 0, 0, null) orelse @panic("CreateEvent failed");
6 | defer win32.closeHandle(event);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/testwindow.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const WINAPI = std.os.windows.WINAPI;
3 |
4 | const win32 = @import("win32").everything;
5 | const L = win32.L;
6 | const HWND = win32.HWND;
7 |
8 | pub const panic = win32.messageBoxThenPanic(.{ .title = "Zigwin32 TestWindow Panic" });
9 |
10 | pub export fn wWinMain(
11 | hInstance: win32.HINSTANCE,
12 | _: ?win32.HINSTANCE,
13 | pCmdLine: [*:0]u16,
14 | nCmdShow: u32,
15 | ) callconv(WINAPI) c_int {
16 | _ = pCmdLine;
17 | _ = nCmdShow;
18 |
19 | std.debug.assert(0x5678 == win32.loword(@as(i32, 0x12345678)));
20 | std.debug.assert(0x1234 == win32.hiword(@as(i32, 0x12345678)));
21 |
22 | const CLASS_NAME = L("ZigTestWindow");
23 | const wc = win32.WNDCLASSW{
24 | .style = .{},
25 | .lpfnWndProc = WindowProc,
26 | .cbClsExtra = 0,
27 | .cbWndExtra = @sizeOf(usize),
28 | .hInstance = hInstance,
29 | .hIcon = null,
30 | .hCursor = null,
31 | .hbrBackground = null,
32 | // TODO: this field is not marked as options so we can't use null atm
33 | .lpszMenuName = L("Some Menu Name"),
34 | .lpszClassName = CLASS_NAME,
35 | };
36 |
37 | if (0 == win32.RegisterClassW(&wc))
38 | std.debug.panic("RegisterClass failed, error={}", .{win32.GetLastError()});
39 |
40 | const hwnd = win32.CreateWindowExW(
41 | .{},
42 | CLASS_NAME,
43 | L("Zigwin32 Test Window"),
44 | win32.WS_OVERLAPPEDWINDOW,
45 | win32.CW_USEDEFAULT, // x
46 | win32.CW_USEDEFAULT, // y
47 | 0, // width
48 | 0, // height
49 | null, // parent window
50 | null, // menu
51 | hInstance,
52 | null, // Additional application data
53 | ) orelse std.debug.panic("CreateWindow failed, error={}", .{win32.GetLastError()});
54 |
55 | const dpi = win32.dpiFromHwnd(hwnd);
56 |
57 | // just test the api works
58 | _ = win32.scaleFromDpi(f32, dpi);
59 | _ = win32.scaleFromDpi(f64, dpi);
60 | _ = win32.scaleFromDpi(f128, dpi);
61 | _ = win32.scaleDpi(i32, 100, dpi);
62 | _ = win32.scaleDpi(f32, 100, dpi);
63 |
64 | if (0 == win32.SetWindowPos(
65 | hwnd,
66 | null,
67 | 0,
68 | 0,
69 | win32.scaleDpi(i32, 600, dpi),
70 | win32.scaleDpi(i32, 400, dpi),
71 | .{ .NOMOVE = 1 },
72 | )) std.debug.panic("SetWindowPos failed, error={}", .{win32.GetLastError()});
73 |
74 | _ = win32.ShowWindow(hwnd, .{ .SHOWNORMAL = 1 });
75 |
76 | var msg: win32.MSG = undefined;
77 | while (win32.GetMessageW(&msg, null, 0, 0) != 0) {
78 | _ = win32.TranslateMessage(&msg);
79 | _ = win32.DispatchMessageW(&msg);
80 | }
81 | return @intCast(msg.wParam);
82 | }
83 |
84 | fn WindowProc(
85 | hwnd: HWND,
86 | uMsg: u32,
87 | wParam: win32.WPARAM,
88 | lParam: win32.LPARAM,
89 | ) callconv(WINAPI) win32.LRESULT {
90 | switch (uMsg) {
91 | win32.WM_CREATE => {
92 | if (win32.has_window_longptr) {
93 | std.debug.assert(0 == win32.setWindowLongPtrW(hwnd, 0, 0x1234));
94 | std.debug.assert(0x1234 == win32.getWindowLongPtrW(hwnd, 0));
95 | }
96 | },
97 | win32.WM_DESTROY => {
98 | win32.PostQuitMessage(0);
99 | return 0;
100 | },
101 | win32.WM_DPICHANGED => win32.invalidateHwnd(hwnd),
102 | win32.WM_PAINT => {
103 | // some of these methods aren't really doing anything, just
104 | // testing various apis.
105 | _ = win32.pointFromLparam(lParam);
106 |
107 | const client_size = win32.getClientSize(hwnd);
108 | _ = client_size;
109 |
110 | const hdc, const ps = win32.beginPaint(hwnd);
111 | defer win32.endPaint(hwnd, &ps);
112 |
113 | {
114 | const tmp_hdc = win32.CreateCompatibleDC(hdc);
115 | win32.deleteDc(tmp_hdc);
116 | }
117 |
118 | {
119 | const brush = win32.createSolidBrush(0xff00ffff);
120 | defer win32.deleteObject(brush);
121 | win32.fillRect(hdc, ps.rcPaint, brush);
122 | }
123 |
124 | var y: i32 = 10;
125 |
126 | {
127 | const msg = win32.L("A window for testing things.");
128 | win32.textOutW(hdc, 20, y, msg);
129 | y += win32.getTextExtentW(hdc, msg).cy;
130 | }
131 | {
132 | const msg = "Testing TextOutA";
133 | win32.textOutA(hdc, 20, y, msg);
134 | y += win32.getTextExtentA(hdc, msg).cy;
135 | }
136 |
137 | {
138 | var buf: [100]u8 = undefined;
139 | const text = std.fmt.bufPrint(
140 | @ptrCast(&buf),
141 | "dpi is {}",
142 | .{win32.dpiFromHwnd(hwnd)},
143 | ) catch unreachable;
144 | win32.textOutA(hdc, 20, y, text);
145 | }
146 |
147 | _ = win32.EndPaint(hwnd, &ps);
148 | return 0;
149 | },
150 | else => {},
151 | }
152 | return win32.DefWindowProcW(hwnd, uMsg, wParam, lParam);
153 | }
154 |
--------------------------------------------------------------------------------
/examples/unionpointers.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | pub const UNICODE = true;
4 | const win32 = struct {
5 | usingnamespace @import("win32").zig;
6 | usingnamespace @import("win32").foundation;
7 | usingnamespace @import("win32").system.system_services;
8 | usingnamespace @import("win32").ui.windows_and_messaging;
9 | usingnamespace @import("win32").graphics.gdi;
10 | };
11 | const L = win32.L;
12 | const HWND = win32.HWND;
13 |
14 | pub const panic = win32.messageBoxThenPanic(.{ .title = "Unionpointers Example Panic" });
15 |
16 | pub export fn wWinMain(
17 | hInstance: win32.HINSTANCE,
18 | _: ?win32.HINSTANCE,
19 | pCmdLine: [*:0]u16,
20 | nCmdShow: u32,
21 | ) callconv(std.os.windows.WINAPI) c_int {
22 | _ = pCmdLine;
23 | _ = nCmdShow;
24 |
25 | for (0..20) |atom| {
26 | // we don't care if this works, we're just verifying @ptrFromInt(atom)
27 | // doesn't trigger a runtime panic
28 | if (win32.CreateWindowEx(
29 | .{},
30 | @ptrFromInt(atom),
31 | L("Test Window"),
32 | .{},
33 | 0,
34 | 0,
35 | 0,
36 | 0,
37 | null,
38 | null,
39 | hInstance,
40 | null,
41 | )) |hwnd| {
42 | std.log.info("atom={} CreateWindow success", .{atom});
43 | if (0 == win32.DestroyWindow(hwnd)) win32.panicWin32(
44 | "DestroyWindow",
45 | win32.GetLastError(),
46 | );
47 | } else {
48 | std.log.info("atom={} CreateWindow failed, error={} (this is fine)", .{ atom, win32.GetLastError() });
49 | }
50 | }
51 |
52 | {
53 | const CLASS_NAME = L("Sample Window Class");
54 | const wc = win32.WNDCLASS{
55 | .style = .{},
56 | .lpfnWndProc = WindowProc,
57 | .cbClsExtra = 0,
58 | .cbWndExtra = 0,
59 | .hInstance = hInstance,
60 | .hIcon = null,
61 | .hCursor = null,
62 | .hbrBackground = null,
63 | // TODO: this field is not marked as options so we can't use null atm
64 | .lpszMenuName = L("Some Menu Name"),
65 | .lpszClassName = CLASS_NAME,
66 | };
67 | const atom = win32.RegisterClass(&wc);
68 | if (0 == atom) win32.panicWin32("RegisterClass", win32.GetLastError());
69 | const hwnd = win32.CreateWindowEx(
70 | .{},
71 | @ptrFromInt(atom),
72 | L("Test Window"),
73 | .{},
74 | 0,
75 | 0,
76 | 0,
77 | 0,
78 | null, // Parent window
79 | null, // Menu
80 | hInstance, // Instance handle
81 | null, // Additional application data
82 | ) orelse win32.panicWin32("CreateWindow", win32.GetLastError());
83 | if (0 == win32.DestroyWindow(hwnd)) win32.panicWin32("DestroyWindow", win32.GetLastError());
84 | }
85 |
86 | {
87 | // Well this sucks...ptr and const cast?
88 | const old_cursor = win32.SetCursor(@constCast(@ptrCast(win32.IDC_ARROW)));
89 | defer _ = win32.SetCursor(old_cursor);
90 | _ = win32.SetCursor(@constCast(@ptrCast(win32.IDC_IBEAM)));
91 | _ = win32.SetCursor(@constCast(@ptrCast(win32.IDC_WAIT)));
92 | }
93 |
94 | std.log.info("success!", .{});
95 | return 0;
96 | }
97 |
98 | fn WindowProc(
99 | hwnd: HWND,
100 | uMsg: u32,
101 | wParam: win32.WPARAM,
102 | lParam: win32.LPARAM,
103 | ) callconv(std.os.windows.WINAPI) win32.LRESULT {
104 | switch (uMsg) {
105 | win32.WM_DESTROY => {
106 | win32.PostQuitMessage(0);
107 | return 0;
108 | },
109 | else => {},
110 | }
111 | return win32.DefWindowProc(hwnd, uMsg, wParam, lParam);
112 | }
113 |
--------------------------------------------------------------------------------
/examples/wasapi.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | const log = std.log.info;
4 |
5 | const win32 = struct {
6 | usingnamespace @import("win32").foundation;
7 | usingnamespace @import("win32").media.audio;
8 | usingnamespace @import("win32").media.audio.direct_music;
9 | usingnamespace @import("win32").storage.structured_storage;
10 | usingnamespace @import("win32").system.com;
11 | usingnamespace @import("win32").system.com.structured_storage;
12 | usingnamespace @import("win32").ui.shell.properties_system;
13 | usingnamespace @import("win32").zig;
14 | };
15 |
16 | pub fn getDefaultDevice() !void {
17 | var enumerator: *win32.IMMDeviceEnumerator = undefined;
18 |
19 | {
20 | const status = win32.CoCreateInstance(
21 | win32.CLSID_MMDeviceEnumerator,
22 | null,
23 | win32.CLSCTX_ALL,
24 | win32.IID_IMMDeviceEnumerator,
25 | @ptrCast(&enumerator),
26 | );
27 | if (win32.FAILED(status)) {
28 | log("CoCreateInstance FAILED: {d}", .{status});
29 | return error.Fail;
30 | }
31 | }
32 | defer _ = enumerator.IUnknown.Release();
33 |
34 | log("pre enumerator: {}", .{enumerator});
35 |
36 | var device: ?*win32.IMMDevice = undefined;
37 | {
38 | const status = enumerator.GetDefaultAudioEndpoint(
39 | win32.EDataFlow.eCapture,
40 | win32.ERole.eCommunications,
41 | &device,
42 | );
43 | if (win32.FAILED(status)) {
44 | log("DEVICE STATUS: {d}", .{status});
45 | return error.Fail;
46 | }
47 | }
48 | defer _ = device.?.IUnknown.Release(); // No such method
49 |
50 | var properties: ?*win32.IPropertyStore = undefined;
51 | {
52 | const status = device.?.OpenPropertyStore(win32.STGM_READ, &properties);
53 | if (win32.FAILED(status)) {
54 | log("DEVICE PROPS: {d}", .{status});
55 | return error.Fail;
56 | }
57 | }
58 |
59 | var count: u32 = 0;
60 | {
61 | const status = properties.?.GetCount(&count);
62 | if (win32.FAILED(status)) {
63 | log("GetCount failed: {d}", .{status});
64 | return error.Fail;
65 | }
66 | }
67 |
68 | var index: u32 = 0;
69 | while (index < count - 1) : (index += 1) {
70 | var propKey: win32.PROPERTYKEY = undefined;
71 |
72 | log("index: {d}", .{index});
73 | {
74 | const status = properties.?.GetAt(index, &propKey);
75 | if (win32.FAILED(status)) {
76 | log("Failed to getAt {x}", .{status});
77 | return error.Fail;
78 | }
79 | }
80 | log("Looping propeties with: {}", .{propKey});
81 |
82 | var propValue: win32.PROPVARIANT = undefined;
83 | // The following line fails with a stack trace (pasted below)
84 | const status = properties.?.GetValue(&propKey, &propValue);
85 | _ = status;
86 | }
87 |
88 | // log("post device: {s}", .{device.?.IMMDevice_GetId()});
89 | }
90 |
91 | pub fn main() !u8 {
92 | const config_value = win32.COINIT{ .APARTMENTTHREADED = 1, .DISABLE_OLE1DDE = 1 };
93 | {
94 | _ = config_value;
95 | const status = win32.CoInitialize(null); // CoInitializeEx(null, @intToEnum(COINIT, config_value));
96 | if (win32.FAILED(status)) {
97 | log("CoInitialize FAILED: {d}", .{status});
98 | return error.Fail;
99 | }
100 | }
101 | // TODO: I'm not sure if we should do this or not
102 | //defer CoUninitialize();
103 |
104 | try getDefaultDevice();
105 | return 0;
106 | }
107 |
--------------------------------------------------------------------------------
/examples/win32.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | true/pm
6 | PerMonitorV2
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/extra.txt:
--------------------------------------------------------------------------------
1 | #
2 | # Format
3 | # --------------------------------------------------------------------------------
4 | # :
5 | # Constant
6 | # Function +
7 | #
8 | # :
9 | # '(' + ')'
10 | # :
11 | # UnionPointer
12 | # NotNull '=' [01]+
13 | #
14 | Networking.WinSock Function accept return(NotNull=1)
15 | Networking.WinSock Function shutdown return(NotNull=1)
16 | Networking.WinSock Function socket return(NotNull=1)
17 | Networking.WinSock Function WSASocketA return(NotNull=1)
18 | Networking.WinSock Function WSASocketW return(NotNull=1)
19 |
20 | Storage.FileSystem Function CreateFileA return(NotNull=1)
21 | Storage.FileSystem Function CreateFileW return(NotNull=1)
22 |
23 | System.Com Function CoCreateInstance riid(NotNull=1)
24 | System.Com Function IUnknown.QueryInterface riid(NotNull=1)
25 |
26 | System.Console Function GetStdHandle return(NotNull=1)
27 |
28 | System.Diagnostics.ToolHelp Function CreateToolhelp32Snapshot return(NotNull=1)
29 |
30 | System.LibraryLoader Function ENUMRESLANGPROCW lpType(UnionPointer)
31 | System.LibraryLoader Function ENUMRESNAMEPROCW lpType(UnionPointer)
32 | System.LibraryLoader Function EnumResourceLanguagesW lpType(UnionPointer)
33 | System.LibraryLoader Function EnumResourceLanguagesExW lpType(UnionPointer)
34 | System.LibraryLoader Function EnumResourceNamesW lpType(UnionPointer)
35 | System.LibraryLoader Function EnumResourceNamesExW lpType(UnionPointer)
36 | System.LibraryLoader Function FindResourceW lpType(UnionPointer) lpName(UnionPointer)
37 | System.LibraryLoader Function FindResourceExW lpType(UnionPointer) lpName(UnionPointer)
38 | System.LibraryLoader Function UpdateResourceW lpType(UnionPointer) lpName(UnionPointer)
39 |
40 | System.Mailslots Function CreateMailslotA return(NotNull=1)
41 | System.Mailslots Function CreateMailslotW return(NotNull=1)
42 |
43 | System.Pipes Function CreateNamedPipeA return(NotNull=1)
44 | System.Pipes Function CreateNamedPipeW return(NotNull=1)
45 |
46 | UI.Controls Constant TD_WARNING_ICON(UnionPointer)
47 | UI.Controls Constant TD_ERROR_ICON(UnionPointer)
48 | UI.Controls Constant TD_INFORMATION_ICON(UnionPointer)
49 | UI.Controls Constant TD_SHIELD_ICON(UnionPointer)
50 |
51 | UI.WindowsAndMessaging Constant IDC_ARROW(UnionPointer)
52 | UI.WindowsAndMessaging Constant IDC_IBEAM(UnionPointer)
53 | UI.WindowsAndMessaging Constant IDC_WAIT(UnionPointer)
54 | UI.WindowsAndMessaging Constant IDC_CROSS(UnionPointer)
55 | UI.WindowsAndMessaging Constant IDC_UPARROW(UnionPointer)
56 | UI.WindowsAndMessaging Constant IDC_SIZE(UnionPointer)
57 | UI.WindowsAndMessaging Constant IDC_ICON(UnionPointer)
58 | UI.WindowsAndMessaging Constant IDC_SIZENWSE(UnionPointer)
59 | UI.WindowsAndMessaging Constant IDC_SIZENESW(UnionPointer)
60 | UI.WindowsAndMessaging Constant IDC_SIZEWE(UnionPointer)
61 | UI.WindowsAndMessaging Constant IDC_SIZENS(UnionPointer)
62 | UI.WindowsAndMessaging Constant IDC_SIZEALL(UnionPointer)
63 | UI.WindowsAndMessaging Constant IDC_NO(UnionPointer)
64 | UI.WindowsAndMessaging Constant IDC_HAND(UnionPointer)
65 | UI.WindowsAndMessaging Constant IDC_APPSTARTING(UnionPointer)
66 | UI.WindowsAndMessaging Constant IDC_HELP(UnionPointer)
67 | UI.WindowsAndMessaging Constant IDC_PIN(UnionPointer)
68 | UI.WindowsAndMessaging Constant IDC_PERSON(UnionPointer)
69 | UI.WindowsAndMessaging Constant IDI_APPLICATION(UnionPointer)
70 | UI.WindowsAndMessaging Constant IDI_HAND(UnionPointer)
71 | UI.WindowsAndMessaging Constant IDI_QUESTION(UnionPointer)
72 | UI.WindowsAndMessaging Constant IDI_EXCLAMATION(UnionPointer)
73 | UI.WindowsAndMessaging Constant IDI_ASTERISK(UnionPointer)
74 | UI.WindowsAndMessaging Constant IDI_WINLOGO(UnionPointer)
75 | UI.WindowsAndMessaging Constant IDI_SHIELD(UnionPointer)
76 | UI.WindowsAndMessaging Constant RT_VERSION(UnionPointer)
77 | UI.WindowsAndMessaging Constant RT_DLGINCLUDE(UnionPointer)
78 | UI.WindowsAndMessaging Constant RT_PLUGPLAY(UnionPointer)
79 | UI.WindowsAndMessaging Constant RT_VXD(UnionPointer)
80 | UI.WindowsAndMessaging Constant RT_ANICURSOR(UnionPointer)
81 | UI.WindowsAndMessaging Constant RT_ANIICON(UnionPointer)
82 | UI.WindowsAndMessaging Constant RT_HTML(UnionPointer)
83 | UI.WindowsAndMessaging Constant RT_CURSOR(UnionPointer)
84 | UI.WindowsAndMessaging Constant RT_BITMAP(UnionPointer)
85 | UI.WindowsAndMessaging Constant RT_ICON(UnionPointer)
86 | UI.WindowsAndMessaging Constant RT_MENU(UnionPointer)
87 | UI.WindowsAndMessaging Constant RT_DIALOG(UnionPointer)
88 | UI.WindowsAndMessaging Constant RT_FONTDIR(UnionPointer)
89 | UI.WindowsAndMessaging Constant RT_FONT(UnionPointer)
90 | UI.WindowsAndMessaging Constant RT_ACCELERATOR(UnionPointer)
91 | UI.WindowsAndMessaging Constant RT_MESSAGETABLE(UnionPointer)
92 |
93 | UI.WindowsAndMessaging Function CreateWindowExA lpClassName(UnionPointer)
94 | UI.WindowsAndMessaging Function CreateWindowExW lpClassName(UnionPointer)
95 | UI.WindowsAndMessaging Function UnregisterClassA lpClassName(UnionPointer)
96 | UI.WindowsAndMessaging Function UnregisterClassW lpClassName(UnionPointer)
97 | UI.WindowsAndMessaging Function LoadCursorW lpCursorName(UnionPointer)
98 | UI.WindowsAndMessaging Function LoadIconW lpIconName(UnionPointer)
99 | UI.WindowsAndMessaging Function LoadImageA name(UnionPointer)
100 | UI.WindowsAndMessaging Function LoadImageW name(UnionPointer)
101 | UI.WindowsAndMessaging Function SENDASYNCPROC param0(NotNull=1)
102 | UI.WindowsAndMessaging Function DLGPROC param0(NotNull=1)
103 | UI.WindowsAndMessaging Function PROPENUMPROCA param0(NotNull=1)
104 | UI.WindowsAndMessaging Function PROPENUMPROCW param0(NotNull=1)
105 | UI.WindowsAndMessaging Function PROPENUMPROCEXA param0(NotNull=1)
106 | UI.WindowsAndMessaging Function PROPENUMPROCEXW param0(NotNull=1)
107 | UI.WindowsAndMessaging Function TIMERPROC param0(NotNull=1)
108 | UI.WindowsAndMessaging Function WNDENUMPROC param0(NotNull=1)
109 | UI.WindowsAndMessaging Function WNDPROC param0(NotNull=1)
110 |
--------------------------------------------------------------------------------
/src/StringPool.zig:
--------------------------------------------------------------------------------
1 | /// Takes an allocator and manages a set of strings.
2 | /// Every string in the pool is owned by the pool.
3 | const StringPool = @This();
4 |
5 | const std = @import("std");
6 | const StringHashMap = std.hash_map.StringHashMap;
7 |
8 | pub const Val = struct {
9 | slice: []const u8,
10 | pub fn eql(self: Val, other: Val) bool {
11 | return self.slice.ptr == other.slice.ptr;
12 | }
13 | pub fn format(
14 | self: @This(),
15 | comptime fmt: []const u8,
16 | options: std.fmt.FormatOptions,
17 | writer: anytype,
18 | ) !void {
19 | _ = fmt;
20 | _ = options;
21 | return writer.writeAll(self.slice);
22 | }
23 | };
24 |
25 | allocator: std.mem.Allocator,
26 | map: StringHashMap(Val),
27 | pub fn init(allocator: std.mem.Allocator) @This() {
28 | return @This(){
29 | .allocator = allocator,
30 | .map = StringHashMap(Val).init(allocator),
31 | };
32 | }
33 |
34 | pub fn deinit(self: *StringPool) void {
35 | var it = self.map.iterator();
36 | while (it.next()) |entry| {
37 | self.allocator.free(entry.value_ptr.slice);
38 | }
39 | self.map.deinit();
40 | }
41 |
42 | /// If the pool already contains this a string that matches the contents
43 | /// of the given string, return the existing string from this pool.
44 | /// Otherwise, create a copy of this string, add it to the pool and return
45 | /// the new copy.
46 | pub fn add(self: *@This(), s: []const u8) error{OutOfMemory}!Val {
47 | if (self.map.get(s)) |entry| {
48 | return entry;
49 | }
50 | const newString = try self.allocator.alloc(u8, s.len);
51 | @memcpy(newString, s);
52 | const val = Val{ .slice = newString };
53 | _ = try self.map.put(newString, val);
54 | return val;
55 | }
56 |
57 | pub fn addFormatted(self: *@This(), comptime fmt: []const u8, args: anytype) error{OutOfMemory}!Val {
58 | const s = try std.fmt.allocPrint(self.allocator, fmt, args);
59 | errdefer self.allocator.free(s);
60 |
61 | const val = try self.add(s);
62 | if (val.slice.ptr != s.ptr) {
63 | self.allocator.free(s);
64 | }
65 | return val;
66 | }
67 |
68 | pub const HashContext = struct {
69 | pub fn hash(self: HashContext, s: Val) u64 {
70 | _ = self;
71 | return std.hash.Wyhash.hash(0, @as([*]const u8, @ptrCast(&s.slice.ptr))[0..@sizeOf(usize)]);
72 | }
73 | pub fn eql(self: HashContext, a: Val, b: Val) bool {
74 | _ = self;
75 | return a.slice.ptr == b.slice.ptr;
76 | }
77 | };
78 | pub const ArrayHashContext = struct {
79 | pub fn hash(self: @This(), s: Val) u32 {
80 | _ = self;
81 | return @as(u32, @truncate(std.hash.Wyhash.hash(0, @as([*]const u8, @ptrCast(&s.slice.ptr))[0..@sizeOf(usize)])));
82 | }
83 | pub fn eql(self: @This(), a: Val, b: Val, index: usize) bool {
84 | _ = self;
85 | _ = index;
86 | return a.slice.ptr == b.slice.ptr;
87 | }
88 | };
89 |
90 | pub fn HashMap(comptime V: type) type {
91 | return std.HashMap(Val, V, HashContext, std.hash_map.default_max_load_percentage);
92 | }
93 | pub fn HashMapUnmanaged(comptime V: type) type {
94 | return std.HashMapUnmanaged(Val, V, HashContext, std.hash_map.default_max_load_percentage);
95 | }
96 |
97 | pub fn asciiLessThanIgnoreCase(_: void, lhs: Val, rhs: Val) bool {
98 | return std.ascii.lessThanIgnoreCase(lhs.slice, rhs.slice);
99 | }
100 |
101 | test "stringpool" {
102 | var pool = StringPool.init(std.testing.allocator);
103 | defer pool.deinit();
104 | const s = try pool.add("hello");
105 | {
106 | var buf: [5]u8 = undefined;
107 | std.mem.copy(u8, buf[0..], "hello");
108 | const s2 = try pool.add(buf[0..]);
109 | try std.testing.expect(s.slice.ptr == s2.slice.ptr);
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/cameltosnake.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | // TODO: should this be in std lib?
4 | pub fn camelToSnakeAlloc(a: std.mem.Allocator, camel: []const u8) ![]u8 {
5 | const snake = try a.alloc(u8, camelToSnakeLen(camel));
6 | errdefer a.free(snake);
7 | camelToSnake(snake, camel);
8 | return snake;
9 | }
10 |
11 | pub fn camelToSnakeLen(camel: []const u8) usize {
12 | var snake_len = camel.len;
13 | {
14 | var i: usize = 1;
15 | while (i < camel.len) : (i += 1) {
16 | if (std.ascii.isUpper(camel[i]) and std.ascii.isLower(camel[i - 1])) {
17 | snake_len += 1;
18 | }
19 | }
20 | }
21 | return snake_len;
22 | }
23 |
24 | pub fn camelToSnake(snake: []u8, camel: []const u8) void {
25 | if (camel.len == 0) return;
26 |
27 | snake[0] = asciiCharToLower(camel[0]);
28 |
29 | var snake_index: usize = 1;
30 | {
31 | var i: usize = 1;
32 | while (i < camel.len) : (i += 1) {
33 | const is_upper = std.ascii.isUpper(camel[i]);
34 | if (is_upper and std.ascii.isLower(camel[i - 1])) {
35 | snake[snake_index] = '_';
36 | snake_index += 1;
37 | }
38 | snake[snake_index] = asciiCharToLower(camel[i]);
39 | snake_index += 1;
40 | }
41 | }
42 | std.debug.assert(snake_index == snake.len);
43 | }
44 |
45 | fn testCamelToSnake(camel: []const u8, expected_snake: []const u8) void {
46 | const actual_snake = camelToSnakeAlloc(std.testing.allocator, camel) catch @panic("out of memory");
47 | defer std.testing.allocator.free(actual_snake);
48 | std.debug.print("test '{s}' expect '{s}'\n", .{ camel, expected_snake });
49 | std.debug.print("actual '{s}'\n", .{actual_snake});
50 | std.testing.expect(std.mem.eql(u8, expected_snake, actual_snake));
51 | }
52 |
53 | test "cameltosnake" {
54 | testCamelToSnake("", "");
55 | testCamelToSnake("a", "a");
56 | testCamelToSnake("A", "a");
57 |
58 | testCamelToSnake("abc", "abc");
59 |
60 | testCamelToSnake("Abc", "abc");
61 | testCamelToSnake("aBc", "a_bc");
62 | testCamelToSnake("abC", "ab_c");
63 |
64 | testCamelToSnake("AbC", "ab_c");
65 | testCamelToSnake("aBC", "a_bc");
66 |
67 | testCamelToSnake("ABC", "abc");
68 | }
69 |
70 | // TODO: this should already exist somewhere?
71 | fn asciiCharToLower(c: u8) u8 {
72 | return c + (if (std.ascii.isUpper(c)) @as(u8, 'a' - 'A') else 0);
73 | }
74 |
--------------------------------------------------------------------------------
/src/common.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const metadata = @import("metadata.zig");
3 |
4 | const path_sep = std.fs.path.sep_str;
5 |
6 | pub fn oom(e: error{OutOfMemory}) noreturn {
7 | @panic(@errorName(e));
8 | }
9 |
10 | pub fn fatal(comptime fmt: []const u8, args: anytype) noreturn {
11 | std.log.err(fmt, args);
12 | std.process.exit(0xff);
13 | }
14 |
15 | pub fn getcwd(a: std.mem.Allocator) ![]u8 {
16 | var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
17 | const path = try std.os.getcwd(&path_buf);
18 | const path_allocated = try a.alloc(u8, path.len);
19 | @memcpy(path_allocated, path);
20 | return path_allocated;
21 | }
22 |
23 | pub fn readApiList(api_dir: std.fs.Dir, api_list: *std.ArrayList([]const u8)) !void {
24 | var dir_it = api_dir.iterate();
25 | while (try dir_it.next()) |entry| {
26 | if (!std.mem.endsWith(u8, entry.name, ".json")) {
27 | std.log.err("expected all files to end in '.json' but got '{s}'\n", .{entry.name});
28 | return error.AlreadyReported;
29 | }
30 | try api_list.append(try api_list.allocator.dupe(u8, entry.name));
31 | }
32 | }
33 |
34 | pub fn asciiLessThanIgnoreCase(_: void, lhs: []const u8, rhs: []const u8) bool {
35 | return std.ascii.lessThanIgnoreCase(lhs, rhs);
36 | }
37 |
38 | fn SliceFormatter(comptime T: type, comptime spec: []const u8) type {
39 | return struct {
40 | slice: []const T,
41 | pub fn format(
42 | self: @This(),
43 | comptime fmt: []const u8,
44 | options: std.fmt.FormatOptions,
45 | writer: anytype,
46 | ) !void {
47 | _ = fmt;
48 | _ = options;
49 | var first: bool = true;
50 | for (self.slice) |e| {
51 | if (first) {
52 | first = false;
53 | } else {
54 | try writer.writeAll(", ");
55 | }
56 | try std.fmt.format(writer, "{" ++ spec ++ "}", .{e});
57 | }
58 | }
59 | };
60 | }
61 | pub fn formatSliceT(comptime T: type, comptime spec: []const u8, slice: []const T) SliceFormatter(T, spec) {
62 | return .{ .slice = slice };
63 | }
64 | // TODO: implement this
65 | //pub fn formatSlice(slice: anytype) SliceFormatter(T) {
66 | // return .{ .slice = slice };
67 | //}
68 |
69 | pub fn jsonPanic() noreturn {
70 | @panic("an assumption about the json format was violated");
71 | }
72 | pub fn jsonPanicMsg(comptime msg: []const u8, args: anytype) noreturn {
73 | std.debug.panic("an assumption about the json format was violated: " ++ msg, args);
74 | }
75 |
76 | pub fn jsonEnforce(cond: bool) void {
77 | if (!cond) {
78 | jsonPanic();
79 | }
80 | }
81 | pub fn jsonEnforceMsg(cond: bool, comptime msg: []const u8, args: anytype) void {
82 | if (!cond) {
83 | jsonPanicMsg(msg, args);
84 | }
85 | }
86 |
87 | const JsonFormatter = struct {
88 | value: std.json.Value,
89 | pub fn format(
90 | self: JsonFormatter,
91 | comptime fmt: []const u8,
92 | options: std.fmt.FormatOptions,
93 | writer: anytype,
94 | ) !void {
95 | _ = fmt;
96 | _ = options;
97 | switch (self.value) {
98 | // avoid issues where std.json adds quotes to big numbers
99 | // (potential fix: https://github.com/ziglang/zig/pull/16707)
100 | .integer => |i| try std.fmt.formatIntValue(i, "", .{}, writer),
101 | .number_string => |s| try writer.writeAll(s),
102 | else => try std.json.stringify(self.value, .{}, writer),
103 | }
104 | }
105 | };
106 | pub fn fmtJson(value: anytype) JsonFormatter {
107 | if (@TypeOf(value) == std.json.ObjectMap) {
108 | return .{ .value = .{ .object = value } };
109 | }
110 | if (@TypeOf(value) == std.json.Array) {
111 | return .{ .value = .{ .array = value } };
112 | }
113 | if (@TypeOf(value) == []std.json.Value) {
114 | return .{ .value = .{ .array = std.json.Array{ .items = value, .capacity = value.len, .allocator = undefined } } };
115 | }
116 | return .{ .value = value };
117 | }
118 |
119 | pub const ComInterface = struct {
120 | name: []const u8,
121 | api: []const u8,
122 | pub fn format(
123 | self: ComInterface,
124 | comptime fmt: []const u8,
125 | options: std.fmt.FormatOptions,
126 | writer: anytype,
127 | ) !void {
128 | _ = fmt;
129 | _ = options;
130 | try writer.print(
131 | "{{\"Kind\":\"ApiRef\",\"Name\":\"{s}\",\"TargetKind\":\"Com\",\"Api\":\"{s}\",\"Parents\":[]}}",
132 | .{ self.name, self.api },
133 | );
134 | }
135 | };
136 |
137 | pub fn getComInterface(type_ref: metadata.TypeRef) ComInterface {
138 | const api_ref = switch (type_ref) {
139 | .ApiRef => |r| r,
140 | else => jsonPanic(),
141 | };
142 | jsonEnforce(api_ref.TargetKind == .Com);
143 | jsonEnforce(api_ref.Parents.len == 0);
144 | return .{
145 | .api = api_ref.Api,
146 | .name = api_ref.Name,
147 | };
148 | }
149 |
150 | // TODO: this should be in std, maybe method on HashMap?
151 | pub fn allocMapValues(alloc: std.mem.Allocator, comptime T: type, map: anytype) ![]T {
152 | var values = try alloc.alloc(T, map.count());
153 | errdefer alloc.free(values);
154 | {
155 | var i: usize = 0;
156 | var it = map.iterator();
157 | while (it.next()) |entry| : (i += 1) {
158 | values[i] = entry.value_ptr.*;
159 | }
160 | std.debug.assert(i == map.count());
161 | }
162 | return values;
163 | }
164 |
--------------------------------------------------------------------------------
/src/diff.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | pub fn fatal(comptime fmt: []const u8, args: anytype) noreturn {
4 | std.log.err(fmt, args);
5 | std.process.exit(0xff);
6 | }
7 |
8 | fn usage(zigbuild: bool) !void {
9 | const options = "[--nofetch|-n]";
10 | const stderr = std.io.getStdErr().writer();
11 | const parts: struct {
12 | diffrepo: []const u8,
13 | generated: []const u8,
14 | } = blk: {
15 | if (zigbuild) {
16 | try stderr.print("Usage: zig build diff -- {s}\n", .{options});
17 | break :blk .{
18 | .diffrepo = "the 'diffrepo' subdirectory",
19 | .generated = "the latest generated release",
20 | };
21 | }
22 | try stderr.print("Usage: diff.exe {s} DIFF_REPO GENERATED_PATH\n", .{options});
23 | break :blk .{
24 | .diffrepo = "DIFF_REPO",
25 | .generated = "GENERATED_PATH",
26 | };
27 | };
28 | try stderr.print(
29 | \\
30 | \\Updates {s} and installs {s}
31 | \\on top for diffing purposes.
32 | \\
33 | \\ --nofetch | -n Disable fetching the latest 'main' branch for
34 | \\ {0s}.
35 | \\
36 | ,
37 | .{ parts.diffrepo, parts.generated },
38 | );
39 | }
40 |
41 | pub fn main() !void {
42 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
43 | const arena = arena_instance.allocator();
44 |
45 | var opt: struct {
46 | // indicates user is running from 'zig build diff' which affects
47 | // the usage output.
48 | zigbuild: bool = false,
49 | fetch: bool = true,
50 | } = .{};
51 |
52 | const cmd_pos_args = blk: {
53 | const all_args = try std.process.argsAlloc(arena);
54 | // don't care about freeing args
55 | var pos_arg_count: usize = 0;
56 | var arg_index: usize = 1;
57 | while (arg_index < all_args.len) {
58 | const arg = all_args[arg_index];
59 | arg_index += 1;
60 | if (!std.mem.startsWith(u8, arg, "-")) {
61 | all_args[pos_arg_count] = arg;
62 | pos_arg_count += 1;
63 | } else if (std.mem.eql(u8, arg, "--zigbuild")) {
64 | opt.zigbuild = true;
65 | } else if (std.mem.eql(u8, arg, "-h") or std.mem.eql(u8, arg, "--help")) {
66 | try usage(opt.zigbuild);
67 | std.process.exit(1);
68 | } else if (std.mem.eql(u8, arg, "--nofetch") or std.mem.eql(u8, arg, "-n")) {
69 | opt.fetch = false;
70 | } else fatal("unknown cmline option '{s}'", .{arg});
71 | }
72 | break :blk all_args[0..pos_arg_count];
73 | };
74 | if (cmd_pos_args.len == 0) {
75 | try usage(opt.zigbuild);
76 | std.process.exit(1);
77 | }
78 | if (cmd_pos_args.len != 2) fatal("expected 2 positional cmdline arguments but got {}", .{cmd_pos_args.len});
79 | const diff_repo = cmd_pos_args[0];
80 | const generated_path = cmd_pos_args[1];
81 |
82 | try makeRepo(diff_repo);
83 |
84 | if (opt.fetch) {
85 | try run(arena, "git fetch", &.{ "git", "-C", diff_repo, "fetch", "origin", "main" });
86 | }
87 | try run(arena, "git clean", &.{ "git", "-C", diff_repo, "clean", "-xffd" });
88 | try run(arena, "git reset", &.{ "git", "-C", diff_repo, "reset", "--hard" });
89 | try run(arena, "git checkout", &.{ "git", "-C", diff_repo, "checkout", "origin/main" });
90 | try run(arena, "git clean", &.{ "git", "-C", diff_repo, "clean", "-xffd" });
91 |
92 | {
93 | var dir = try std.fs.cwd().openDir(diff_repo, .{ .iterate = true });
94 | defer dir.close();
95 | var it = dir.iterate();
96 | while (try it.next()) |entry| {
97 | if (std.mem.eql(u8, entry.name, ".git")) continue;
98 | std.log.info("rm -rf '{s}/{s}'", .{ diff_repo, entry.name });
99 | try dir.deleteTree(entry.name);
100 | }
101 | }
102 |
103 | std.log.info("copying generated files from '{s}'...", .{generated_path});
104 | try copyDir(
105 | std.fs.cwd(),
106 | generated_path,
107 | std.fs.cwd(),
108 | diff_repo,
109 | );
110 |
111 | try run(arena, "git status", &.{ "git", "-C", diff_repo, "status" });
112 | }
113 |
114 | pub fn makeRepo(path: []const u8) !void {
115 | std.fs.cwd().access(path, .{}) catch |err| switch (err) {
116 | error.FileNotFound => try gitInit(path),
117 | else => |e| return e,
118 | };
119 | }
120 | fn gitInit(repo: []const u8) !void {
121 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
122 | defer arena.deinit();
123 | const allocator = arena.allocator();
124 |
125 | const tmp_repo = try std.mem.concat(allocator, u8, &.{ repo, ".initializing" });
126 | defer allocator.free(tmp_repo);
127 | try std.fs.cwd().deleteTree(tmp_repo);
128 | try std.fs.cwd().makeDir(tmp_repo);
129 | try run(allocator, "git init", &.{
130 | "git",
131 | "-C",
132 | tmp_repo,
133 | "init",
134 | });
135 | const zigwin32_repo_url = "https://github.com/marlersoft/zigwin32";
136 | try run(allocator, "git init", &.{
137 | "git",
138 | "-C",
139 | tmp_repo,
140 | "remote",
141 | "add",
142 | "origin",
143 | zigwin32_repo_url,
144 | });
145 | try std.fs.cwd().rename(tmp_repo, repo);
146 | }
147 |
148 | const FormatArgv = struct {
149 | argv: []const []const u8,
150 | pub fn format(
151 | self: @This(),
152 | comptime fmt: []const u8,
153 | options: std.fmt.FormatOptions,
154 | writer: anytype,
155 | ) !void {
156 | _ = fmt;
157 | _ = options;
158 | var prefix: []const u8 = "";
159 | for (self.argv) |arg| {
160 | try writer.print("{s}{s}", .{ prefix, arg });
161 | prefix = " ";
162 | }
163 | }
164 | };
165 | pub fn fmtArgv(argv: []const []const u8) FormatArgv {
166 | return .{ .argv = argv };
167 | }
168 |
169 | pub fn childProcFailed(term: std.process.Child.Term) bool {
170 | return switch (term) {
171 | .Exited => |code| code != 0,
172 | .Signal => true,
173 | .Stopped => true,
174 | .Unknown => true,
175 | };
176 | }
177 | const FormatTerm = struct {
178 | term: std.process.Child.Term,
179 | pub fn format(
180 | self: @This(),
181 | comptime fmt: []const u8,
182 | options: std.fmt.FormatOptions,
183 | writer: anytype,
184 | ) !void {
185 | _ = fmt;
186 | _ = options;
187 | switch (self.term) {
188 | .Exited => |code| try writer.print("exited with code {}", .{code}),
189 | .Signal => |sig| try writer.print("exited with signal {}", .{sig}),
190 | .Stopped => |sig| try writer.print("stopped with signal {}", .{sig}),
191 | .Unknown => |sig| try writer.print("terminated abnormally with signal {}", .{sig}),
192 | }
193 | }
194 | };
195 | pub fn fmtTerm(term: std.process.Child.Term) FormatTerm {
196 | return .{ .term = term };
197 | }
198 |
199 | pub fn run(
200 | allocator: std.mem.Allocator,
201 | name: []const u8,
202 | argv: []const []const u8,
203 | ) !void {
204 | var child = std.process.Child.init(argv, allocator);
205 | std.log.info("{}", .{fmtArgv(child.argv)});
206 | try child.spawn();
207 | const term = try child.wait();
208 | if (childProcFailed(term)) {
209 | fatal("{s} {}", .{ name, fmtTerm(term) });
210 | }
211 | }
212 |
213 | fn copyDir(
214 | src_parent_dir: std.fs.Dir,
215 | src_path: []const u8,
216 | dst_parent_dir: std.fs.Dir,
217 | dst_path: []const u8,
218 | ) !void {
219 | var dst_dir = try dst_parent_dir.openDir(dst_path, .{});
220 | defer dst_dir.close();
221 | var src_dir = try src_parent_dir.openDir(src_path, .{ .iterate = true });
222 | defer src_dir.close();
223 | var it = src_dir.iterate();
224 | while (try it.next()) |entry| {
225 | switch (entry.kind) {
226 | .directory => {
227 | try dst_dir.makeDir(entry.name);
228 | try copyDir(src_dir, entry.name, dst_dir, entry.name);
229 | },
230 | .file => try src_dir.copyFile(
231 | entry.name,
232 | dst_dir,
233 | entry.name,
234 | .{},
235 | ),
236 | else => |kind| fatal("unsupported file kind '{s}'", .{@tagName(kind)}),
237 | }
238 | }
239 | }
240 |
--------------------------------------------------------------------------------
/src/extra.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | const StringPool = @import("StringPool.zig");
4 |
5 | pub const Root = StringPool.HashMapUnmanaged(Api);
6 |
7 | pub const Functions = StringPool.HashMapUnmanaged(Function);
8 | pub const Constants = StringPool.HashMapUnmanaged(TypeModifier);
9 | pub const Api = struct {
10 | functions: Functions = .{},
11 | constants: Constants = .{},
12 | };
13 | pub const NullModifier = u3;
14 | pub const TypeModifier = struct {
15 | union_pointer: bool = false,
16 | null_modifier: NullModifier = 0,
17 | };
18 | const Function = struct {
19 | ret: ?TypeModifier = null,
20 | params: StringPool.HashMapUnmanaged(TypeModifier) = .{},
21 | };
22 |
23 | pub fn oom(e: error{OutOfMemory}) noreturn {
24 | @panic(@errorName(e));
25 | }
26 | fn parseError(filename: []const u8, lineno: u32, comptime fmt: []const u8, args: anytype) noreturn {
27 | std.io.getStdErr().writer().print("{s}:{}: parse error: ", .{ filename, lineno }) catch |e| std.debug.panic("write to stderr failed with {s}", .{@errorName(e)});
28 | std.io.getStdErr().writer().print(fmt, args) catch |e| std.debug.panic("write to stderr failed with {s}", .{@errorName(e)});
29 | std.process.exit(0xff);
30 | }
31 |
32 | pub fn read(
33 | api_name_set: StringPool.HashMapUnmanaged(void),
34 | string_pool: *StringPool,
35 | allocator: std.mem.Allocator,
36 | filename: []const u8,
37 | content: []const u8,
38 | ) Root {
39 | var root: Root = .{};
40 |
41 | const return_id = string_pool.add("return") catch |e| oom(e);
42 |
43 | var line_it = std.mem.splitAny(u8, content, "\r\n");
44 | var lineno: u32 = 0;
45 | while (line_it.next()) |line| {
46 | lineno += 1;
47 |
48 | var field_it = std.mem.tokenizeScalar(u8, line, ' ');
49 | const first_field = field_it.next() orelse continue;
50 | if (first_field.len == 0 or first_field[0] == '#') continue;
51 | const api_name = string_pool.add(first_field) catch |e| oom(e);
52 | if (api_name_set.get(api_name)) |_| {} else parseError(filename, lineno, "unknown api '{}'", .{api_name});
53 |
54 | const api = blk: {
55 | const entry = root.getOrPut(allocator, api_name) catch |e| oom(e);
56 | if (!entry.found_existing) {
57 | entry.value_ptr.* = .{};
58 | }
59 | break :blk entry.value_ptr;
60 | };
61 |
62 | const kind = field_it.next() orelse parseError(filename, lineno, "missing kind specifier", .{});
63 |
64 | if (std.mem.eql(u8, kind, "Function")) {
65 | const func_name = string_pool.add(
66 | field_it.next() orelse parseError(filename, lineno, "missing function name", .{}),
67 | ) catch |e| oom(e);
68 |
69 | const func = blk: {
70 | const entry = api.functions.getOrPut(allocator, func_name) catch |e| oom(e);
71 | if (entry.found_existing) parseError(filename, lineno, "duplicate function '{s} {s}'", .{ api_name, func_name });
72 | entry.value_ptr.* = .{};
73 | break :blk entry.value_ptr;
74 | };
75 |
76 | var next_index = field_it.index;
77 | var mod_count: u32 = 0;
78 | while (parseNamedModifier(filename, lineno, line, next_index)) |named_mod| {
79 | mod_count += 1;
80 | const name = string_pool.add(named_mod.name) catch |e| oom(e);
81 | if (name.eql(return_id)) {
82 | if (func.ret) |_| parseError(filename, lineno, "duplicate return specifier", .{});
83 | func.ret = named_mod.modifier;
84 | } else {
85 | const entry = func.params.getOrPut(allocator, name) catch |e| oom(e);
86 | if (entry.found_existing) parseError(filename, lineno, "duplicate parameter '{s}'", .{name});
87 | entry.value_ptr.* = named_mod.modifier;
88 | }
89 | next_index = named_mod.end;
90 | }
91 | if (mod_count == 0) parseError(filename, lineno, "missing return/parameter specifiers", .{});
92 | } else if (std.mem.eql(u8, kind, "Constant")) {
93 | const named_mod = parseNamedModifier(filename, lineno, line, field_it.index) orelse parseError(
94 | filename,
95 | lineno,
96 | "missing name/modifiers",
97 | .{},
98 | );
99 | if (skipWhitespace(line, named_mod.end) != line.len) parseError(filename, lineno, "unexpected data: '{s}'", .{line[named_mod.end..]});
100 | const name = string_pool.add(named_mod.name) catch |e| oom(e);
101 | const entry = api.constants.getOrPut(allocator, name) catch |e| oom(e);
102 | if (entry.found_existing) parseError(filename, lineno, "duplicate constant '{s}'", .{name});
103 | entry.value_ptr.* = named_mod.modifier;
104 | } else parseError(filename, lineno, "unknown kind '{s}'", .{kind});
105 | }
106 | return root;
107 | }
108 |
109 | fn parseNamedModifier(
110 | filename: []const u8,
111 | lineno: u32,
112 | line: []const u8,
113 | start: usize,
114 | ) ?struct {
115 | end: usize,
116 | name: []const u8,
117 | modifier: TypeModifier,
118 | } {
119 | const name_start = skipWhitespace(line, start);
120 | if (name_start == line.len) return null;
121 | const name_end = scanId(line, name_start);
122 | const name = line[name_start..name_end];
123 | if (name.len == 0) parseError(filename, lineno, "expected id [a-zA-Z0-9_] but got '{s}'", .{line[name_start..]});
124 | if (!matches(line, name_end, '(')) parseError(filename, lineno, "expected '(' but got '{s}'", .{line[name_end..]});
125 | const result = parseModifier(filename, lineno, line, name_end + 1);
126 | return .{
127 | .end = result.end,
128 | .name = name,
129 | .modifier = result.modifier,
130 | };
131 | }
132 |
133 | fn parseModifier(filename: []const u8, lineno: u32, line: []const u8, start: usize) struct {
134 | end: usize,
135 | modifier: TypeModifier,
136 | } {
137 | var modifier: TypeModifier = .{};
138 | var next_index = start;
139 | while (true) {
140 | const id_start = skipWhitespace(line, next_index);
141 | if (id_start == line.len) parseError(filename, lineno, "missing ')'", .{});
142 | if (matches(line, id_start, ')'))
143 | return .{ .end = next_index + 1, .modifier = modifier };
144 |
145 | const id_end = scanId(line, id_start);
146 | const id = line[id_start..id_end];
147 | if (id.len == 0) parseError(filename, lineno, "expected id [a-zA-Z0-9_] but got '{s}'", .{line[id_start..]});
148 |
149 | if (std.mem.eql(u8, id, "NotNull")) {
150 | if (!matches(line, id_end, '=')) parseError(filename, lineno, "expected '=' after NotNull but got '{s}'", .{line[id_start..]});
151 |
152 | next_index = id_end + 1;
153 | var flags: NullModifier = 0;
154 | var flag_count: u32 = 0;
155 | while (true) {
156 | const on = if (matches(line, next_index, '0'))
157 | false
158 | else if (matches(line, next_index, '1'))
159 | true
160 | else
161 | break;
162 | next_index += 1;
163 | flag_count += 1;
164 | if (flag_count > @typeInfo(NullModifier).int.bits) parseError(filename, lineno, "NullModifier type doesn't have enough bits", .{});
165 | flags = flags << 1;
166 | if (on) flags |= 1;
167 | }
168 | if (flag_count == 0) parseError(filename, lineno, "expected 1's and 0's after 'NotNull=' but got '{s}'", .{line[id_start..]});
169 | modifier.null_modifier = flags;
170 | } else if (std.mem.eql(u8, id, "UnionPointer")) {
171 | modifier.union_pointer = true;
172 | next_index = id_end;
173 | } else parseError(filename, lineno, "unknown type modifier '{s}'", .{id});
174 | }
175 | }
176 |
177 | fn skipWhitespace(str: []const u8, start: usize) usize {
178 | var i = start;
179 | while (i < str.len) : (i += 1) {
180 | if (str[i] != ' ') break;
181 | }
182 | return i;
183 | }
184 |
185 | fn isIdChar(c: u8) bool {
186 | return switch (c) {
187 | 'a'...'z', 'A'...'Z', '0'...'9', '_' => true,
188 | else => false,
189 | };
190 | }
191 | fn scanId(str: []const u8, start: usize) usize {
192 | var i = start;
193 | while (i < str.len) : (i += 1) {
194 | if (!isIdChar(str[i])) break;
195 | }
196 | return i;
197 | }
198 |
199 | fn matches(str: []const u8, index: usize, c: u8) bool {
200 | return index < str.len and str[index] == c;
201 | }
202 |
--------------------------------------------------------------------------------
/src/genzigexports.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const win32_stub = @import("win32_stub");
3 | const zig = win32_stub.zig;
4 |
5 | // make win32/zig.zig happy
6 | pub const UNICODE = true;
7 |
8 | pub fn main() !void {
9 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
10 | const arena = arena_instance.allocator();
11 | const all_args = try std.process.argsAlloc(arena);
12 | // don't care about freeing args
13 |
14 | const cmd_args = all_args[1..];
15 | if (cmd_args.len != 1) {
16 | std.log.err("expected 1 cmdline argument but got {}", .{cmd_args.len});
17 | std.process.exit(0xff);
18 | }
19 | const out_file_path = cmd_args[0];
20 |
21 | const out_file = try std.fs.cwd().createFile(out_file_path, .{});
22 | defer out_file.close();
23 |
24 | var bw = std.io.bufferedWriter(out_file.writer());
25 | try generate(bw.writer().any());
26 | try bw.flush();
27 | }
28 | fn generate(writer: std.io.AnyWriter) !void {
29 | try writer.writeAll("pub const Kind = enum { constant, type, function };\n");
30 | try writer.writeAll("pub const Decl = struct { kind: Kind, name: []const u8};\n");
31 | try writer.writeAll("pub const declarations = [_]Decl{\n");
32 | inline for (comptime std.meta.declarations(zig)) |decl| {
33 | const field_type_info = @typeInfo(@TypeOf(@field(zig, decl.name)));
34 | const kind = switch (comptime field_type_info) {
35 | .bool, .int, .@"enum" => "constant",
36 | .@"fn" => "function",
37 | .type => "type",
38 | else => @compileError(
39 | "zig.zig decl '" ++ decl.name ++ "' has unsupported type info: " ++ @tagName(field_type_info),
40 | ),
41 | };
42 | try writer.print(" .{{ .kind = .{s}, .name = \"{s}\" }},\n", .{ kind, decl.name });
43 | }
44 | try writer.writeAll("};\n");
45 | }
46 |
--------------------------------------------------------------------------------
/src/handletypes.zig:
--------------------------------------------------------------------------------
1 | //! workaround github.com/microsoft/win32metadata/issues/395
2 | const std = @import("std");
3 | pub const std_handle_types = std.StaticStringMap([]const u8).initComptime(.{
4 | .{ "HANDLE", "os.windows.HANDLE" },
5 | .{ "HINSTANCE", "os.windows.HINSTANCE" },
6 | .{ "SOCKET", "os.windows.ws2_32.SOCKET" },
7 | });
8 | pub const handle_types = list: {
9 | @setEvalBranchQuota(3000);
10 | break :list std.StaticStringMap(void).initComptime(.{
11 | .{"HICON"},
12 | .{"HCURSOR"},
13 | .{"HBRUSH"},
14 | .{"HSYNTHETICPOINTERDEVICE"},
15 | .{"HPROPSHEETPAGE"},
16 | .{"HIMAGELIST"},
17 | .{"HTREEITEM"},
18 | .{"HCOLORSPACE"},
19 | .{"HCRYPTASYNC"},
20 | .{"HCERTCHAINENGINE"},
21 | .{"HCOMDB"},
22 | .{"HKEY"},
23 | .{"HWINWATCH"},
24 | .{"HWINSTA"},
25 | .{"HDESK"},
26 | .{"HSTRING"},
27 | .{"HSZ"},
28 | .{"HCONV"},
29 | .{"HCONVLIST"},
30 | .{"HDDEDATA"},
31 | .{"HRASCONN"},
32 | .{"HAMSICONTEXT"},
33 | .{"HAMSISESSION"},
34 | .{"HCMNOTIFICATION"},
35 | .{"HKL"},
36 | .{"HIFTIMESTAMPCHANGE"},
37 | .{"HWSAEVENT"},
38 | .{"HTASK"},
39 | .{"HWINEVENTHOOK"},
40 | .{"HUIANODE"},
41 | .{"HUIAPATTERNOBJECT"},
42 | .{"HUIATEXTRANGE"},
43 | .{"HUIAEVENT"},
44 | .{"HMIDI"},
45 | .{"HMIDIIN"},
46 | .{"HMIDIOUT"},
47 | .{"HMIDISTRM"},
48 | .{"HMIXER"},
49 | .{"HMIXEROBJ"},
50 | .{"HWAVE"},
51 | .{"HWAVEOUT"},
52 | .{"HWAVEIN"},
53 | .{"HMMIO"},
54 | .{"HDRVR"},
55 | .{"HACMDRIVERID"},
56 | .{"HACMDRIVER"},
57 | .{"HACMSTREAM"},
58 | .{"HACMOBJ"},
59 | .{"HIC"},
60 | .{"HVIDEO"},
61 | .{"HSWDEVICE"},
62 | .{"HINTERACTIONCONTEXT"},
63 | .{"HRAWINPUT"},
64 | .{"HRECOALT"},
65 | .{"HRECOCONTEXT"},
66 | .{"HRECOGNIZER"},
67 | .{"HRECOLATTICE"},
68 | .{"HRECOWORDLIST"},
69 | .{"HIMC"},
70 | .{"HIMCC"},
71 | .{"HSAVEDUILANGUAGES"},
72 | .{"HRSRC"},
73 | .{"HSURF"},
74 | .{"HPOWERNOTIFY"},
75 | .{"HUMPD"},
76 | .{"HSTR"},
77 | .{"HSPRITE"},
78 | .{"HSEMAPHORE"},
79 | .{"HLSURF"},
80 | .{"HFASTMUTEX"},
81 | .{"HDRVOBJ"},
82 | .{"HDEV"},
83 | .{"HBM"},
84 | .{"HPCON"},
85 | .{"HMENU"},
86 | .{"HACCEL"},
87 | .{"HDROP"},
88 | .{"HPSXA"},
89 | .{"HDC"},
90 | .{"HGDIOBJ"},
91 | .{"HBITMAP"},
92 | .{"HRGN"},
93 | .{"HPEN"},
94 | .{"HBRUSH"},
95 | .{"HFONT"},
96 | .{"HMETAFILE"},
97 | .{"HENHMETAFILE"},
98 | .{"HMONITOR"},
99 | .{"HPALETTE"},
100 | .{"HWND"},
101 | .{"HHOOK"},
102 | .{"HGESTUREINFO"},
103 | .{"HTOUCHINPUT"},
104 | .{"HGLRC"},
105 | .{"HFILTER"},
106 | .{"HPTPROVIDER"},
107 | .{"HPSS"},
108 | .{"HPSSWALK"},
109 | .{"HSTRING_BUFFER"},
110 | .{"JET_SESID"},
111 | .{"PSID"},
112 | .{"AUTHZ_AUDIT_EVENT_HANDLE"},
113 | .{"HeapHandle"},
114 | .{"HDPA"},
115 | .{"HDSA"},
116 | });
117 | };
118 |
--------------------------------------------------------------------------------
/src/jsonextra.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 |
3 | pub fn ArrayHashMap(comptime T: type) type {
4 | return struct {
5 | map: std.StringArrayHashMap(T),
6 |
7 | const Self = @This();
8 | pub fn get(self: Self, name: []const u8) ?T {
9 | return self.map.get(name);
10 | }
11 |
12 | pub fn jsonParse(
13 | allocator: std.mem.Allocator,
14 | source: anytype,
15 | options: std.json.ParseOptions,
16 | ) std.json.ParseError(@TypeOf(source.*))!Self {
17 | var map = std.StringArrayHashMap(T).init(allocator);
18 | errdefer map.deinit();
19 |
20 | if (.object_begin != try source.next()) return error.UnexpectedToken;
21 | while (true) {
22 | const api_name = switch (try source.next()) {
23 | .string => |s| s,
24 | .object_end => return .{ .map = map },
25 | else => return error.UnexpectedToken,
26 | };
27 | const value = try std.json.innerParse(T, allocator, source, options);
28 | try map.put(api_name, value);
29 | }
30 | }
31 | };
32 | }
33 |
--------------------------------------------------------------------------------
/src/metadata.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const metadata = @This();
3 |
4 | pub const Api = struct {
5 | Constants: []const Constant,
6 | Types: []const Type,
7 | Functions: []const Function,
8 | UnicodeAliases: []const []const u8,
9 |
10 | pub fn parse(
11 | allocator: std.mem.Allocator,
12 | api_path: []const u8,
13 | filename: []const u8,
14 | content: []const u8,
15 | ) Api {
16 | var diagnostics = std.json.Diagnostics{};
17 | var scanner = std.json.Scanner.initCompleteInput(allocator, content);
18 | defer scanner.deinit();
19 | scanner.enableDiagnostics(&diagnostics);
20 | return std.json.parseFromTokenSourceLeaky(
21 | Api,
22 | allocator,
23 | &scanner,
24 | .{},
25 | ) catch |err| {
26 | std.log.err(
27 | "{s}{c}{s}:{}:{}: {s}",
28 | .{
29 | api_path, std.fs.path.sep, filename,
30 | diagnostics.getLine(), diagnostics.getColumn(), @errorName(err),
31 | },
32 | );
33 | @panic("json error");
34 | };
35 | }
36 | };
37 |
38 | pub const ValueType = enum {
39 | Byte,
40 | UInt16,
41 | Int32,
42 | UInt32,
43 | Int64,
44 | UInt64,
45 | Single,
46 | Double,
47 | String,
48 | PropertyKey,
49 | };
50 |
51 | pub const TypeRefNative = enum {
52 | Void,
53 | Boolean,
54 | SByte,
55 | Byte,
56 | Int16,
57 | UInt16,
58 | Int32,
59 | UInt32,
60 | Int64,
61 | UInt64,
62 | Char,
63 | Single,
64 | Double,
65 | String,
66 | IntPtr,
67 | UIntPtr,
68 | Guid,
69 | };
70 |
71 | pub const Constant = struct {
72 | Name: []const u8,
73 | Type: TypeRef,
74 | ValueType: ValueType,
75 | Value: std.json.Value,
76 | Attrs: ConstantAttrs,
77 | };
78 | pub const ConstantAttrs = struct {
79 | pub fn jsonParse(
80 | allocator: std.mem.Allocator,
81 | source: anytype,
82 | options: std.json.ParseOptions,
83 | ) std.json.ParseError(@TypeOf(source.*))!ConstantAttrs {
84 | return try parseAttrsArray(ConstantAttrs, allocator, source, options);
85 | }
86 | };
87 |
88 | pub const EnumIntegerBase = enum { Byte, SByte, UInt16, UInt32, Int32, UInt64 };
89 |
90 | const TypeKind = enum {
91 | NativeTypedef,
92 | Enum,
93 | Struct,
94 | Union,
95 | ComClassID,
96 | Com,
97 | FunctionPointer,
98 | };
99 | const type_kinds = std.StaticStringMap(TypeKind).initComptime(.{
100 | .{ "NativeTypedef", .NativeTypedef },
101 | .{ "Enum", .Enum },
102 | .{ "Struct", .Struct },
103 | .{ "Union", .Union },
104 | .{ "ComClassID", .ComClassID },
105 | .{ "Com", .Com },
106 | .{ "FunctionPointer", .FunctionPointer },
107 | });
108 | pub const Type = struct {
109 | Name: []const u8,
110 | Architectures: Architectures,
111 | Platform: ?Platform,
112 | Kind: union(enum) {
113 | NativeTypedef: NativeTypedef,
114 | Enum: Enum,
115 | Struct: StructOrUnion,
116 | Union: StructOrUnion,
117 | ComClassID: ComClassID,
118 | Com: Com,
119 | FunctionPointer: FunctionPointer,
120 | },
121 |
122 | pub const Enum = struct {
123 | Flags: bool,
124 | Scoped: bool,
125 | Values: []EnumField,
126 | IntegerBase: ?EnumIntegerBase,
127 | };
128 | pub const EnumField = struct {
129 | Name: []const u8,
130 | Value: std.json.Value,
131 | };
132 |
133 | pub const ComClassID = struct {
134 | Guid: []const u8,
135 | };
136 |
137 | pub fn jsonParse(
138 | allocator: std.mem.Allocator,
139 | source: anytype,
140 | options: std.json.ParseOptions,
141 | ) std.json.ParseError(@TypeOf(source.*))!Type {
142 | if (.object_begin != try source.next()) return error.UnexpectedToken;
143 | try expectFieldName(source, "Name");
144 | const name = switch (try source.next()) {
145 | .string => |s| s,
146 | else => return error.UnexpectedToken,
147 | };
148 | try expectFieldName(source, "Architectures");
149 | const arches = try Architectures.jsonParse(allocator, source, options);
150 | try expectFieldName(source, "Platform");
151 | const platform = try std.json.innerParse(?Platform, allocator, source, options);
152 | switch (try jsonParseUnionKind(TypeKind, "Type", source, type_kinds)) {
153 | .NativeTypedef => return .{ .Name = name, .Architectures = arches, .Platform = platform, .Kind = .{
154 | .NativeTypedef = try parseUnionObject(NativeTypedef, allocator, source, options),
155 | } },
156 | .Enum => return .{ .Name = name, .Architectures = arches, .Platform = platform, .Kind = .{
157 | .Enum = try parseUnionObject(Enum, allocator, source, options),
158 | } },
159 | .Struct => return .{ .Name = name, .Architectures = arches, .Platform = platform, .Kind = .{
160 | .Struct = try parseUnionObject(StructOrUnion, allocator, source, options),
161 | } },
162 | .Union => return .{ .Name = name, .Architectures = arches, .Platform = platform, .Kind = .{
163 | .Union = try parseUnionObject(StructOrUnion, allocator, source, options),
164 | } },
165 | .ComClassID => return .{ .Name = name, .Architectures = arches, .Platform = platform, .Kind = .{
166 | .ComClassID = try parseUnionObject(ComClassID, allocator, source, options),
167 | } },
168 | .Com => return .{ .Name = name, .Architectures = arches, .Platform = platform, .Kind = .{
169 | .Com = try parseUnionObject(Com, allocator, source, options),
170 | } },
171 | .FunctionPointer => return .{ .Name = name, .Architectures = arches, .Platform = platform, .Kind = .{
172 | .FunctionPointer = try parseUnionObject(FunctionPointer, allocator, source, options),
173 | } },
174 | }
175 | }
176 | };
177 |
178 | pub const FunctionPointer = struct {
179 | SetLastError: bool,
180 | ReturnType: TypeRef,
181 | //ReturnAttrs: ReturnAttrs,
182 | ReturnAttrs: ParamAttrs,
183 | Attrs: FunctionAttrs,
184 | Params: []const Param,
185 | };
186 |
187 | pub const StructOrUnion = struct {
188 | Size: u32,
189 | PackingSize: u32,
190 | Fields: []const StructOrUnionField,
191 | NestedTypes: []const Type,
192 | Comment: ?[]const u8 = null,
193 | };
194 | pub const StructOrUnionField = struct {
195 | Name: []const u8,
196 | Type: TypeRef,
197 | Attrs: FieldAttrs,
198 | };
199 |
200 | pub const FieldAttrs = struct {
201 | Const: bool = false,
202 | Obselete: bool = false,
203 | Optional: bool = false,
204 | NotNullTerminated: bool = false,
205 | NullNullTerminated: bool = false,
206 | pub fn jsonParse(
207 | allocator: std.mem.Allocator,
208 | source: anytype,
209 | options: std.json.ParseOptions,
210 | ) std.json.ParseError(@TypeOf(source.*))!FieldAttrs {
211 | return parseAttrsArray(FieldAttrs, allocator, source, options);
212 | }
213 | };
214 |
215 | pub const NativeTypedef = struct {
216 | AlsoUsableFor: ?[]const u8,
217 | Def: TypeRef,
218 | FreeFunc: ?[]const u8,
219 | InvalidHandleValue: ?i64,
220 | };
221 |
222 | pub const Com = struct {
223 | Guid: ?[]const u8,
224 | Attrs: ComAttrs,
225 | Interface: ?TypeRef,
226 | Methods: []const ComMethod,
227 | };
228 | pub const ComAttrs = struct {
229 | Agile: bool = false,
230 | pub fn jsonParse(
231 | allocator: std.mem.Allocator,
232 | source: anytype,
233 | options: std.json.ParseOptions,
234 | ) std.json.ParseError(@TypeOf(source.*))!ComAttrs {
235 | return parseAttrsArray(ComAttrs, allocator, source, options);
236 | }
237 | };
238 |
239 | pub const ComMethod = struct {
240 | Name: []const u8,
241 | SetLastError: bool,
242 | ReturnType: TypeRef,
243 | //ReturnAttrs: ReturnAttrs,
244 | ReturnAttrs: ParamAttrs,
245 | Architectures: Architectures,
246 | Platform: ?Platform,
247 | Attrs: FunctionAttrs,
248 | Params: []const Param,
249 | };
250 | //pub const ComMethodAttrs = struct {
251 | // SpecialName: bool = false,
252 | // PreserveSig: bool = false,
253 | // pub fn jsonParse(
254 | // allocator: std.mem.Allocator,
255 | // source: anytype,
256 | // options: std.json.ParseOptions,
257 | // ) std.json.ParseError(@TypeOf(source.*))!ComMethodAttrs {
258 | // return parseAttrsArray(ComMethodAttrs, allocator, source, options);
259 | // }
260 | //};
261 |
262 | //pub const ComMethodParam = struct {
263 | // Name: []const u8,
264 | // Type: TypeRef,
265 | // Attrs: ComMethodParamAttrs,
266 | //};
267 | //pub const ComMethodParamAttrs = struct {
268 | // In: bool = false,
269 | // Out: bool = false,
270 | // Const: bool = false,
271 | // Optional: bool = false,
272 | // ComOutPtr: bool = false,
273 | // RetVal: bool = false,
274 | // Reserved: bool = false,
275 | // NotNullTerminated: bool = false,
276 | // NullNullTerminated: bool = false,
277 | // MemorySize: ?MemorySize = null,
278 | // FreeWith: ?FreeWith = null,
279 | //
280 | // pub fn jsonParse(
281 | // allocator: std.mem.Allocator,
282 | // source: anytype,
283 | // options: std.json.ParseOptions,
284 | // ) std.json.ParseError(@TypeOf(source.*))!ComMethodParamAttrs {
285 | // return parseAttrsArray(ComMethodParamAttrs, allocator, source, options);
286 | // }
287 | //};
288 |
289 | pub const Function = struct {
290 | Name: []const u8,
291 | SetLastError: bool,
292 | DllImport: []const u8,
293 | ReturnType: TypeRef,
294 | //ReturnAttrs: ReturnAttrs,
295 | ReturnAttrs: ParamAttrs,
296 | Architectures: Architectures,
297 | Platform: ?Platform,
298 | Attrs: FunctionAttrs,
299 | Params: []const Param,
300 | };
301 | pub const Platform = enum {
302 | windowsServer2000,
303 | windowsServer2003,
304 | windowsServer2008,
305 | windowsServer2012,
306 | windowsServer2016,
307 | windowsServer2020,
308 | @"windows5.0",
309 | @"windows5.1.2600",
310 | @"windows6.0.6000",
311 | @"windows6.1",
312 | @"windows8.0",
313 | @"windows8.1",
314 | @"windows10.0.10240",
315 | @"windows10.0.10586",
316 | @"windows10.0.14393",
317 | @"windows10.0.15063",
318 | @"windows10.0.16299",
319 | @"windows10.0.17134",
320 | @"windows10.0.17763",
321 | @"windows10.0.18362",
322 | @"windows10.0.19041",
323 | };
324 |
325 | pub const Architectures = struct {
326 | filter: ?Filter = null,
327 |
328 | pub const Filter = struct {
329 | X86: bool = false,
330 | X64: bool = false,
331 | Arm64: bool = false,
332 | //pub fn allAreSet(self: Filter) bool {
333 | //return self.X86 and self.X64 and self.Arm64;
334 | //}
335 | pub fn eql(self: Filter, other: Filter) bool {
336 | return self.X86 == other.X86 and
337 | self.X64 == other.X64 and
338 | self.Arm64 == other.Arm64;
339 | }
340 | pub fn unionWith(self: Filter, other: Filter) ?Filter {
341 | const new_filter: Filter = .{
342 | .X86 = self.X86 or other.X86,
343 | .X64 = self.X64 or other.X64,
344 | .Arm64 = self.Arm64 or other.Arm64,
345 | };
346 | if (new_filter.X86 and new_filter.X64 and new_filter.Arm64)
347 | return null;
348 | return new_filter;
349 | }
350 | };
351 |
352 | pub fn eql(self: Architectures, other: Architectures) bool {
353 | const self_filter = self.filter orelse return other.filter == null;
354 | const other_filter = other.filter orelse return false;
355 | return self_filter.eql(other_filter);
356 | }
357 |
358 | pub fn unionWith(self: Architectures, other: Architectures) Architectures {
359 | const self_filter = self.filter orelse return .{};
360 | const other_filter = other.filter orelse return .{};
361 | return .{ .filter = self_filter.unionWith(other_filter) };
362 | }
363 |
364 | pub fn jsonParse(
365 | allocator: std.mem.Allocator,
366 | source: anytype,
367 | options: std.json.ParseOptions,
368 | ) std.json.ParseError(@TypeOf(source.*))!Architectures {
369 | _ = allocator;
370 | _ = options;
371 | if (.array_begin != try source.next()) return error.UnexpectedToken;
372 | const filter_struct_info = @typeInfo(Filter).@"struct";
373 | var result: Architectures = .{};
374 | while (true) {
375 | switch (try source.next()) {
376 | .array_end => return result,
377 | .string => |s| {
378 | if (result.filter == null) {
379 | result.filter = .{};
380 | }
381 |
382 | inline for (filter_struct_info.fields) |field| {
383 | if (field.type == bool and std.mem.eql(u8, s, field.name)) {
384 | @field(result.filter.?, field.name) = true;
385 | break;
386 | }
387 | } else {
388 | std.log.err("unknown Architecture attribute '{s}'", .{s});
389 | return error.UnexpectedToken;
390 | }
391 | },
392 | else => |token| {
393 | std.log.err(
394 | "expected string or array_close but got {s}",
395 | .{@tagName(token)},
396 | );
397 | return error.UnexpectedToken;
398 | },
399 | }
400 | }
401 | }
402 | };
403 |
404 | const MemorySize = struct {
405 | BytesParamIndex: u16,
406 | };
407 | const FreeWith = struct {
408 | Func: []const u8,
409 | };
410 |
411 | pub const FunctionAttrs = struct {
412 | SpecialName: bool = false,
413 | PreserveSig: bool = false,
414 | DoesNotReturn: bool = false,
415 | pub fn jsonParse(
416 | allocator: std.mem.Allocator,
417 | source: anytype,
418 | options: std.json.ParseOptions,
419 | ) std.json.ParseError(@TypeOf(source.*))!FunctionAttrs {
420 | return parseAttrsArray(FunctionAttrs, allocator, source, options);
421 | }
422 | };
423 |
424 | //pub const ReturnAttrs = struct {
425 | // Optional: bool = false,
426 | // pub fn jsonParse(
427 | // allocator: std.mem.Allocator,
428 | // source: anytype,
429 | // options: std.json.ParseOptions,
430 | // ) std.json.ParseError(@TypeOf(source.*))!ReturnAttrs {
431 | // return parseAttrsArray(ReturnAttrs, allocator, source, options);
432 | // }
433 | //};
434 | pub const ParamAttrs = struct {
435 | Const: bool = false,
436 | In: bool = false,
437 | Out: bool = false,
438 | Optional: bool = false,
439 | NotNullTerminated: bool = false,
440 | NullNullTerminated: bool = false,
441 | RetVal: bool = false,
442 | ComOutPtr: bool = false,
443 | DoNotRelease: bool = false,
444 | Reserved: bool = false,
445 | MemorySize: ?MemorySize = null,
446 | FreeWith: ?FreeWith = null,
447 |
448 | pub fn jsonParse(
449 | allocator: std.mem.Allocator,
450 | source: anytype,
451 | options: std.json.ParseOptions,
452 | ) std.json.ParseError(@TypeOf(source.*))!ParamAttrs {
453 | return parseAttrsArray(ParamAttrs, allocator, source, options);
454 | }
455 | };
456 |
457 | pub const Param = struct {
458 | Name: []const u8,
459 | Type: TypeRef,
460 | Attrs: ParamAttrs,
461 | };
462 |
463 | const TargetKind = enum {
464 | Default,
465 | Com,
466 | FunctionPointer,
467 | };
468 |
469 | const TypeRefKind = enum {
470 | Native,
471 | ApiRef,
472 | PointerTo,
473 | Array,
474 | LPArray,
475 | MissingClrType,
476 | };
477 | const type_ref_kinds = std.StaticStringMap(TypeRefKind).initComptime(.{
478 | .{ "Native", .Native },
479 | .{ "ApiRef", .ApiRef },
480 | .{ "PointerTo", .PointerTo },
481 | .{ "Array", .Array },
482 | .{ "LPArray", .LPArray },
483 | .{ "MissingClrType", .MissingClrType },
484 | });
485 |
486 | const Native = struct {
487 | Name: TypeRefNative,
488 | };
489 | const ApiRef = struct {
490 | Name: []const u8,
491 | TargetKind: TargetKind,
492 | Api: []const u8,
493 | Parents: []const []const u8,
494 | };
495 | const PointerTo = struct {
496 | Child: *const TypeRef,
497 | };
498 | const PointerToAttrs = struct {
499 | pub fn jsonParse(
500 | allocator: std.mem.Allocator,
501 | source: anytype,
502 | options: std.json.ParseOptions,
503 | ) std.json.ParseError(@TypeOf(source.*))!PointerToAttrs {
504 | return parseAttrsArray(PointerToAttrs, allocator, source, options);
505 | }
506 | };
507 | const Array = struct {
508 | Shape: ?ArrayShape,
509 | Child: *const TypeRef,
510 | };
511 | const ArrayShape = struct {
512 | Size: u32,
513 | };
514 | const LPArray = struct {
515 | NullNullTerm: bool,
516 | CountConst: i32,
517 | CountParamIndex: i32,
518 | Child: *const TypeRef,
519 | };
520 | const MissingClrType = struct {
521 | Name: []const u8,
522 | Namespace: []const u8,
523 | };
524 |
525 | pub const TypeRef = union(TypeRefKind) {
526 | Native: Native,
527 | ApiRef: ApiRef,
528 | PointerTo: PointerTo,
529 | Array: Array,
530 | LPArray: LPArray,
531 | MissingClrType: MissingClrType,
532 |
533 | pub fn jsonParse(
534 | allocator: std.mem.Allocator,
535 | source: anytype,
536 | options: std.json.ParseOptions,
537 | ) std.json.ParseError(@TypeOf(source.*))!TypeRef {
538 | if (.object_begin != try source.next()) return error.UnexpectedToken;
539 | switch (try jsonParseUnionKind(TypeRefKind, "TypeRef", source, type_ref_kinds)) {
540 | .Native => return .{
541 | .Native = try parseUnionObject(Native, allocator, source, options),
542 | },
543 | .ApiRef => return .{
544 | .ApiRef = try parseUnionObject(ApiRef, allocator, source, options),
545 | },
546 | .PointerTo => return .{
547 | .PointerTo = try parseUnionObject(PointerTo, allocator, source, options),
548 | },
549 | .Array => return .{
550 | .Array = try parseUnionObject(Array, allocator, source, options),
551 | },
552 | .LPArray => return .{
553 | .LPArray = try parseUnionObject(LPArray, allocator, source, options),
554 | },
555 | .MissingClrType => return .{
556 | .MissingClrType = try parseUnionObject(MissingClrType, allocator, source, options),
557 | },
558 | }
559 | }
560 | };
561 |
562 | fn parseAttrsArray(
563 | comptime Attrs: type,
564 | allocator: std.mem.Allocator,
565 | source: anytype,
566 | options: std.json.ParseOptions,
567 | ) std.json.ParseError(@TypeOf(source.*))!Attrs {
568 | const structInfo = switch (@typeInfo(Attrs)) {
569 | .@"struct" => |i| i,
570 | else => @compileError("Unable to parse attribute array into non-struct type '" ++ @typeName(Attrs) ++ "'"),
571 | };
572 |
573 | if (.array_begin != try source.next()) return error.UnexpectedToken;
574 | var result: Attrs = .{};
575 |
576 | while (true) {
577 | switch (try source.next()) {
578 | .array_end => return result,
579 | .string => |s| {
580 | inline for (structInfo.fields) |field| {
581 | if (field.type == bool and std.mem.eql(u8, s, field.name)) {
582 | @field(result, field.name) = true;
583 | break;
584 | }
585 | } else {
586 | std.log.err(
587 | "unknown attribute '{s}' for type {s}",
588 | .{ s, @typeName(Attrs) },
589 | );
590 | return error.UnexpectedToken;
591 | }
592 | },
593 | .object_begin => {
594 | const kind = blk: {
595 | const field_name = switch (try source.next()) {
596 | .string => |s| s,
597 | else => return error.UnexpectedToken,
598 | };
599 | if (!std.mem.eql(u8, field_name, "Kind"))
600 | return error.UnexpectedToken;
601 | break :blk switch (try source.next()) {
602 | .string => |s| s,
603 | else => return error.UnexpectedToken,
604 | };
605 | };
606 | if (@hasField(Attrs, "MemorySize")) {
607 | if (std.mem.eql(u8, kind, "MemorySize")) {
608 | result.MemorySize = try parseUnionObject(
609 | MemorySize,
610 | allocator,
611 | source,
612 | options,
613 | );
614 | continue;
615 | }
616 | }
617 | if (@hasField(Attrs, "FreeWith")) {
618 | if (std.mem.eql(u8, kind, "FreeWith")) {
619 | result.FreeWith = try parseUnionObject(
620 | FreeWith,
621 | allocator,
622 | source,
623 | options,
624 | );
625 | continue;
626 | }
627 | }
628 | std.log.err(
629 | "unknown object attribute object kind '{s}' for type {s}",
630 | .{ kind, @typeName(Attrs) },
631 | );
632 | return error.UnknownField;
633 | },
634 | else => |token| {
635 | std.log.err(
636 | "expected token string, object or array_close but got {s} for attr type {s}",
637 | .{ @tagName(token), @typeName(Attrs) },
638 | );
639 | return error.UnexpectedToken;
640 | },
641 | }
642 | }
643 | }
644 |
645 | pub fn jsonParseUnionKind(
646 | comptime KindEnum: type,
647 | type_name: []const u8,
648 | source: anytype,
649 | map: std.StaticStringMap(KindEnum),
650 | ) std.json.ParseError(@TypeOf(source.*))!KindEnum {
651 | switch (try source.next()) {
652 | .string => |field_name| if (!std.mem.eql(u8, field_name, "Kind")) {
653 | std.log.err(
654 | "expected first field of {s} to be 'Kind' but got '{s}'",
655 | .{ type_name, field_name },
656 | );
657 | return error.UnexpectedToken;
658 | },
659 | else => return error.UnexpectedToken,
660 | }
661 | const kind_str = switch (try source.next()) {
662 | .string => |s| s,
663 | else => return error.UnexpectedToken,
664 | };
665 | return map.get(kind_str) orelse {
666 | std.log.err("unknown {s} Kind '{s}'", .{ type_name, kind_str });
667 | return error.UnexpectedToken;
668 | };
669 | }
670 |
671 | pub fn parseUnionObject(
672 | comptime T: type,
673 | allocator: std.mem.Allocator,
674 | source: anytype,
675 | options: std.json.ParseOptions,
676 | ) !T {
677 | const structInfo = switch (@typeInfo(T)) {
678 | .@"struct" => |i| i,
679 | else => @compileError("Unable to parse into non-struct type '" ++ @typeName(T) ++ "'"),
680 | };
681 |
682 | var r: T = undefined;
683 | var fields_seen = [_]bool{false} ** structInfo.fields.len;
684 |
685 | while (true) {
686 | var name_token: ?std.json.Token = try source.nextAllocMax(allocator, .alloc_if_needed, options.max_value_len.?);
687 | const field_name = switch (name_token.?) {
688 | inline .string, .allocated_string => |slice| slice,
689 | .object_end => { // No more fields.
690 | break;
691 | },
692 | else => {
693 | return error.UnexpectedToken;
694 | },
695 | };
696 |
697 | inline for (structInfo.fields, 0..) |field, i| {
698 | if (field.is_comptime) @compileError("comptime fields are not supported: " ++ @typeName(T) ++ "." ++ field.name);
699 | if (std.mem.eql(u8, field.name, field_name)) {
700 | // Free the name token now in case we're using an allocator that optimizes freeing the last allocated object.
701 | // (Recursing into innerParse() might trigger more allocations.)
702 | freeAllocated(allocator, name_token.?);
703 | name_token = null;
704 | if (fields_seen[i]) {
705 | switch (options.duplicate_field_behavior) {
706 | .use_first => {
707 | // Parse and ignore the redundant value.
708 | // We don't want to skip the value, because we want type checking.
709 | _ = try std.json.innerParse(field.type, allocator, source, options);
710 | break;
711 | },
712 | .@"error" => return error.DuplicateField,
713 | .use_last => {},
714 | }
715 | }
716 | @field(r, field.name) = try std.json.innerParse(field.type, allocator, source, options);
717 | fields_seen[i] = true;
718 | break;
719 | }
720 | } else {
721 | // Didn't match anything.
722 | std.log.err("unknown field '{s}' on type {s}", .{ field_name, @typeName(T) });
723 | freeAllocated(allocator, name_token.?);
724 | if (options.ignore_unknown_fields) {
725 | try source.skipValue();
726 | } else {
727 | return error.UnknownField;
728 | }
729 | }
730 | }
731 | inline for (structInfo.fields, 0..) |field, i| {
732 | if (!fields_seen[i] and !std.mem.eql(u8, field.name, "Comment")) {
733 | std.log.err("field '{s}' has not been set", .{field.name});
734 | return error.MissingField;
735 | }
736 | }
737 | return r;
738 | }
739 |
740 | fn freeAllocated(allocator: std.mem.Allocator, token: std.json.Token) void {
741 | switch (token) {
742 | .allocated_number, .allocated_string => |slice| {
743 | allocator.free(slice);
744 | },
745 | else => {},
746 | }
747 | }
748 |
749 | fn expectFieldName(
750 | source: anytype,
751 | name: []const u8,
752 | ) !void {
753 | switch (try source.next()) {
754 | .string => |s| {
755 | if (!std.mem.eql(u8, s, name)) {
756 | std.log.err(
757 | "expected field '{s}' but got '{s}'",
758 | .{ name, s },
759 | );
760 | return error.UnexpectedToken;
761 | }
762 | },
763 | else => return error.UnexpectedToken,
764 | }
765 | }
766 |
--------------------------------------------------------------------------------
/src/pass1.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const metadata = @import("metadata.zig");
3 |
4 | const common = @import("common.zig");
5 | const fatal = common.fatal;
6 |
7 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
8 | const allocator = arena.allocator();
9 |
10 | const BufferedWriter = std.io.BufferedWriter(4096, std.fs.File.Writer);
11 | const OutWriter = BufferedWriter.Writer;
12 |
13 | pub fn main() !u8 {
14 | const all_args = try std.process.argsAlloc(allocator);
15 | // don't care about freeing args
16 |
17 | const cmd_args = all_args[1..];
18 | if (cmd_args.len != 2) {
19 | std.log.err("expected 2 arguments but got {}", .{cmd_args.len});
20 | return 1;
21 | }
22 | const win32json_path = cmd_args[0];
23 | const out_filename = cmd_args[1];
24 |
25 | const api_path = try std.fs.path.join(allocator, &.{ win32json_path, "api" });
26 | var api_dir = try std.fs.cwd().openDir(api_path, .{ .iterate = true });
27 | defer api_dir.close();
28 |
29 | var api_list = std.ArrayList([]const u8).init(allocator);
30 | defer {
31 | for (api_list.items) |api_name| {
32 | allocator.free(api_name);
33 | }
34 | api_list.deinit();
35 | }
36 | try common.readApiList(api_dir, &api_list);
37 |
38 | // sort so our data is always in the same order
39 | std.mem.sort([]const u8, api_list.items, {}, common.asciiLessThanIgnoreCase);
40 |
41 | const out_file = try std.fs.cwd().createFile(out_filename, .{});
42 | defer out_file.close();
43 | var buffered_writer = BufferedWriter{
44 | .unbuffered_writer = out_file.writer(),
45 | };
46 | const out = buffered_writer.writer();
47 |
48 | try out.writeAll("{\n");
49 | var json_obj_prefix: []const u8 = "";
50 |
51 | for (api_list.items) |api_json_basename| {
52 | const name = api_json_basename[0 .. api_json_basename.len - 5];
53 | try out.print(" {s}\"{s}\": {{\n", .{ json_obj_prefix, name });
54 | var file = try api_dir.openFile(api_json_basename, .{});
55 | defer file.close();
56 | try pass1OnFile(out, api_path, api_json_basename, file);
57 | try out.writeAll(" }\n");
58 | json_obj_prefix = ",";
59 | }
60 |
61 | try out.writeAll("}\n");
62 | try buffered_writer.flush();
63 | std.log.info("wrote {s}", .{out_filename});
64 | return 0;
65 | }
66 |
67 | fn pass1OnFile(out: OutWriter, api_dir: []const u8, filename: []const u8, file: std.fs.File) !void {
68 | var json_arena_instance = std.heap.ArenaAllocator.init(allocator);
69 | defer json_arena_instance.deinit();
70 | const json_arena = json_arena_instance.allocator();
71 |
72 | const content = try file.readToEndAlloc(json_arena, std.math.maxInt(usize));
73 | // no need to free, owned by json_arena
74 | const parse_start = std.time.milliTimestamp();
75 |
76 | const api = metadata.Api.parse(json_arena, api_dir, filename, content);
77 | // no need to free, owned by json_arena
78 | const parse_time = std.time.milliTimestamp() - parse_start;
79 | std.log.info("{} ms: parse time for '{s}'", .{ parse_time, filename });
80 |
81 | try pass1OnJson(out, api);
82 | }
83 |
84 | fn writeType(out: OutWriter, json_obj_prefix: []const u8, name: []const u8, kind: []const u8) !void {
85 | try out.print(" {s}\"{s}\": {{\"Kind\":\"{s}\"}}\n", .{ json_obj_prefix, name, kind });
86 | }
87 |
88 | fn pass1OnJson(out: OutWriter, api: metadata.Api) !void {
89 | var json_obj_prefix: []const u8 = "";
90 |
91 | for (api.Types) |t| {
92 | switch (t.Kind) {
93 | .NativeTypedef => |n| try generateNativeTypedef(out, json_obj_prefix, t, n),
94 | .Enum => try writeType(out, json_obj_prefix, t.Name, "Enum"),
95 | .Struct => try writeType(out, json_obj_prefix, t.Name, "Struct"),
96 | .Union => try writeType(out, json_obj_prefix, t.Name, "Union"),
97 | .ComClassID => continue,
98 | .Com => |com| try writeComType(out, json_obj_prefix, t, com),
99 | .FunctionPointer => try writeType(out, json_obj_prefix, t.Name, "FunctionPointer"),
100 | }
101 | json_obj_prefix = ",";
102 | }
103 | }
104 |
105 | fn generateNativeTypedef(
106 | out: OutWriter,
107 | json_obj_prefix: []const u8,
108 | t: metadata.Type,
109 | native_typedef: metadata.NativeTypedef,
110 | ) !void {
111 | // HANDLE PSTR and PWSTR specially because win32metadata is not properly declaring them as arrays, only pointers
112 | // not sure if this is a real issue with the metadata or intentional
113 | const special: enum { pstr, pwstr, other } = blk: {
114 | if (std.mem.eql(u8, t.Name, "PSTR")) break :blk .pstr;
115 | if (std.mem.eql(u8, t.Name, "PWSTR")) break :blk .pwstr;
116 | break :blk .other;
117 | };
118 | if (special == .pstr or special == .pwstr) {
119 | try writeType(out, json_obj_prefix, t.Name, "Pointer");
120 | return;
121 | }
122 |
123 | // we should be able to ignore also_usable_for_node because the def_type should be the same as the type being defined
124 | //switch (also_usable_for_node) {
125 | // .string => |also_usable_for| {
126 | // if (also_usable_type_api_map.get(also_usable_for)) |api| {
127 | // try sdk_file.addApiImport(arches, also_usable_for, api, json.Array { .items = &[_]json.Value{}, .capacity = 0, .allocator = allocator });
128 | // try writer.linef("//TODO: type '{s}' is \"AlsoUsableFor\" '{s}' which means this type is implicitly", .{tmp_name, also_usable_for});
129 | // try writer.linef("// convertible to '{s}' but not the other way around. I don't know how to do this", .{also_usable_for});
130 | // try writer.line("// in Zig so for now I'm just defining it as an alias");
131 | // try writer.linef("pub const {s} = {s};", .{tmp_name, also_usable_for});
132 | // //try writer.linef("pub const {s} = extern struct {{ base: {s} }};", .{tmp_name, also_usable_for});
133 | // } else std.debug.panic("AlsoUsableFor type '{s}' is missing from alsoUsableForApiMap", .{also_usable_for});
134 | // return;
135 | // },
136 | // .Null => {},
137 | // else => jsonPanic(),
138 | //}
139 |
140 | // NOTE: for now, I'm just hardcoding a few types to redirect to the ones defined in 'std'
141 | // this allows apps to use values of these types interchangeably with bindings in std
142 | if (@import("handletypes.zig").std_handle_types.get(t.Name)) |_| {
143 | try writeType(out, json_obj_prefix, t.Name, "Pointer");
144 | return;
145 | }
146 | // workaround https://github.com/microsoft/win32metadata/issues/395
147 | if (@import("handletypes.zig").handle_types.get(t.Name)) |_| {
148 | try writeType(out, json_obj_prefix, t.Name, "Pointer");
149 | return;
150 | }
151 |
152 | switch (native_typedef.Def) {
153 | .Native => |native| if (isIntegral(native.Name)) {
154 | try writeType(out, json_obj_prefix, t.Name, "Integral");
155 | } else std.debug.panic("unhandled Native kind in NativeTypedef '{s}'", .{@tagName(native.Name)}),
156 | .PointerTo => try writeType(out, json_obj_prefix, t.Name, "Pointer"),
157 | else => |kind| std.debug.panic("unhandled NativeTypedef kind '{s}'", .{@tagName(kind)}),
158 | }
159 | }
160 |
161 | fn isIntegral(native: metadata.TypeRefNative) bool {
162 | return switch (native) {
163 | .Void => false,
164 | .Boolean => false,
165 | .SByte => true,
166 | .Byte => true,
167 | .Int16 => true,
168 | .UInt16 => true,
169 | .Int32 => true,
170 | .UInt32 => true,
171 | .Int64 => true,
172 | .UInt64 => true,
173 | .Char => false,
174 | .Single => false,
175 | .Double => false,
176 | .String => false,
177 | .IntPtr => true,
178 | .UIntPtr => true,
179 | .Guid => false,
180 | };
181 | }
182 |
183 | fn writeComType(
184 | out: OutWriter,
185 | json_obj_prefix: []const u8,
186 | t: metadata.Type,
187 | com: metadata.Com,
188 | ) !void {
189 | const iface: ?common.ComInterface = blk: {
190 | if (com.Interface) |iface|
191 | break :blk common.getComInterface(iface);
192 | if (!std.mem.eql(u8, t.Name, "IUnknown")) {
193 | std.log.warn("com type '{s}' does not have an interface (file bug if we're on the latest metadata version)", .{t.Name});
194 | }
195 | break :blk null;
196 | };
197 |
198 | try out.print(
199 | " {s}\"{s}\": {{\"Kind\":\"Com\",\"Interface\":{?}}}\n",
200 | .{ json_obj_prefix, t.Name, iface },
201 | );
202 | }
203 |
--------------------------------------------------------------------------------
/src/pass1data.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const metadata = @import("metadata.zig");
3 | const jsonextra = @import("jsonextra.zig");
4 |
5 | pub const Root = jsonextra.ArrayHashMap(TypeMap);
6 |
7 | pub fn parseRoot(
8 | allocator: std.mem.Allocator,
9 | json_filename: []const u8,
10 | content: []const u8,
11 | ) Root {
12 | var diagnostics = std.json.Diagnostics{};
13 | var scanner = std.json.Scanner.initCompleteInput(allocator, content);
14 | defer scanner.deinit();
15 | scanner.enableDiagnostics(&diagnostics);
16 | return std.json.parseFromTokenSourceLeaky(
17 | Root,
18 | allocator,
19 | &scanner,
20 | .{},
21 | ) catch |err| {
22 | std.log.err(
23 | "{s}:{}:{}: {s}",
24 | .{
25 | json_filename,
26 | diagnostics.getLine(),
27 | diagnostics.getColumn(),
28 | @errorName(err),
29 | },
30 | );
31 | @panic("json error");
32 | };
33 | }
34 |
35 | pub const TypeMap = jsonextra.ArrayHashMap(Type);
36 |
37 | pub const TypeKind = enum {
38 | Integral,
39 | Enum,
40 | Struct,
41 | Union,
42 | Pointer,
43 | FunctionPointer,
44 | Com,
45 | };
46 | const type_kinds = std.StaticStringMap(TypeKind).initComptime(.{
47 | .{ "Integral", .Integral },
48 | .{ "Enum", .Enum },
49 | .{ "Struct", .Struct },
50 | .{ "Union", .Union },
51 | .{ "Pointer", .Pointer },
52 | .{ "FunctionPointer", .FunctionPointer },
53 | .{ "Com", .Com },
54 | });
55 |
56 | const EmptyStruct = struct {};
57 | pub const Type = union(TypeKind) {
58 | Integral: EmptyStruct,
59 | Enum: EmptyStruct,
60 | Struct: EmptyStruct,
61 | Union: EmptyStruct,
62 | Pointer: EmptyStruct,
63 | FunctionPointer: EmptyStruct,
64 | Com: Com,
65 |
66 | pub fn jsonParse(
67 | allocator: std.mem.Allocator,
68 | source: anytype,
69 | options: std.json.ParseOptions,
70 | ) std.json.ParseError(@TypeOf(source.*))!Type {
71 | if (.object_begin != try source.next()) return error.UnexpectedToken;
72 | switch (try metadata.jsonParseUnionKind(TypeKind, "Type", source, type_kinds)) {
73 | .Integral => return .{ .Integral = try metadata.parseUnionObject(EmptyStruct, allocator, source, options) },
74 | .Enum => return .{ .Enum = try metadata.parseUnionObject(EmptyStruct, allocator, source, options) },
75 | .Struct => return .{ .Struct = try metadata.parseUnionObject(EmptyStruct, allocator, source, options) },
76 | .Union => return .{ .Union = try metadata.parseUnionObject(EmptyStruct, allocator, source, options) },
77 | .Pointer => return .{ .Pointer = try metadata.parseUnionObject(EmptyStruct, allocator, source, options) },
78 | .FunctionPointer => return .{ .FunctionPointer = try metadata.parseUnionObject(EmptyStruct, allocator, source, options) },
79 | .Com => return .{ .Com = try metadata.parseUnionObject(Com, allocator, source, options) },
80 | }
81 | }
82 | };
83 |
84 | pub const Com = struct {
85 | Interface: ?metadata.TypeRef,
86 | };
87 |
--------------------------------------------------------------------------------
/src/static/.gitignore:
--------------------------------------------------------------------------------
1 | .zig-cache/
2 |
--------------------------------------------------------------------------------
/src/static/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2021 Jonathan Marler
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/src/static/README.md:
--------------------------------------------------------------------------------
1 | # zigwin32
2 |
3 | A complete autogenerated set of Zig bindings for the Win32 API.
4 |
5 | These bindings are generated by https://github.com/marlersoft/zigwin32gen
6 |
--------------------------------------------------------------------------------
/src/static/build.zig:
--------------------------------------------------------------------------------
1 | const builtin = @import("builtin");
2 | const std = @import("std");
3 |
4 | pub fn build(b: *std.Build) void {
5 | _ = b.addModule("win32", .{
6 | .root_source_file = b.path("win32.zig"),
7 | });
8 | }
9 |
--------------------------------------------------------------------------------
/src/static/win32.zig:
--------------------------------------------------------------------------------
1 | /// This file is just a stub so that we can reflect on win32/zig.zig in order to
2 | /// get its list of exports before generating everything.zig. The definitions don't
3 | /// need to be correct, they just need to exist and "make sense" enough to
4 | /// get win32/zig.zig to compile.
5 | pub const zig = @import("win32/zig.zig");
6 | pub const foundation = struct {
7 | pub const BOOL = i32;
8 | pub const WIN32_ERROR = enum {};
9 | pub const HRESULT = i32;
10 | pub const HWND = *opaque {};
11 | pub const HANDLE = @import("std").os.windows.HANDLE;
12 | pub const LPARAM = isize;
13 | pub const POINT = struct {};
14 | pub const SIZE = struct {};
15 | pub const RECT = struct {};
16 | };
17 | pub const graphics = struct {
18 | pub const gdi = struct {
19 | pub const HDC = *opaque {};
20 | pub const HGDIOBJ = *opaque {};
21 | pub const HBRUSH = HGDIOBJ;
22 | pub const PAINTSTRUCT = struct {};
23 | };
24 | };
25 | pub const ui = struct {
26 | pub const windows_and_messaging = struct {
27 | pub const MESSAGEBOX_STYLE = struct {
28 | ICONASTERISK: u1 = 0,
29 | };
30 | };
31 | };
32 |
--------------------------------------------------------------------------------
/src/static/win32/windowlongptr.zig:
--------------------------------------------------------------------------------
1 | //! The SetWindowLongPtr and GetWindowLongPtr variants are missing because they are 64-bit only
2 | //! See: https://github.com/microsoft/win32metadata/issues/142 (SetWindowLongPtr/GetWindowLongPtr are missing)
3 | const win32 = @import("../win32.zig");
4 |
5 | pub const SetWindowLongPtrA = if (@sizeOf(usize) == 8)
6 | win32.ui.windows_and_messaging.SetWindowLongPtrA
7 | else
8 | win32.ui.windows_and_messaging.SetWindowLongA;
9 |
10 | pub const SetWindowLongPtrW = if (@sizeOf(usize) == 8)
11 | win32.ui.windows_and_messaging.SetWindowLongPtrW
12 | else
13 | win32.ui.windows_and_messaging.SetWindowLongW;
14 |
15 | pub const GetWindowLongPtrA = if (@sizeOf(usize) == 8)
16 | win32.ui.windows_and_messaging.GetWindowLongPtrA
17 | else
18 | win32.ui.windows_and_messaging.GetWindowLongA;
19 |
20 | pub const GetWindowLongPtrW = if (@sizeOf(usize) == 8)
21 | win32.ui.windows_and_messaging.GetWindowLongPtrW
22 | else
23 | win32.ui.windows_and_messaging.GetWindowLongW;
24 |
25 | pub const SetWindowLongPtr = switch (@import("zig.zig").unicode_mode) {
26 | .ansi => SetWindowLongPtrA,
27 | .wide => SetWindowLongPtrW,
28 | .unspecified => if (@import("builtin").is_test) *opaque{} else @compileError("'SetWindowLongPtr' requires that UNICODE be set to true or false in the root module"),
29 | };
30 |
31 | pub const GetWindowLongPtr = switch (@import("zig.zig").unicode_mode) {
32 | .ansi => GetWindowLongPtrA,
33 | .wide => GetWindowLongPtrW,
34 | .unspecified => if (@import("builtin").is_test) *opaque{} else @compileError("'GetWindowLongPtr' requires that UNICODE be set to true or false in the root module"),
35 | };
36 |
--------------------------------------------------------------------------------
/src/static/win32/zig.zig:
--------------------------------------------------------------------------------
1 | //! This module is maintained by hand and is copied to the generated code directory
2 | const std = @import("std");
3 | const builtin = @import("builtin");
4 | const testing = std.testing;
5 |
6 | const mod_root = @import("../win32.zig");
7 | const win32 = struct {
8 | const BOOL = mod_root.foundation.BOOL;
9 | const WIN32_ERROR = mod_root.foundation.WIN32_ERROR;
10 | const HRESULT = mod_root.foundation.HRESULT;
11 | const HWND = mod_root.foundation.HWND;
12 | const HANDLE = mod_root.foundation.HANDLE;
13 | const LPARAM = mod_root.foundation.LPARAM;
14 | const POINT = mod_root.foundation.POINT;
15 | const SIZE = mod_root.foundation.SIZE;
16 | const RECT = mod_root.foundation.RECT;
17 |
18 | const HDC = mod_root.graphics.gdi.HDC;
19 | const HGDIOBJ = mod_root.graphics.gdi.HGDIOBJ;
20 | const HBRUSH = mod_root.graphics.gdi.HBRUSH;
21 | const PAINTSTRUCT = mod_root.graphics.gdi.PAINTSTRUCT;
22 |
23 | const GetLastError = mod_root.foundation.GetLastError;
24 | const CloseHandle = mod_root.foundation.CloseHandle;
25 | const FormatMessageA = mod_root.system.diagnostics.debug.FormatMessageA;
26 | const DeleteObject = mod_root.graphics.gdi.DeleteObject;
27 | const DeleteDC = mod_root.graphics.gdi.DeleteDC;
28 | const InvalidateRect = mod_root.graphics.gdi.InvalidateRect;
29 | const BeginPaint = mod_root.graphics.gdi.BeginPaint;
30 | const EndPaint = mod_root.graphics.gdi.EndPaint;
31 | const CreateSolidBrush = mod_root.graphics.gdi.CreateSolidBrush;
32 | const FillRect = mod_root.graphics.gdi.FillRect;
33 | const TextOutA = mod_root.graphics.gdi.TextOutA;
34 | const TextOutW = mod_root.graphics.gdi.TextOutW;
35 | const GetTextExtentPoint32A = mod_root.graphics.gdi.GetTextExtentPoint32A;
36 | const GetTextExtentPoint32W = mod_root.graphics.gdi.GetTextExtentPoint32W;
37 | const MESSAGEBOX_STYLE = mod_root.ui.windows_and_messaging.MESSAGEBOX_STYLE;
38 | const MessageBoxA = mod_root.ui.windows_and_messaging.MessageBoxA;
39 | const GetWindowLongPtrA = mod_root.ui.windows_and_messaging.GetWindowLongPtrA;
40 | const GetWindowLongPtrW = mod_root.ui.windows_and_messaging.GetWindowLongPtrW;
41 | const SetWindowLongPtrA = mod_root.ui.windows_and_messaging.SetWindowLongPtrA;
42 | const SetWindowLongPtrW = mod_root.ui.windows_and_messaging.SetWindowLongPtrW;
43 | const GetClientRect = mod_root.ui.windows_and_messaging.GetClientRect;
44 | const GetDpiForWindow = mod_root.ui.hi_dpi.GetDpiForWindow;
45 | };
46 |
47 | const root = @import("root");
48 | pub const UnicodeMode = enum { ansi, wide, unspecified };
49 | pub const unicode_mode: UnicodeMode = if (@hasDecl(root, "UNICODE")) (if (root.UNICODE) .wide else .ansi) else .unspecified;
50 |
51 | const is_zig_0_11 = std.mem.eql(u8, builtin.zig_version_string, "0.11.0");
52 | const zig_version_0_13 = std.SemanticVersion{ .major = 0, .minor = 13, .patch = 0 };
53 |
54 | pub const L = std.unicode.utf8ToUtf16LeStringLiteral;
55 |
56 | pub const TCHAR = switch (unicode_mode) {
57 | .ansi => u8,
58 | .wide => u16,
59 | .unspecified => if (builtin.is_test) void else @compileError("'TCHAR' requires that UNICODE be set to true or false in the root module"),
60 | };
61 | pub const _T = switch (unicode_mode) {
62 | .ansi => (struct {
63 | pub fn _T(comptime str: []const u8) *const [str.len:0]u8 {
64 | return str;
65 | }
66 | })._T,
67 | .wide => L,
68 | .unspecified => if (builtin.is_test) void else @compileError("'_T' requires that UNICODE be set to true or false in the root module"),
69 | };
70 |
71 | pub const Arch = enum { X86, X64, Arm64 };
72 | pub const arch: Arch = switch (builtin.target.cpu.arch) {
73 | .x86 => .X86,
74 | .x86_64 => .X64,
75 | .arm, .armeb, .aarch64 => .Arm64,
76 | else => @compileError("unhandled arch " ++ @tagName(builtin.target.cpu.arch)),
77 | };
78 |
79 | // TODO: this should probably be in the standard lib somewhere?
80 | pub const Guid = extern union {
81 | Ints: extern struct {
82 | a: u32,
83 | b: u16,
84 | c: u16,
85 | d: [8]u8,
86 | },
87 | Bytes: [16]u8,
88 |
89 | const big_endian_hex_offsets = [16]u6{ 0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34 };
90 | const little_endian_hex_offsets = [16]u6{ 6, 4, 2, 0, 11, 9, 16, 14, 19, 21, 24, 26, 28, 30, 32, 34 };
91 |
92 | const hex_offsets = if (is_zig_0_11) switch (builtin.target.cpu.arch.endian()) {
93 | .Big => big_endian_hex_offsets,
94 | .Little => little_endian_hex_offsets,
95 | } else switch (builtin.target.cpu.arch.endian()) {
96 | .big => big_endian_hex_offsets,
97 | .little => little_endian_hex_offsets,
98 | };
99 |
100 | pub fn initString(s: []const u8) Guid {
101 | var guid = Guid{ .Bytes = undefined };
102 | for (hex_offsets, 0..) |hex_offset, i| {
103 | //guid.Bytes[i] = decodeHexByte(s[offset..offset+2]);
104 | guid.Bytes[i] = decodeHexByte([2]u8{ s[hex_offset], s[hex_offset + 1] });
105 | }
106 | return guid;
107 | }
108 | };
109 | comptime {
110 | std.debug.assert(@sizeOf(Guid) == 16);
111 | }
112 |
113 | // TODO: is this in the standard lib somewhere?
114 | fn hexVal(c: u8) u4 {
115 | if (c <= '9') return @as(u4, @intCast(c - '0'));
116 | if (c >= 'a') return @as(u4, @intCast(c + 10 - 'a'));
117 | return @as(u4, @intCast(c + 10 - 'A'));
118 | }
119 |
120 | // TODO: is this in the standard lib somewhere?
121 | fn decodeHexByte(hex: [2]u8) u8 {
122 | return @as(u8, @intCast(hexVal(hex[0]))) << 4 | hexVal(hex[1]);
123 | }
124 |
125 | test "Guid" {
126 | if (is_zig_0_11) {
127 | try testing.expect(std.mem.eql(u8, switch (builtin.target.cpu.arch.endian()) {
128 | .Big => "\x01\x23\x45\x67\x89\xAB\xEF\x10\x32\x54\x76\x98\xba\xdc\xfe\x91",
129 | .Little => "\x67\x45\x23\x01\xAB\x89\x10\xEF\x32\x54\x76\x98\xba\xdc\xfe\x91",
130 | }, &Guid.initString("01234567-89AB-EF10-3254-7698badcfe91").Bytes));
131 | } else {
132 | try testing.expect(std.mem.eql(u8, switch (builtin.target.cpu.arch.endian()) {
133 | .big => "\x01\x23\x45\x67\x89\xAB\xEF\x10\x32\x54\x76\x98\xba\xdc\xfe\x91",
134 | .little => "\x67\x45\x23\x01\xAB\x89\x10\xEF\x32\x54\x76\x98\xba\xdc\xfe\x91",
135 | }, &Guid.initString("01234567-89AB-EF10-3254-7698badcfe91").Bytes));
136 | }
137 | }
138 |
139 | pub const PropertyKey = extern struct {
140 | fmtid: Guid,
141 | pid: u32,
142 | pub fn init(fmtid: []const u8, pid: u32) PropertyKey {
143 | return .{
144 | .fmtid = Guid.initString(fmtid),
145 | .pid = pid,
146 | };
147 | }
148 | };
149 |
150 | pub fn FAILED(hr: win32.HRESULT) bool {
151 | return hr < 0;
152 | }
153 | pub fn SUCCEEDED(hr: win32.HRESULT) bool {
154 | return hr >= 0;
155 | }
156 |
157 | // These constants were removed from the metadata to allow each projection
158 | // to define them however they like (see https://github.com/microsoft/win32metadata/issues/530)
159 | pub const FALSE: win32.BOOL = 0;
160 | pub const TRUE: win32.BOOL = 1;
161 |
162 | /// Returns a formatter that will print the given error in the following format:
163 | ///
164 | /// ([...])
165 | ///
166 | /// For example:
167 | ///
168 | /// 2 (The system cannot find the file specified.)
169 | /// 5 (Access is denied.)
170 | ///
171 | /// The error is formatted using FormatMessage into a stack allocated buffer
172 | /// of 300 bytes. If the message exceeds 300 bytes (Messages can be arbitrarily
173 | /// long) then "..." is appended to the message. The message may contain newlines
174 | /// and carriage returns but any trailing ones are trimmed.
175 | ///
176 | /// Provide the 's' fmt specifier to omit the error code.
177 | pub fn fmtError(error_code: u32) FormatError(300) {
178 | return .{ .error_code = error_code };
179 | }
180 | pub fn FormatError(comptime max_len: usize) type {
181 | return struct {
182 | error_code: u32,
183 | pub fn format(
184 | self: @This(),
185 | comptime fmt: []const u8,
186 | options: std.fmt.FormatOptions,
187 | writer: anytype,
188 | ) @TypeOf(writer).Error!void {
189 | _ = options;
190 |
191 | const with_code = comptime blk: {
192 | if (std.mem.eql(u8, fmt, "")) break :blk true;
193 | if (std.mem.eql(u8, fmt, "s")) break :blk false;
194 | @compileError("expected '{}' or '{s}' but got '{" ++ fmt ++ "}'");
195 | };
196 | if (with_code) try writer.print("{} (", .{self.error_code});
197 | var buf: [max_len]u8 = undefined;
198 | const len = win32.FormatMessageA(
199 | .{ .FROM_SYSTEM = 1, .IGNORE_INSERTS = 1 },
200 | null,
201 | self.error_code,
202 | 0,
203 | @ptrCast(&buf),
204 | buf.len,
205 | null,
206 | );
207 | if (len == 0) {
208 | try writer.writeAll("unknown error");
209 | }
210 | const msg = std.mem.trimRight(u8, buf[0..len], "\r\n");
211 | try writer.writeAll(msg);
212 | if (len + 1 >= buf.len) {
213 | try writer.writeAll("...");
214 | }
215 | if (with_code) try writer.writeAll(")");
216 | }
217 | };
218 | }
219 |
220 | threadlocal var thread_is_panicing = false;
221 |
222 | pub const PanicType = switch (builtin.zig_version.order(zig_version_0_13)) {
223 | .lt, .eq => fn ([]const u8, ?*std.builtin.StackTrace, ?usize) noreturn,
224 | .gt => type,
225 | };
226 |
227 | /// Returns a panic handler that can be set in your root module that will show the panic
228 | /// message to the user in a message box, then call the default builtin panic handler.
229 | /// It also handles re-entrancy by skipping the message box if the current thread
230 | /// is already panicing.
231 | pub fn messageBoxThenPanic(
232 | opt: struct {
233 | title: [:0]const u8,
234 | style: win32.MESSAGEBOX_STYLE = .{ .ICONASTERISK = 1 },
235 | // TODO: add option/logic to include the stacktrace in the messagebox
236 | },
237 | ) PanicType {
238 | switch (comptime builtin.zig_version.order(zig_version_0_13)) {
239 | .lt, .eq => return struct {
240 | pub fn panic(
241 | msg: []const u8,
242 | error_return_trace: ?*std.builtin.StackTrace,
243 | ret_addr: ?usize,
244 | ) noreturn {
245 | if (!thread_is_panicing) {
246 | thread_is_panicing = true;
247 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
248 | const msg_z: [:0]const u8 = if (std.fmt.allocPrintZ(
249 | arena.allocator(),
250 | "{s}",
251 | .{msg},
252 | )) |msg_z| msg_z else |_| "failed allocate error message";
253 | _ = win32.MessageBoxA(null, msg_z, opt.title, opt.style);
254 | }
255 | std.builtin.default_panic(msg, error_return_trace, ret_addr);
256 | }
257 | }.panic,
258 | .gt => {},
259 | }
260 | return std.debug.FullPanic(struct {
261 | pub fn panic(
262 | msg: []const u8,
263 | ret_addr: ?usize,
264 | ) noreturn {
265 | if (!thread_is_panicing) {
266 | thread_is_panicing = true;
267 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
268 | const msg_z: [:0]const u8 = if (std.fmt.allocPrintZ(
269 | arena.allocator(),
270 | "{s}",
271 | .{msg},
272 | )) |msg_z| msg_z else |_| "failed allocate error message";
273 | _ = win32.MessageBoxA(null, msg_z, opt.title, opt.style);
274 | }
275 | std.debug.defaultPanic(msg, ret_addr);
276 | }
277 | }.panic);
278 | }
279 |
280 | /// Calls std.debug.panic with a message that indicates what failed and the
281 | /// associated win32 error code.
282 | pub fn panicWin32(what: []const u8, err: win32.WIN32_ERROR) noreturn {
283 | std.debug.panic("{s} failed, error={}", .{ what, err });
284 | }
285 |
286 | /// Calls std.debug.panic with a message that indicates what failed and the
287 | /// associated hresult error code.
288 | pub fn panicHresult(what: []const u8, hresult: win32.HRESULT) noreturn {
289 | std.debug.panic("{s} failed, hresult=0x{x}", .{ what, @as(u32, @bitCast(hresult)) });
290 | }
291 |
292 | /// calls CloseHandle, panics on failure
293 | pub fn closeHandle(handle: win32.HANDLE) void {
294 | if (0 == win32.CloseHandle(handle)) panicWin32(
295 | "CloseHandle",
296 | win32.GetLastError(),
297 | );
298 | }
299 |
300 | pub fn xFromLparam(lparam: win32.LPARAM) i16 {
301 | return @bitCast(loword(lparam));
302 | }
303 | pub fn yFromLparam(lparam: win32.LPARAM) i16 {
304 | return @bitCast(hiword(lparam));
305 | }
306 | pub fn pointFromLparam(lparam: win32.LPARAM) win32.POINT {
307 | return win32.POINT{ .x = xFromLparam(lparam), .y = yFromLparam(lparam) };
308 | }
309 |
310 | pub fn loword(value: anytype) u16 {
311 | switch (comptime builtin.zig_version.order(zig_version_0_13)) {
312 | .gt => switch (@typeInfo(@TypeOf(value))) {
313 | .int => |int| switch (int.signedness) {
314 | .signed => return loword(@as(@Type(.{ .int = .{ .signedness = .unsigned, .bits = int.bits } }), @bitCast(value))),
315 | .unsigned => return if (int.bits <= 16) value else @intCast(0xffff & value),
316 | },
317 | else => {},
318 | },
319 | .lt, .eq => switch (@typeInfo(@TypeOf(value))) {
320 | .Int => |int| switch (int.signedness) {
321 | .signed => return loword(@as(@Type(.{ .Int = .{ .signedness = .unsigned, .bits = int.bits } }), @bitCast(value))),
322 | .unsigned => return if (int.bits <= 16) value else @intCast(0xffff & value),
323 | },
324 | else => {},
325 | },
326 | }
327 | @compileError("unsupported type " ++ @typeName(@TypeOf(value)));
328 | }
329 | pub fn hiword(value: anytype) u16 {
330 | switch (comptime builtin.zig_version.order(zig_version_0_13)) {
331 | .gt => switch (@typeInfo(@TypeOf(value))) {
332 | .int => |int| switch (int.signedness) {
333 | .signed => return hiword(@as(@Type(.{ .int = .{ .signedness = .unsigned, .bits = int.bits } }), @bitCast(value))),
334 | .unsigned => return @intCast(0xffff & (value >> 16)),
335 | },
336 | else => {},
337 | },
338 | .lt, .eq => switch (@typeInfo(@TypeOf(value))) {
339 | .Int => |int| switch (int.signedness) {
340 | .signed => return hiword(@as(@Type(.{ .Int = .{ .signedness = .unsigned, .bits = int.bits } }), @bitCast(value))),
341 | .unsigned => return @intCast(0xffff & (value >> 16)),
342 | },
343 | else => {},
344 | },
345 | }
346 | @compileError("unsupported type " ++ @typeName(@TypeOf(value)));
347 | }
348 |
349 | pub const has_window_longptr = switch (arch) {
350 | .X86 => false,
351 | .X64, .Arm64 => true,
352 | };
353 |
354 | pub const getWindowLongPtr = switch (unicode_mode) {
355 | .ansi => getWindowLongPtrA,
356 | .wide => getWindowLongPtrW,
357 | .unspecified => if (builtin.is_test) struct {} else @compileError(
358 | "getWindowLongPtr requires that UNICODE be set to true or false in the root module",
359 | ),
360 | };
361 |
362 | pub const setWindowLongPtr = switch (unicode_mode) {
363 | .ansi => setWindowLongPtrA,
364 | .wide => setWindowLongPtrW,
365 | .unspecified => if (builtin.is_test) struct {} else @compileError(
366 | "setWindowLongPtr requires that UNICODE be set to true or false in the root module",
367 | ),
368 | };
369 |
370 | pub fn getWindowLongPtrA(hwnd: win32.HWND, index: i32) usize {
371 | if (!has_window_longptr) @compileError("this arch does not have GetWindowLongPtr");
372 | return @bitCast(win32.GetWindowLongPtrA(hwnd, @enumFromInt(index)));
373 | }
374 | pub fn getWindowLongPtrW(hwnd: win32.HWND, index: i32) usize {
375 | if (!has_window_longptr) @compileError("this arch does not have GetWindowLongPtr");
376 | return @bitCast(win32.GetWindowLongPtrW(hwnd, @enumFromInt(index)));
377 | }
378 | pub fn setWindowLongPtrA(hwnd: win32.HWND, index: i32, value: usize) usize {
379 | if (!has_window_longptr) @compileError("this arch does not have SetWindowLongPtr");
380 | return @bitCast(win32.SetWindowLongPtrA(hwnd, @enumFromInt(index), @bitCast(value)));
381 | }
382 | pub fn setWindowLongPtrW(hwnd: win32.HWND, index: i32, value: usize) usize {
383 | if (!has_window_longptr) @compileError("this arch does not have SetWindowLongPtr");
384 | return @bitCast(win32.SetWindowLongPtrW(hwnd, @enumFromInt(index), @bitCast(value)));
385 | }
386 |
387 | /// calls DpiForWindow, panics on failure
388 | pub fn dpiFromHwnd(hwnd: win32.HWND) u32 {
389 | const value = win32.GetDpiForWindow(hwnd);
390 | if (value == 0) panicWin32("GetDpiForWindow", win32.GetLastError());
391 | return value;
392 | }
393 |
394 | /// Converts the given DPI to a floating point scale where 96 returns 1.0, 120 return 1.25 and so on.
395 | pub fn scaleFromDpi(comptime Float: type, dpi: u32) Float {
396 | return @as(Float, @floatFromInt(dpi)) / @as(Float, 96.0);
397 | }
398 |
399 | pub fn scaleDpi(comptime T: type, value: T, dpi: u32) T {
400 | std.debug.assert(dpi >= 96);
401 | return switch (comptime builtin.zig_version.order(zig_version_0_13)) {
402 | .gt => switch (@typeInfo(T)) {
403 | .float => value * scaleFromDpi(T, dpi),
404 | .int => @intFromFloat(@round(@as(f32, @floatFromInt(value)) * scaleFromDpi(f32, dpi))),
405 | else => @compileError("scale_dpi does not support type " ++ @typeName(@TypeOf(value))),
406 | },
407 | .lt, .eq => switch (@typeInfo(T)) {
408 | .Float => value * scaleFromDpi(T, dpi),
409 | .Int => @intFromFloat(@round(@as(f32, @floatFromInt(value)) * scaleFromDpi(f32, dpi))),
410 | else => @compileError("scale_dpi does not support type " ++ @typeName(@TypeOf(value))),
411 | },
412 | };
413 | }
414 |
415 | /// wrapper for GetClientRect, panics on failure
416 | pub fn getClientSize(hwnd: win32.HWND) win32.SIZE {
417 | var rect: win32.RECT = undefined;
418 | if (0 == win32.GetClientRect(hwnd, &rect))
419 | panicWin32("GetClientRect", win32.GetLastError());
420 | std.debug.assert(rect.left == 0);
421 | std.debug.assert(rect.top == 0);
422 | return .{ .cx = rect.right, .cy = rect.bottom };
423 | }
424 |
425 | /// Converts comptime values to the given type.
426 | /// Note that this function is called at compile time rather than converting constant values earlier at code generation time.
427 | /// The reason for doing it a compile time is because genzig.zig generates all constants as they are encountered which can
428 | /// be before it knows the constant's type definition, so we delay the convession to compile-time where the compiler knows
429 | /// all type definition.
430 | pub fn typedConst(comptime T: type, comptime value: anytype) T {
431 | return switch (comptime builtin.zig_version.order(zig_version_0_13)) {
432 | .gt => typedConst2(T, T, value),
433 | .lt, .eq => typedConst2_0_13(T, T, value),
434 | };
435 | }
436 |
437 | fn typedConst2(comptime ReturnType: type, comptime SwitchType: type, comptime value: anytype) ReturnType {
438 | const target_type_error = @as([]const u8, "typedConst cannot convert to " ++ @typeName(ReturnType));
439 | const value_type_error = @as([]const u8, "typedConst cannot convert " ++ @typeName(@TypeOf(value)) ++ " to " ++ @typeName(ReturnType));
440 |
441 | switch (@typeInfo(SwitchType)) {
442 | .int => |target_type_info| {
443 | if (value >= std.math.maxInt(SwitchType)) {
444 | if (target_type_info.signedness == .signed) {
445 | const UnsignedT = @Type(std.builtin.Type{ .int = .{ .signedness = .unsigned, .bits = target_type_info.bits } });
446 | return @as(SwitchType, @bitCast(@as(UnsignedT, value)));
447 | }
448 | }
449 | return value;
450 | },
451 | .pointer => |target_type_info| switch (target_type_info.size) {
452 | .one, .many, .c => {
453 | switch (@typeInfo(@TypeOf(value))) {
454 | .comptime_int, .int => {
455 | const usize_value = if (value >= 0) value else @as(usize, @bitCast(@as(isize, value)));
456 | return @as(ReturnType, @ptrFromInt(usize_value));
457 | },
458 | else => @compileError(value_type_error),
459 | }
460 | },
461 | else => target_type_error,
462 | },
463 | .optional => |target_type_info| switch (@typeInfo(target_type_info.child)) {
464 | .pointer => return typedConst2(ReturnType, target_type_info.child, value),
465 | else => target_type_error,
466 | },
467 | .@"enum" => |_| switch (@typeInfo(@TypeOf(value))) {
468 | .Int => return @as(ReturnType, @enumFromInt(value)),
469 | else => target_type_error,
470 | },
471 | else => @compileError(target_type_error),
472 | }
473 | }
474 | fn typedConst2_0_13(comptime ReturnType: type, comptime SwitchType: type, comptime value: anytype) ReturnType {
475 | const target_type_error = @as([]const u8, "typedConst cannot convert to " ++ @typeName(ReturnType));
476 | const value_type_error = @as([]const u8, "typedConst cannot convert " ++ @typeName(@TypeOf(value)) ++ " to " ++ @typeName(ReturnType));
477 |
478 | switch (@typeInfo(SwitchType)) {
479 | .Int => |target_type_info| {
480 | if (value >= std.math.maxInt(SwitchType)) {
481 | if (target_type_info.signedness == .signed) {
482 | const UnsignedT = @Type(std.builtin.Type{ .Int = .{ .signedness = .unsigned, .bits = target_type_info.bits } });
483 | return @as(SwitchType, @bitCast(@as(UnsignedT, value)));
484 | }
485 | }
486 | return value;
487 | },
488 | .Pointer => |target_type_info| switch (target_type_info.size) {
489 | .One, .Many, .C => {
490 | switch (@typeInfo(@TypeOf(value))) {
491 | .ComptimeInt, .Int => {
492 | const usize_value = if (value >= 0) value else @as(usize, @bitCast(@as(isize, value)));
493 | return @as(ReturnType, @ptrFromInt(usize_value));
494 | },
495 | else => @compileError(value_type_error),
496 | }
497 | },
498 | else => target_type_error,
499 | },
500 | .Optional => |target_type_info| switch (@typeInfo(target_type_info.child)) {
501 | .Pointer => return typedConst2_0_13(ReturnType, target_type_info.child, value),
502 | else => target_type_error,
503 | },
504 | .Enum => |_| switch (@typeInfo(@TypeOf(value))) {
505 | .Int => return @as(ReturnType, @enumFromInt(value)),
506 | else => target_type_error,
507 | },
508 | else => @compileError(target_type_error),
509 | }
510 | }
511 | test "typedConst" {
512 | try testing.expectEqual(@as(usize, @bitCast(@as(isize, -1))), @intFromPtr(typedConst(?*opaque {}, -1)));
513 | try testing.expectEqual(@as(usize, @bitCast(@as(isize, -12))), @intFromPtr(typedConst(?*opaque {}, -12)));
514 | try testing.expectEqual(@as(u32, 0xffffffff), typedConst(u32, 0xffffffff));
515 | try testing.expectEqual(@as(i32, @bitCast(@as(u32, 0x80000000))), typedConst(i32, 0x80000000));
516 | }
517 |
518 | // =============================================================================
519 | // GDI function wrappers
520 | // =============================================================================
521 |
522 | /// calls DeleteObject, panics on failure
523 | pub fn deleteObject(obj: win32.HGDIOBJ) void {
524 | if (0 == win32.DeleteObject(obj)) panicWin32("DeleteObject", win32.GetLastError());
525 | }
526 |
527 | /// calls DeleteDC, panics on failure
528 | pub fn deleteDc(hdc: win32.HDC) void {
529 | if (0 == win32.DeleteDC(hdc)) panicWin32("DeleteDC", win32.GetLastError());
530 | }
531 |
532 | /// calls InvalidateRect, panics on failure
533 | pub fn invalidateHwnd(hwnd: win32.HWND) void {
534 | if (0 == win32.InvalidateRect(hwnd, null, 0)) panicWin32("InvalidateRect", win32.GetLastError());
535 | }
536 |
537 | /// calls BeginPaint, panics on failure
538 | pub fn beginPaint(hwnd: win32.HWND) struct { win32.HDC, win32.PAINTSTRUCT } {
539 | var paintstruct: win32.PAINTSTRUCT = undefined;
540 | const hdc = win32.BeginPaint(hwnd, &paintstruct) orelse panicWin32(
541 | "BeginPaint",
542 | win32.GetLastError(),
543 | );
544 | return .{ hdc, paintstruct };
545 | }
546 | /// calls EndPaint, panics on failure
547 | pub fn endPaint(hwnd: win32.HWND, paintstruct: *const win32.PAINTSTRUCT) void {
548 | if (0 == win32.EndPaint(hwnd, paintstruct)) panicWin32(
549 | "EndPaint",
550 | win32.GetLastError(),
551 | );
552 | }
553 |
554 | /// calls CreateSolidBrush, panics on failure
555 | pub fn createSolidBrush(color: u32) win32.HBRUSH {
556 | return win32.CreateSolidBrush(color) orelse panicWin32(
557 | "CreateSolidBrush",
558 | win32.GetLastError(),
559 | );
560 | }
561 |
562 | /// calls FillRect, panics on failure
563 | pub fn fillRect(hdc: win32.HDC, rect: win32.RECT, brush: win32.HBRUSH) void {
564 | if (0 == win32.FillRect(hdc, &rect, brush)) panicWin32(
565 | "FillRect",
566 | win32.GetLastError(),
567 | );
568 | }
569 |
570 | /// calls TextOutA, panics on failure
571 | pub fn textOutA(hdc: win32.HDC, x: i32, y: i32, msg: []const u8) void {
572 | if (0 == win32.TextOutA(hdc, x, y, @ptrCast(msg.ptr), @intCast(msg.len))) panicWin32(
573 | "TextOut",
574 | win32.GetLastError(),
575 | );
576 | }
577 |
578 | /// calls TextOutW, panics on failure
579 | pub fn textOutW(hdc: win32.HDC, x: i32, y: i32, msg: []const u16) void {
580 | if (0 == win32.TextOutW(hdc, x, y, @ptrCast(msg.ptr), @intCast(msg.len))) panicWin32(
581 | "TextOut",
582 | win32.GetLastError(),
583 | );
584 | }
585 |
586 | /// calls GetTextExtentPoint32A, panics on failure
587 | pub fn getTextExtentA(hdc: win32.HDC, str: []const u8) win32.SIZE {
588 | var size: win32.SIZE = undefined;
589 | if (0 == win32.GetTextExtentPoint32A(hdc, @ptrCast(str.ptr), @intCast(str.len), &size)) panicWin32(
590 | "GetTextExtentPoint32A",
591 | win32.GetLastError(),
592 | );
593 | return size;
594 | }
595 |
596 | /// calls GetTextExtentPoint32W, panics on failure
597 | pub fn getTextExtentW(hdc: win32.HDC, str: []const u16) win32.SIZE {
598 | var size: win32.SIZE = undefined;
599 | if (0 == win32.GetTextExtentPoint32W(hdc, @ptrCast(str.ptr), @intCast(str.len), &size)) panicWin32(
600 | "GetTextExtentPoint32W",
601 | win32.GetLastError(),
602 | );
603 | return size;
604 | }
605 |
--------------------------------------------------------------------------------
/src/static/zig.mod:
--------------------------------------------------------------------------------
1 | id: o6ogpor87xc23o863qaqfciqqdnt48nlj0395dk1xt4m9b34
2 | name: win32
3 | main: win32.zig
4 | license: MIT
5 | description: "Zig bindings for Win32 generated by https://github.com/marlersoft/zigwin32gen"
6 | dependencies:
7 |
--------------------------------------------------------------------------------
/test/badcomoverload.zig:
--------------------------------------------------------------------------------
1 | const win32 = struct {
2 | usingnamespace @import("win32").graphics.direct2d;
3 | };
4 |
5 | pub fn main() void {
6 | var elem: *win32.ID2D1SvgElement = undefined;
7 | elem.GetAttributeValue(0, 0, 0);
8 | }
9 |
--------------------------------------------------------------------------------
/test/comoverload.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const WINAPI = std.os.windows.WINAPI;
3 | const win32 = struct {
4 | usingnamespace @import("win32").foundation;
5 | usingnamespace @import("win32").graphics.direct2d;
6 | usingnamespace @import("win32").zig;
7 | };
8 |
9 | fn GetAttributeValueString(
10 | self: *const win32.ID2D1SvgElement,
11 | name: ?[*:0]const u16,
12 | @"type": win32.D2D1_SVG_ATTRIBUTE_STRING_TYPE,
13 | value: [*:0]u16,
14 | valueCount: u32,
15 | ) callconv(WINAPI) win32.HRESULT {
16 | _ = self;
17 | _ = name;
18 | _ = @"type";
19 | _ = value;
20 | _ = valueCount;
21 | return 0;
22 | }
23 |
24 | pub fn main() void {
25 | var vtable: win32.ID2D1SvgElement.VTable = undefined;
26 | vtable.GetAttributeValueString = &GetAttributeValueString;
27 | var element_instance: win32.ID2D1SvgElement = .{
28 | .vtable = &vtable,
29 | };
30 | const element = &element_instance;
31 | var value_buf: [10:0]u16 = undefined;
32 | std.debug.assert(0 == element.GetAttributeValueString(
33 | win32.L("Hello"),
34 | .SVG,
35 | &value_buf,
36 | value_buf.len,
37 | ));
38 | }
39 |
--------------------------------------------------------------------------------
/update-merged-branch:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | #
3 | # Updates origin/merged with the latest set of commits from all release branches
4 | #
5 | import sys
6 | import os
7 | import subprocess
8 | import functools
9 |
10 | SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
11 | ZIGWIN32_DIR = os.path.join(SCRIPT_DIR, "zigwin32")
12 |
13 | def gitRun(git_args, **kwargs):
14 | if "allow_fail" in kwargs:
15 | allow_fail = kwargs["allow_fail"]
16 | del kwargs["allow_fail"]
17 | else:
18 | allow_fail = False
19 | args = ["git", "-C", ZIGWIN32_DIR] + git_args
20 | print("[RUN] {}".format(" ".join(args)))
21 | result = subprocess.run(args, **kwargs)
22 | capture_output = kwargs.get('capture_output', False)
23 | if capture_output:
24 | sys.stderr.buffer.write(result.stderr)
25 | if (not allow_fail) and result.returncode != 0:
26 | sys.exit(result.returncode)
27 | if capture_output:
28 | if allow_fail:
29 | return { "returncode": result.returncode, "stdout": result.stdout }
30 | return result.stdout
31 | if allow_fail:
32 | return result.returncode
33 |
34 | class Branch:
35 | def __init__(self, full):
36 | self.full = full
37 | name = os.path.basename(full)
38 | self.name = name
39 | preview_index = name.index("-preview")
40 | self.parts = name[:preview_index].split(".")
41 | suffix = name[preview_index+8:]
42 | if suffix:
43 | if not suffix.startswith("."):
44 | sys.exit(f"expected '.' after '-preview' but got '{name}'")
45 | self.suffix = int(suffix[1:])
46 | else:
47 | self.suffix = None
48 | def __repr__(self):
49 | return self.name
50 | def compare(self, right):
51 | i = 0
52 | while True:
53 | if i == len(self.parts):
54 | if i == len(right.parts):
55 | break;
56 | sys.exit("todo or error?")
57 | self_i = int(self.parts[i])
58 | right_i = int(right.parts[i])
59 | if self_i < right_i:
60 | return -1
61 | if self_i > right_i:
62 | return 1
63 | i += 1
64 |
65 | if self.suffix:
66 | if not right.suffix:
67 | sys.exit("todo or error?")
68 | return self.suffix - right.suffix
69 | else:
70 | if right.suffix:
71 | sys.exit("todo or error?")
72 | return 0
73 |
74 | def hasCommit(refspec, sha):
75 | result = gitRun(["merge-base", refspec, sha], capture_output=True, allow_fail=True)
76 | returncode = result["returncode"]
77 | merge_base = result["stdout"].decode("utf8").strip()
78 | if (returncode == 0) and (merge_base == sha):
79 | #if returncode == 0:
80 | return True
81 | elif (returncode != 0) and (len(merge_base) == 0):
82 | return False
83 | return False
84 | sys.exit(f"error: unexpected merge-base output returncode={returncode}, stdout='{merge_base}'")
85 |
86 | def getShas(refspec):
87 | return gitRun(["log", "--reverse", "--pretty=%H", refspec, "--"], capture_output=True).decode("utf8").strip().split()
88 |
89 | def findMissingSha(branches):
90 | for branch_index, branch in enumerate(branches):
91 | print(f"Checking if {branch} is in origin/merged")
92 | shas = getShas(branch.full)
93 | # shas are ordered oldest to newest
94 | for sha_index, sha in enumerate(shas):
95 | if not hasCommit("origin/merged", sha):
96 | return (branch_index, shas, sha_index)
97 | print(f" {sha} YES")
98 | return (None, None, None)
99 |
100 | def main():
101 | gitRun(["fetch"])
102 | gitRun(["reset", "--hard", "HEAD"])
103 | gitRun(["clean", "-xfd"])
104 |
105 | raw_branches = gitRun(["branch", "--remotes", "--format", "%(refname)"], capture_output=True).decode("utf8").split()
106 | branches = []
107 | for raw_branch in raw_branches:
108 | name = os.path.basename(raw_branch)
109 | if "-preview" in name:
110 | branches.append(Branch(raw_branch))
111 | else:
112 | print(f"Ignoring Branch {name}")
113 | branches.sort(key=functools.cmp_to_key(Branch.compare))
114 |
115 | (branch_index, shas, sha_index) = findMissingSha(branches)
116 | if branch_index == None:
117 | print("Success: origin/merged already contains all commits")
118 | return
119 |
120 | print("First missing Sha:")
121 | print(f" Branch '{branches[branch_index]}' ({branch_index+1} out of {len(branches)})")
122 | print(f" Sha {sha_index+1} out of {len(shas)}: {shas[sha_index]}")
123 |
124 | # detach in case we are on the merged branch we are about to delete
125 | gitRun(["checkout", "--detach"])
126 | gitRun(["branch", "-D", "merged"], allow_fail=True)
127 | gitRun(["checkout", "origin/merged", "-b", "merged"])
128 |
129 | for branch in branches[branch_index:]:
130 | print(f"Processing {branch}...")
131 | shas = getShas(branch.full)
132 | for sha_index, sha in enumerate(shas):
133 | if hasCommit("HEAD", sha):
134 | print(f" {sha} already merged")
135 | continue
136 | body = gitRun([
137 | "log",
138 | "--format=%B",
139 | "-n", "1",
140 | sha,
141 | ], capture_output=True).decode("utf8")
142 | gitRun([
143 | "merge",
144 | "-m", f"{branch} commit {sha_index+1}/{len(shas)}: {body}",
145 | "--allow-unrelated-histories", sha,
146 | "--strategy-option", "theirs",
147 | ])
148 |
149 | gitRun(["push", "origin", "merged", "-f"])
150 | print("Success: updated origin/merged with latest set of commits")
151 |
152 | main()
153 |
--------------------------------------------------------------------------------