Skip to content

Commit 1b4784f

Browse files
authored
Merge pull request #34 from MrScriptX/zig-linking-dear-imgui
Zig linking dear imgui
2 parents 05cf77d + eb634cc commit 1b4784f

File tree

2 files changed

+210
-0
lines changed

2 files changed

+210
-0
lines changed
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
---
2+
layout: post
3+
title: Zig - Linking Dear ImGui
4+
date: 2025-04-30 10:00:00 +0100
5+
banner: /assets/img/2025-04/c_programming_language_striked_by_lightning.png
6+
categories: devlog zig
7+
---
8+
9+
I have come to find that apparently, linking Dear ImGui isn't as straight forward as I thought.
10+
While working on my new rendering engine [Z8](https://github.com/MrScriptX/z8), I had to reimplement Dear ImGui.
11+
I have been using it in C++ for a while now, and I thought it would be easy to just link it in Zig.
12+
And well, it wasn't too hard really, but I had to figure out a few things along the way.
13+
So here are the sweet little tips I got from this experience.
14+
15+
## Building Dear ImGui
16+
17+
First of all, you need to build the Dear ImGui bindings for C.
18+
19+
Instead of using the `cimgui` bindings, we are going to use the official Dear ImGui C bindings: [dear bindings](https://github.com/dearimgui/dear_bindings).
20+
21+
First, clone imgui repository and the dear bindings repository.:
22+
23+
```bash
24+
git clone https://github.com/ocornut/imgui.git
25+
git clone https://github.com/dearimgui/dear_bindings.git
26+
```
27+
28+
Now we can generate the bindings by running the following command:
29+
30+
```bash
31+
$ cd dear_bindings
32+
$ python -m venv .venv
33+
$ source .venv/bin/activate
34+
$ pip install -r requirements.txt
35+
$ ./BuildAllBindings.sh
36+
$ deactivate
37+
```
38+
39+
For Windows users, you can use the `BuildAllBindings.bat` script instead.
40+
41+
```bat
42+
$ cd dear_bindings
43+
$ python -m venv .venv
44+
$ .venv\Scripts\activate.bat
45+
$ pip install -r requirements.txt
46+
$ BuildAllBindings.bat
47+
$ deactivate
48+
```
49+
50+
This will generate the bindings in the `generated` folder.
51+
52+
## Linking Dear ImGui
53+
54+
Now that we have the bindings, we can link them in our Zig project.
55+
Copy the `generated` folder to your Zig project folder.
56+
57+
I personally structure my project like this:
58+
59+
- `project_root`
60+
- `libs`
61+
- `cimgui` (Dear ImGui module for Zig)
62+
- `src`
63+
- `root.zig` (the root file of the module)
64+
- `build.zig` (the build file of the module)
65+
- `common`
66+
- `imgui` (the Dear ImGui source files)
67+
- `src`
68+
- `main.zig` (the main file of the project)
69+
70+
As you can see, I don't directly import the Dear ImGui source files, but I create a module for it.
71+
Then I import the module in my main file.
72+
73+
You can reproduce this structure or use another one, but just make sure the paths are correct,
74+
and that you possess at least `root.zig` and `build.zig` files for Dear ImGui module.
75+
76+
In the new `build.zig`, we will add the following code:
77+
78+
```zig
79+
const std = @import("std");
80+
const Build = std.Build;
81+
82+
pub fn build(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *Build.Module {
83+
// we create a new module for the imgui library
84+
const module = b.addModule("imgui", .{
85+
.root_source_file = b.path("libs/cimgui/src/root.zig"), // this is the root file of the module
86+
.target = target,
87+
.optimize = optimize,
88+
});
89+
90+
module.addIncludePath(.{ .cwd_relative = "common/imgui" });
91+
module.addCSourceFile(.{ .file = b.path("common/imgui/imgui.cpp"), .flags = &.{ "" } });
92+
module.addCSourceFile(.{ .file = b.path("common/imgui/imgui_widgets.cpp"), .flags = &.{ "" } });
93+
module.addCSourceFile(.{ .file = b.path("common/imgui/imgui_tables.cpp"), .flags = &.{ "" } });
94+
module.addCSourceFile(.{ .file = b.path("common/imgui/imgui_draw.cpp"), .flags = &.{ "" } });
95+
module.addCSourceFile(.{ .file = b.path("common/imgui/imgui_demo.cpp"), .flags = &.{ "" } });
96+
module.addCSourceFile(.{ .file = b.path("common/imgui/dcimgui.cpp"), .flags = &.{ "" } });
97+
module.addCSourceFile(.{ .file = b.path("common/imgui/dcimgui_internal.cpp"), .flags = &.{ "" } });
98+
99+
return module; // return the module so we can use it in our main file
100+
}
101+
```
102+
103+
For the backends, you have to link their respective library too and add the include paths.
104+
For example, if you are using SDL3 and Vulkan, you can add the following :
105+
106+
```zig
107+
// Add SDL3 and Vulkan backends
108+
module.addCSourceFile(.{ .file = b.path("common/imgui/imgui_impl_sdl3.cpp"), .flags = &.{ "" } });
109+
module.addCSourceFile(.{ .file = b.path("common/imgui/imgui_impl_vulkan.cpp"), .flags = &.{ "" } });
110+
111+
// add the c bindings for SDL3 and Vulkan
112+
module.addCSourceFile(.{ .file = b.path("common/imgui/dcimgui_impl_sdl3.cpp"), .flags = &.{ "" } });
113+
module.addCSourceFile(.{ .file = b.path("common/imgui/dcimgui_impl_vulkan.cpp"), .flags = &.{ "" } });
114+
115+
// Add SDL3 include path
116+
module.addIncludePath(.{ .cwd_relative = "common/SDL3/include" });
117+
118+
// Add Vulkan include path
119+
const env_map = std.process.getEnvMap(b.allocator) catch @panic("Out of memory !");
120+
if (env_map.get("VK_SDK_PATH")) |path| { // find VK_SDK_PATH in the environment variables
121+
module.addIncludePath(.{ .cwd_relative = std.fmt.allocPrint(b.allocator, "{s}/include", .{path}) catch @panic("OOM") });
122+
}
123+
else {
124+
@panic("VK_SDK_PATH not found ! Please install Vulkan SDK.");
125+
}
126+
```
127+
128+
Ok, now that we have a build function to create a module for imgui, we can go to the `root.zig` file.
129+
In this file, we are going to export the functions we need from the imgui library.
130+
131+
```zig
132+
pub usingnamespace @cImport({
133+
@cInclude("dcimgui.h");
134+
@cInclude("dcimgui_impl_sdl3.h");
135+
@cInclude("dcimgui_impl_vulkan.h");
136+
// ...add other includes here if needed
137+
});
138+
```
139+
140+
We can even export wrapper functions for the Dear ImGui functions we need.
141+
```zig
142+
pub fn SliderInt(label: []const u8, v: *i32, v_min: i32, v_max: i32) bool {
143+
return c.ImGui_SliderInt(@ptrCast(label), v, v_min, v_max);
144+
}
145+
146+
// import the C functions again but under the c namespace to use them in Zig
147+
// if you expose all the functions you need, you can even drop the previous import.
148+
const c = @cImport({
149+
@cInclude("dcimgui.h");
150+
@cInclude("dcimgui_impl_sdl3.h");
151+
@cInclude("dcimgui_impl_vulkan.h");
152+
});
153+
```
154+
155+
Back to our main `build.zig` file, we can now add the imgui module to our build.
156+
```zig
157+
// you should already have this at the top of your file
158+
const target = b.standardTargetOptions(.{});
159+
const optimize = b.standardOptimizeOption(.{});
160+
161+
const exe = b.addExecutable(.{
162+
.name = "project",
163+
.root_source_file = b.path("src/main.zig"),
164+
.target = target,
165+
.optimize = optimize,
166+
.link_libc = true,
167+
});
168+
169+
// add the imgui module to the executable
170+
const cimgui = @import("libs/cimgui/build.zig").build(b, target, optimize);
171+
exe.root_module.addImport("imgui", cimgui); // this is so we can import our module
172+
173+
// link std cpp library
174+
exe.linkLibCpp();
175+
```
176+
177+
## Using our brand new module
178+
179+
Now that we have our module, we can use it in our main file.
180+
181+
```zig
182+
const std = @import("std");
183+
const imgui = @import("imgui");
184+
185+
pub fn main() void {
186+
// use c functions from imgui
187+
const context = imgui.ImGui_CreateContext(null);
188+
if (context == null) {
189+
@panic("Failed to create ImGui context!");
190+
}
191+
defer imgui.ImGui_DestroyContext(context);
192+
193+
// ... other code
194+
195+
// or use the wrapper function we created
196+
var value: i32 = 0;
197+
if (imgui.SliderInt("My Slider", &value, 0, 100)) {
198+
std.debug.print("Slider value: {}\n", .{value});
199+
}
200+
}
201+
```
202+
203+
## Conclusion
204+
205+
That's really all there is to it.
206+
If you have any questions, feel free to ask in the comments or anywhere I post ([YouTube](https://www.youtube.com/@R3DC0DE), Reddit, etc...).
207+
208+
Hopefully this guide did help you out a bit.
209+
210+
Be blessed and happy coding !
1.42 MB
Loading

0 commit comments

Comments
 (0)