Initial commit
This commit is contained in:
commit
48a86027ba
16 changed files with 80801 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
.zig-cache
|
||||||
|
zig-out
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) contributors
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
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.
|
||||||
55
README.md
Normal file
55
README.md
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
# Dear ImGui Zig
|
||||||
|
|
||||||
|
A Zig interface for [Dear ImGui](https://github.com/ocornut/imgui).
|
||||||
|
|
||||||
|
Generated at build time from the JSON generated by [dear_bindings](https://github.com/dearimgui/dear_bindings).
|
||||||
|
|
||||||
|
## Which version of Dear ImGui is built?
|
||||||
|
|
||||||
|
See `build.zig.zon`, at the time of writing it's a recent commit on the docking branch. See Dear ImGui's readme for info on Dear ImGui branches.
|
||||||
|
|
||||||
|
## How to use Dear ImGui
|
||||||
|
|
||||||
|
Import the `dear_imgui` module provided by this package. It exports Dear ImGui types and functions, converted to standard Zig syntax. You can always check the generated file if you are unsure how the names map, but it should be fairly straightforward.
|
||||||
|
|
||||||
|
## How to build Dear ImGui extensions
|
||||||
|
|
||||||
|
The static library for Dear ImGui, and supported backends, are provided as artifacts under the same name as the Zig modules. You can link Dear Imgui extensions to these.
|
||||||
|
|
||||||
|
## How to use the backends
|
||||||
|
|
||||||
|
Right now, this package only exports the Vulkan backend: `dear_imgui_vulkan`.
|
||||||
|
|
||||||
|
Contributions are welcome if you'd like to add others. The process should be fairly straightforward.
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
`dear_bindings` generates a C wrapper for Dear ImGui, and a JSON file detailing the declarations.
|
||||||
|
|
||||||
|
The results of executing `dear_bindings` are cached in `src/cached` to avoid a Python dependency
|
||||||
|
(`dear_bindings` is implemented in Python.)
|
||||||
|
|
||||||
|
At build time, `generate.zig` is called on the relevant JSON files to generate a Zig module that links to the wrapper generated by `dear_bindings`.
|
||||||
|
|
||||||
|
`dear_bindings` does not provide enough data to generate perfect pointer types for all situations, but a reasonable conservative heuristic is used to avoid `[*c]` where possible. This could be improved in the future through whitelists.
|
||||||
|
|
||||||
|
## How to change versions
|
||||||
|
|
||||||
|
Update `build.zig.zon`, and then if the headers changed, regenerate them into `src/cached` with [dear_bindings](https://github.com/dearimgui/dear_bindings).
|
||||||
|
|
||||||
|
Building `cimgui.h` is explained in their README, here'd how to build a backend. You can also see the command format in their `bindings.bat` file.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 dear_bindings.py --backend --imconfig-path ~/Documents/imgui/imconfig.h -o cimgui_internal ~/Documents/imgui/imgui_internal.h
|
||||||
|
```
|
||||||
|
|
||||||
|
If `generator.zig` fails, you may need to add missing values to the enumeration. `std.json` does not provide helpful errors at the time of writing, the easiest way to debug this is to comment out the actual generation code, and all fields in `Header`, adding them back until you figure out which are causing the issue.
|
||||||
|
|
||||||
|
## Known differences
|
||||||
|
* doesn't build `binary_to_compressed_c`
|
||||||
|
* doesn't build `misc/cpp/imgui_stdlib.cpp`
|
||||||
|
* freetype mode not supported (but easily could be)
|
||||||
|
* only the vulkan backend is provided as of now (but others are easy to add)
|
||||||
|
* the Vulkan backend assumes `IMGUI_IMPL_VULKAN_NO_PROTOTYPES`
|
||||||
|
* the Vulkan backend assumes dynamic rendering is available (>= `VK_VERSION_1_3` or `VK_KHR_dynamic_rendering`)
|
||||||
|
* we could add support for this conditional, but it seems unlikely to get much use
|
||||||
109
build.zig
Normal file
109
build.zig
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const flags: []const []const u8 = &.{
|
||||||
|
"-fno-exceptions",
|
||||||
|
"-fno-rtti",
|
||||||
|
"-fno-threadsafe-statics",
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
// Standard options
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const optimize_external = switch (optimize) {
|
||||||
|
.Debug => .ReleaseSafe,
|
||||||
|
else => optimize,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the upstream code
|
||||||
|
const upstream = b.dependency("dear-imgui", .{});
|
||||||
|
|
||||||
|
// Compile Dear ImGui as a static library
|
||||||
|
const dear_imgui_lib = b.addStaticLibrary(.{
|
||||||
|
.name = "dear_imgui",
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize_external,
|
||||||
|
});
|
||||||
|
dear_imgui_lib.addIncludePath(upstream.path(""));
|
||||||
|
dear_imgui_lib.installHeadersDirectory(upstream.path(""), "", .{});
|
||||||
|
dear_imgui_lib.linkLibC();
|
||||||
|
dear_imgui_lib.addCSourceFiles(.{
|
||||||
|
.root = upstream.path(""),
|
||||||
|
.files = &.{
|
||||||
|
"imgui.cpp",
|
||||||
|
"imgui_demo.cpp",
|
||||||
|
"imgui_draw.cpp",
|
||||||
|
"imgui_tables.cpp",
|
||||||
|
"imgui_widgets.cpp",
|
||||||
|
},
|
||||||
|
.flags = flags,
|
||||||
|
});
|
||||||
|
dear_imgui_lib.addCSourceFiles(.{
|
||||||
|
.root = b.path("src/cached"),
|
||||||
|
.files = &.{"cimgui.cpp"},
|
||||||
|
.flags = flags,
|
||||||
|
});
|
||||||
|
b.installArtifact(dear_imgui_lib);
|
||||||
|
|
||||||
|
// Compile the Vulkan backend as a static library
|
||||||
|
const dear_imgui_vulkan_lib = b.addStaticLibrary(.{
|
||||||
|
.name = "dear_imgui_vulkan",
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize_external,
|
||||||
|
});
|
||||||
|
dear_imgui_vulkan_lib.linkLibrary(dear_imgui_lib);
|
||||||
|
dear_imgui_vulkan_lib.addCSourceFile(.{ .file = upstream.path("backends/imgui_impl_vulkan.cpp"), .flags = flags });
|
||||||
|
dear_imgui_vulkan_lib.addCSourceFile(.{ .file = b.path("src/cached/cimgui_impl_vulkan.cpp"), .flags = flags });
|
||||||
|
dear_imgui_vulkan_lib.addIncludePath(upstream.path(""));
|
||||||
|
dear_imgui_vulkan_lib.addIncludePath(upstream.path("backends"));
|
||||||
|
const vulkan_headers = b.dependency("Vulkan-Headers", .{});
|
||||||
|
dear_imgui_vulkan_lib.addIncludePath(vulkan_headers.path("include"));
|
||||||
|
dear_imgui_vulkan_lib.defineCMacro("IMGUI_IMPL_VULKAN_NO_PROTOTYPES", "1"); // Assumed in generator
|
||||||
|
dear_imgui_vulkan_lib.installHeadersDirectory(upstream.path("backends"), "", .{});
|
||||||
|
dear_imgui_vulkan_lib.installHeadersDirectory(vulkan_headers.path("include"), "", .{});
|
||||||
|
b.installArtifact(dear_imgui_vulkan_lib);
|
||||||
|
|
||||||
|
// Compile the generator
|
||||||
|
const generate_exe = b.addExecutable(.{
|
||||||
|
.name = "generate",
|
||||||
|
.root_source_file = b.path("src/generate.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const generate_cmd = b.addRunArtifact(generate_exe);
|
||||||
|
generate_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
if (b.args) |args| {
|
||||||
|
generate_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const generate_step = b.step("generate", "Generate Zig bindings for Dear ImGui. This is done automatically as part of the build process, but is exposed as an option for debugging purposes.");
|
||||||
|
generate_step.dependOn(&generate_cmd.step);
|
||||||
|
|
||||||
|
// Generate Zig bindings for Dear ImGui
|
||||||
|
const generate_dear_imgui = b.addRunArtifact(generate_exe);
|
||||||
|
generate_dear_imgui.addFileArg(b.path("src/cached/cimgui.json"));
|
||||||
|
const dear_imgui_zig = generate_dear_imgui.addOutputFileArg("dear_imgui.zig");
|
||||||
|
generate_dear_imgui.addFileArg(b.path("src/templates/cimgui_prefix.zig.template"));
|
||||||
|
generate_dear_imgui.addFileArg(b.path("src/templates/cimgui_postfix.zig.template"));
|
||||||
|
const dear_imgui_zig_module = b.addModule("dear_imgui", .{
|
||||||
|
.root_source_file = dear_imgui_zig,
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
dear_imgui_zig_module.linkLibrary(dear_imgui_lib);
|
||||||
|
|
||||||
|
// Generate Zig bindings for the Vulkan backend
|
||||||
|
const generate_vulkan = b.addRunArtifact(generate_exe);
|
||||||
|
generate_vulkan.addFileArg(b.path("src/cached/cimgui_impl_vulkan.json"));
|
||||||
|
const dear_imgui_vulkan_zig = generate_vulkan.addOutputFileArg("dear_imgui_impl_vulkan.zig");
|
||||||
|
generate_vulkan.addFileArg(b.path("src/templates/impl_vulkan_prefix.zig.template"));
|
||||||
|
generate_vulkan.addFileArg(b.path("src/templates/impl_vulkan_postfix.zig.template"));
|
||||||
|
const dear_imgui_vulkan_zig_module = b.addModule("dear_imgui_vulkan", .{
|
||||||
|
.root_source_file = dear_imgui_vulkan_zig,
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
dear_imgui_vulkan_zig_module.linkLibrary(dear_imgui_vulkan_lib);
|
||||||
|
}
|
||||||
22
build.zig.zon
Normal file
22
build.zig.zon
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
.{
|
||||||
|
.name = "dear_imgui_zig",
|
||||||
|
.version = "0.0.0",
|
||||||
|
.dependencies = .{
|
||||||
|
.@"dear-imgui" = .{
|
||||||
|
.url = "https://github.com/ocornut/imgui/archive/refs/tags/v1.90.9-docking.tar.gz",
|
||||||
|
.hash = "1220854ebf6bca15874cac634f0c9e39145cc0d13c1cf74822b6ed3073e906a4f8d5",
|
||||||
|
},
|
||||||
|
.@"Vulkan-Headers" = .{
|
||||||
|
.url = "https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/v1.3.290.tar.gz",
|
||||||
|
.hash = "12200beeb3d546548d62a5ad1ac4153e28f78bdeb3617a07aa7a8e584b51cba2daa1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
.paths = .{
|
||||||
|
"build.zig",
|
||||||
|
"build.zig.zon",
|
||||||
|
"src",
|
||||||
|
"LICENSE",
|
||||||
|
"README.md",
|
||||||
|
},
|
||||||
|
}
|
||||||
3714
src/cached/cimgui.cpp
Normal file
3714
src/cached/cimgui.cpp
Normal file
File diff suppressed because it is too large
Load diff
3861
src/cached/cimgui.h
Normal file
3861
src/cached/cimgui.h
Normal file
File diff suppressed because it is too large
Load diff
69600
src/cached/cimgui.json
Normal file
69600
src/cached/cimgui.json
Normal file
File diff suppressed because it is too large
Load diff
107
src/cached/cimgui_impl_vulkan.cpp
Normal file
107
src/cached/cimgui_impl_vulkan.cpp
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
// THIS FILE HAS BEEN AUTO-GENERATED BY THE 'DEAR BINDINGS' GENERATOR.
|
||||||
|
// **DO NOT EDIT DIRECTLY**
|
||||||
|
// https://github.com/dearimgui/dear_bindings
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_impl_vulkan.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
// Wrap this in a namespace to keep it separate from the C++ API
|
||||||
|
namespace cimgui
|
||||||
|
{
|
||||||
|
#include "cimgui_impl_vulkan.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
// By-value struct conversions
|
||||||
|
|
||||||
|
// Function stubs
|
||||||
|
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
|
||||||
|
CIMGUI_IMPL_API bool cimgui::cImGui_ImplVulkan_Init(cimgui::ImGui_ImplVulkan_InitInfo* info)
|
||||||
|
{
|
||||||
|
return ::ImGui_ImplVulkan_Init(reinterpret_cast<::ImGui_ImplVulkan_InitInfo*>(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkan_Shutdown(void)
|
||||||
|
{
|
||||||
|
::ImGui_ImplVulkan_Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkan_NewFrame(void)
|
||||||
|
{
|
||||||
|
::ImGui_ImplVulkan_NewFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkan_RenderDrawData(cimgui::ImDrawData* draw_data, VkCommandBuffer command_buffer)
|
||||||
|
{
|
||||||
|
::ImGui_ImplVulkan_RenderDrawData(reinterpret_cast<::ImDrawData*>(draw_data), command_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkan_RenderDrawDataEx(cimgui::ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline)
|
||||||
|
{
|
||||||
|
::ImGui_ImplVulkan_RenderDrawData(reinterpret_cast<::ImDrawData*>(draw_data), command_buffer, pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
CIMGUI_IMPL_API bool cimgui::cImGui_ImplVulkan_CreateFontsTexture(void)
|
||||||
|
{
|
||||||
|
return ::ImGui_ImplVulkan_CreateFontsTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkan_DestroyFontsTexture(void)
|
||||||
|
{
|
||||||
|
::ImGui_ImplVulkan_DestroyFontsTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count)
|
||||||
|
{
|
||||||
|
::ImGui_ImplVulkan_SetMinImageCount(min_image_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
CIMGUI_IMPL_API VkDescriptorSet cimgui::cImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout)
|
||||||
|
{
|
||||||
|
return ::ImGui_ImplVulkan_AddTexture(sampler, image_view, image_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set)
|
||||||
|
{
|
||||||
|
::ImGui_ImplVulkan_RemoveTexture(descriptor_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
CIMGUI_IMPL_API bool cimgui::cImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction (*loader_func)(const char* function_name, void* user_data))
|
||||||
|
{
|
||||||
|
return ::ImGui_ImplVulkan_LoadFunctions(loader_func);
|
||||||
|
}
|
||||||
|
|
||||||
|
CIMGUI_IMPL_API bool cimgui::cImGui_ImplVulkan_LoadFunctionsEx(PFN_vkVoidFunction (*loader_func)(const char* function_name, void* user_data), void* user_data)
|
||||||
|
{
|
||||||
|
return ::ImGui_ImplVulkan_LoadFunctions(loader_func, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, cimgui::ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count)
|
||||||
|
{
|
||||||
|
::ImGui_ImplVulkanH_CreateOrResizeWindow(instance, physical_device, device, reinterpret_cast<::ImGui_ImplVulkanH_Window*>(wd), queue_family, allocator, w, h, min_image_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, cimgui::ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator)
|
||||||
|
{
|
||||||
|
::ImGui_ImplVulkanH_DestroyWindow(instance, device, reinterpret_cast<::ImGui_ImplVulkanH_Window*>(wd), allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
CIMGUI_IMPL_API VkSurfaceFormatKHR cimgui::cImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space)
|
||||||
|
{
|
||||||
|
return ::ImGui_ImplVulkanH_SelectSurfaceFormat(physical_device, surface, request_formats, request_formats_count, request_color_space);
|
||||||
|
}
|
||||||
|
|
||||||
|
CIMGUI_IMPL_API VkPresentModeKHR cimgui::cImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count)
|
||||||
|
{
|
||||||
|
return ::ImGui_ImplVulkanH_SelectPresentMode(physical_device, surface, request_modes, request_modes_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
CIMGUI_IMPL_API int cimgui::cImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode)
|
||||||
|
{
|
||||||
|
return ::ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(present_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
||||||
198
src/cached/cimgui_impl_vulkan.h
Normal file
198
src/cached/cimgui_impl_vulkan.h
Normal file
|
|
@ -0,0 +1,198 @@
|
||||||
|
// THIS FILE HAS BEEN AUTO-GENERATED BY THE 'DEAR BINDINGS' GENERATOR.
|
||||||
|
// **DO NOT EDIT DIRECTLY**
|
||||||
|
// https://github.com/dearimgui/dear_bindings
|
||||||
|
|
||||||
|
// dear imgui: Renderer Backend for Vulkan
|
||||||
|
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [x] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions.
|
||||||
|
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||||
|
// [x] Renderer: Multi-viewport / platform windows. With issues (flickering when creating a new viewport).
|
||||||
|
|
||||||
|
// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'.
|
||||||
|
// See imgui_impl_vulkan.cpp file for details.
|
||||||
|
|
||||||
|
// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification.
|
||||||
|
// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app.
|
||||||
|
// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h.
|
||||||
|
// You will use those if you want to use this rendering backend in your engine/app.
|
||||||
|
// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by
|
||||||
|
// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code.
|
||||||
|
// Read comments in imgui_impl_vulkan.h.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
#include "cimgui.h"
|
||||||
|
// [Configuration] in order to use a custom Vulkan function loader:
|
||||||
|
// (1) You'll need to disable default Vulkan function prototypes.
|
||||||
|
// We provide a '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' convenience configuration flag.
|
||||||
|
// In order to make sure this is visible from the imgui_impl_vulkan.cpp compilation unit:
|
||||||
|
// - Add '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' in your imconfig.h file
|
||||||
|
// - Or as a compilation flag in your build system
|
||||||
|
// - Or uncomment here (not recommended because you'd be modifying imgui sources!)
|
||||||
|
// - Do not simply add it in a .cpp file!
|
||||||
|
// (2) Call ImGui_ImplVulkan_LoadFunctions() before ImGui_ImplVulkan_Init() with your custom function.
|
||||||
|
// If you have no idea what this is, leave it alone!
|
||||||
|
//#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES
|
||||||
|
|
||||||
|
// Convenience support for Volk
|
||||||
|
// (you can also technically use IMGUI_IMPL_VULKAN_NO_PROTOTYPES + wrap Volk via ImGui_ImplVulkan_LoadFunctions().)
|
||||||
|
//#define IMGUI_IMPL_VULKAN_USE_VOLK
|
||||||
|
|
||||||
|
#if defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES)&&!defined(VK_NO_PROTOTYPES)
|
||||||
|
#define VK_NO_PROTOTYPES
|
||||||
|
#endif // #if defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES)&&!defined(VK_NO_PROTOTYPES)
|
||||||
|
#if defined(VK_USE_PLATFORM_WIN32_KHR)&&!defined(NOMINMAX)
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif // #if defined(VK_USE_PLATFORM_WIN32_KHR)&&!defined(NOMINMAX)
|
||||||
|
// Vulkan includes
|
||||||
|
#ifdef IMGUI_IMPL_VULKAN_USE_VOLK
|
||||||
|
#include <volk.h>
|
||||||
|
#else
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#endif // #ifdef IMGUI_IMPL_VULKAN_USE_VOLK
|
||||||
|
#if defined(VK_VERSION_1_3)|| defined(VK_KHR_dynamic_rendering)
|
||||||
|
#define IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
|
||||||
|
#endif // #if defined(VK_VERSION_1_3)|| defined(VK_KHR_dynamic_rendering)
|
||||||
|
// Initialization data, for ImGui_ImplVulkan_Init()
|
||||||
|
// - VkDescriptorPool should be created with VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
|
||||||
|
// and must contain a pool size large enough to hold an ImGui VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER descriptor.
|
||||||
|
// - When using dynamic rendering, set UseDynamicRendering=true and fill PipelineRenderingCreateInfo structure.
|
||||||
|
// [Please zero-clear before use!]
|
||||||
|
typedef struct ImGui_ImplVulkan_InitInfo_t
|
||||||
|
{
|
||||||
|
VkInstance Instance;
|
||||||
|
VkPhysicalDevice PhysicalDevice;
|
||||||
|
VkDevice Device;
|
||||||
|
uint32_t QueueFamily;
|
||||||
|
VkQueue Queue;
|
||||||
|
VkDescriptorPool DescriptorPool; // See requirements in note above
|
||||||
|
VkRenderPass RenderPass; // Ignored if using dynamic rendering
|
||||||
|
uint32_t MinImageCount; // >= 2
|
||||||
|
uint32_t ImageCount; // >= MinImageCount
|
||||||
|
VkSampleCountFlagBits MSAASamples; // 0 defaults to VK_SAMPLE_COUNT_1_BIT
|
||||||
|
|
||||||
|
// (Optional)
|
||||||
|
VkPipelineCache PipelineCache;
|
||||||
|
uint32_t Subpass;
|
||||||
|
|
||||||
|
// (Optional) Dynamic Rendering
|
||||||
|
// Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3.
|
||||||
|
bool UseDynamicRendering;
|
||||||
|
#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
|
||||||
|
VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo;
|
||||||
|
#endif // #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
|
||||||
|
// (Optional) Allocation, Debugging
|
||||||
|
const VkAllocationCallbacks* Allocator;
|
||||||
|
void (*CheckVkResultFn)(VkResult err);
|
||||||
|
VkDeviceSize MinAllocationSize; // Minimum allocation size. Set to 1024*1024 to satisfy zealous best practices validation layer and waste a little memory.
|
||||||
|
} ImGui_ImplVulkan_InitInfo;
|
||||||
|
|
||||||
|
typedef struct ImDrawData_t ImDrawData;
|
||||||
|
// Called by user code
|
||||||
|
CIMGUI_IMPL_API bool cImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info);
|
||||||
|
CIMGUI_IMPL_API void cImGui_ImplVulkan_Shutdown(void);
|
||||||
|
CIMGUI_IMPL_API void cImGui_ImplVulkan_NewFrame(void);
|
||||||
|
CIMGUI_IMPL_API void cImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer); // Implied pipeline = VK_NULL_HANDLE
|
||||||
|
CIMGUI_IMPL_API void cImGui_ImplVulkan_RenderDrawDataEx(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline /* = VK_NULL_HANDLE */);
|
||||||
|
CIMGUI_IMPL_API bool cImGui_ImplVulkan_CreateFontsTexture(void);
|
||||||
|
CIMGUI_IMPL_API void cImGui_ImplVulkan_DestroyFontsTexture(void);
|
||||||
|
CIMGUI_IMPL_API void cImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated)
|
||||||
|
|
||||||
|
// Register a texture (VkDescriptorSet == ImTextureID)
|
||||||
|
// FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem
|
||||||
|
// Please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions.
|
||||||
|
CIMGUI_IMPL_API VkDescriptorSet cImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout);
|
||||||
|
CIMGUI_IMPL_API void cImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set);
|
||||||
|
|
||||||
|
// Optional: load Vulkan functions with a custom function loader
|
||||||
|
// This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES
|
||||||
|
CIMGUI_IMPL_API bool cImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction (*loader_func)(const char* function_name, void* user_data)); // Implied user_data = nullptr
|
||||||
|
CIMGUI_IMPL_API bool cImGui_ImplVulkan_LoadFunctionsEx(PFN_vkVoidFunction (*loader_func)(const char* function_name, void* user_data), void* user_data /* = nullptr */);
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// Internal / Miscellaneous Vulkan Helpers
|
||||||
|
// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app.)
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// You probably do NOT need to use or care about those functions.
|
||||||
|
// Those functions only exist because:
|
||||||
|
// 1) they facilitate the readability and maintenance of the multiple main.cpp examples files.
|
||||||
|
// 2) the multi-viewport / platform window implementation needs them internally.
|
||||||
|
// Generally we avoid exposing any kind of superfluous high-level helpers in the bindings,
|
||||||
|
// but it is too much code to duplicate everywhere so we exceptionally expose them.
|
||||||
|
//
|
||||||
|
// Your engine/app will likely _already_ have code to setup all that stuff (swap chain, render pass, frame buffers, etc.).
|
||||||
|
// You may read this code to learn about Vulkan, but it is recommended you use you own custom tailored code to do equivalent work.
|
||||||
|
// (The ImGui_ImplVulkanH_XXX functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions)
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
typedef struct ImGui_ImplVulkanH_Frame_t ImGui_ImplVulkanH_Frame;
|
||||||
|
typedef struct ImGui_ImplVulkanH_Window_t ImGui_ImplVulkanH_Window;
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
CIMGUI_IMPL_API void cImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count);
|
||||||
|
CIMGUI_IMPL_API void cImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator);
|
||||||
|
CIMGUI_IMPL_API VkSurfaceFormatKHR cImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space);
|
||||||
|
CIMGUI_IMPL_API VkPresentModeKHR cImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count);
|
||||||
|
CIMGUI_IMPL_API int cImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode);
|
||||||
|
|
||||||
|
// Helper structure to hold the data needed by one rendering frame
|
||||||
|
// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.)
|
||||||
|
// [Please zero-clear before use!]
|
||||||
|
typedef struct ImGui_ImplVulkanH_Frame_t
|
||||||
|
{
|
||||||
|
VkCommandPool CommandPool;
|
||||||
|
VkCommandBuffer CommandBuffer;
|
||||||
|
VkFence Fence;
|
||||||
|
VkImage Backbuffer;
|
||||||
|
VkImageView BackbufferView;
|
||||||
|
VkFramebuffer Framebuffer;
|
||||||
|
} ImGui_ImplVulkanH_Frame;
|
||||||
|
|
||||||
|
typedef struct ImGui_ImplVulkanH_FrameSemaphores_t
|
||||||
|
{
|
||||||
|
VkSemaphore ImageAcquiredSemaphore;
|
||||||
|
VkSemaphore RenderCompleteSemaphore;
|
||||||
|
} ImGui_ImplVulkanH_FrameSemaphores;
|
||||||
|
|
||||||
|
// Helper structure to hold the data needed by one rendering context into one OS window
|
||||||
|
// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.)
|
||||||
|
typedef struct ImGui_ImplVulkanH_Window_t
|
||||||
|
{
|
||||||
|
int Width;
|
||||||
|
int Height;
|
||||||
|
VkSwapchainKHR Swapchain;
|
||||||
|
VkSurfaceKHR Surface;
|
||||||
|
VkSurfaceFormatKHR SurfaceFormat;
|
||||||
|
VkPresentModeKHR PresentMode;
|
||||||
|
VkRenderPass RenderPass;
|
||||||
|
bool UseDynamicRendering;
|
||||||
|
bool ClearEnable;
|
||||||
|
VkClearValue ClearValue;
|
||||||
|
uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount)
|
||||||
|
uint32_t ImageCount; // Number of simultaneous in-flight frames (returned by vkGetSwapchainImagesKHR, usually derived from min_image_count)
|
||||||
|
uint32_t SemaphoreCount; // Number of simultaneous in-flight frames + 1, to be able to use it in vkAcquireNextImageKHR
|
||||||
|
uint32_t SemaphoreIndex; // Current set of swapchain wait semaphores we're using (needs to be distinct from per frame data)
|
||||||
|
ImGui_ImplVulkanH_Frame* Frames;
|
||||||
|
ImGui_ImplVulkanH_FrameSemaphores* FrameSemaphores;
|
||||||
|
} ImGui_ImplVulkanH_Window;
|
||||||
|
#endif// #ifndef IMGUI_DISABLE
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // End of extern "C" block
|
||||||
|
#endif
|
||||||
2063
src/cached/cimgui_impl_vulkan.json
Normal file
2063
src/cached/cimgui_impl_vulkan.json
Normal file
File diff suppressed because it is too large
Load diff
971
src/generate.zig
Normal file
971
src/generate.zig
Normal file
|
|
@ -0,0 +1,971 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const Declarations = std.StringArrayHashMap(struct { is_opaque: bool });
|
||||||
|
const max_size = 5000000;
|
||||||
|
|
||||||
|
// The header type we'll parse from JSON. Fields are only included as needed.
|
||||||
|
// practice, etc.
|
||||||
|
const Header = struct {
|
||||||
|
defines: []Define,
|
||||||
|
typedefs: []Typedef,
|
||||||
|
enums: []Enum,
|
||||||
|
structs: []const Struct,
|
||||||
|
functions: []Function,
|
||||||
|
|
||||||
|
const Define = struct {
|
||||||
|
name: []const u8,
|
||||||
|
content: ?[]const u8 = null,
|
||||||
|
is_internal: bool,
|
||||||
|
conditionals: []const Conditional = &.{},
|
||||||
|
};
|
||||||
|
|
||||||
|
const Typedef = struct {
|
||||||
|
name: []const u8,
|
||||||
|
type: Type,
|
||||||
|
conditionals: []const Conditional = &.{},
|
||||||
|
};
|
||||||
|
|
||||||
|
const Enum = struct {
|
||||||
|
name: []const u8,
|
||||||
|
storage_type: StorageType = .{ .declaration = .int },
|
||||||
|
is_flags_enum: bool,
|
||||||
|
elements: []const Element,
|
||||||
|
is_internal: bool,
|
||||||
|
conditionals: []const Conditional = &.{},
|
||||||
|
|
||||||
|
const Element = struct {
|
||||||
|
name: []const u8,
|
||||||
|
value: i64,
|
||||||
|
is_count: bool,
|
||||||
|
is_internal: bool,
|
||||||
|
conditionals: []const Conditional = &.{},
|
||||||
|
};
|
||||||
|
|
||||||
|
const StorageType = struct {
|
||||||
|
declaration: enum { int, ImU8 },
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const Struct = struct {
|
||||||
|
name: []const u8,
|
||||||
|
is_anonymous: bool,
|
||||||
|
kind: enum { @"struct", @"union" },
|
||||||
|
forward_declaration: bool,
|
||||||
|
fields: []const Field,
|
||||||
|
conditionals: []const Conditional = &.{},
|
||||||
|
|
||||||
|
const Field = struct {
|
||||||
|
name: []const u8,
|
||||||
|
is_anonymous: bool,
|
||||||
|
type: Type,
|
||||||
|
width: ?usize = null,
|
||||||
|
default_value: ?std.json.Value = null,
|
||||||
|
conditionals: []const Conditional = &.{},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const Function = struct {
|
||||||
|
original_class: ?[]const u8 = null,
|
||||||
|
name: []const u8,
|
||||||
|
arguments: []const Argument = &.{},
|
||||||
|
return_type: Type,
|
||||||
|
conditionals: []const Conditional = &.{},
|
||||||
|
|
||||||
|
const Argument = struct {
|
||||||
|
type: ?Type = null,
|
||||||
|
is_varargs: bool,
|
||||||
|
is_instance_pointer: bool,
|
||||||
|
default_value: ?[]const u8 = null,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// `dear_bindings` doesn't parse these for us, it just forwards the c preprocessor strings.
|
||||||
|
// This is a whitelist of the current values so that we can react appropriately as new values
|
||||||
|
// are added.
|
||||||
|
const Conditional = struct {
|
||||||
|
condition: enum { ifdef, ifndef, @"if", ifnot },
|
||||||
|
expression: enum {
|
||||||
|
IMGUI_DISABLE_OBSOLETE_FUNCTIONS,
|
||||||
|
IMGUI_DISABLE_OBSOLETE_KEYIO,
|
||||||
|
IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT,
|
||||||
|
IMGUI_USE_WCHAR32,
|
||||||
|
ImTextureID,
|
||||||
|
ImDrawIdx,
|
||||||
|
ImDrawCallback,
|
||||||
|
CIMGUI_API,
|
||||||
|
CIMGUI_IMPL_API,
|
||||||
|
@"defined(_MSC_VER)&&!defined(__clang__)&&!defined(__INTEL_COMPILER)&&!defined(IMGUI_DEBUG_PARANOID)",
|
||||||
|
@"defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS)&&!defined(IMGUI_DISABLE_OBSOLETE_KEYIO)",
|
||||||
|
IMGUI_DEFINE_MATH_OPERATORS,
|
||||||
|
IM_COL32_R_SHIFT,
|
||||||
|
IMGUI_USE_BGRA_PACKED_COLOR,
|
||||||
|
IM_DRAWLIST_TEX_LINES_WIDTH_MAX,
|
||||||
|
@"defined(IMGUI_DISABLE_METRICS_WINDOW)&&!defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS)&&!defined(IMGUI_DISABLE_DEBUG_TOOLS)",
|
||||||
|
@"defined(IMGUI_HAS_IMSTR)",
|
||||||
|
IMGUI_HAS_IMSTR,
|
||||||
|
@"defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES)&&!defined(VK_NO_PROTOTYPES)",
|
||||||
|
@"defined(VK_USE_PLATFORM_WIN32_KHR)&&!defined(NOMINMAX)",
|
||||||
|
@"defined(VK_VERSION_1_3)|| defined(VK_KHR_dynamic_rendering)",
|
||||||
|
IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const Type = struct {
|
||||||
|
type_details: ?Details = null,
|
||||||
|
description: Description,
|
||||||
|
|
||||||
|
const Details = struct {
|
||||||
|
flavour: enum { function_pointer },
|
||||||
|
arguments: []const struct {
|
||||||
|
type: Type,
|
||||||
|
is_array: bool,
|
||||||
|
is_varargs: bool,
|
||||||
|
},
|
||||||
|
return_type: *Type,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Description = struct {
|
||||||
|
kind: enum { Type, Function, Array, Pointer, Builtin, User },
|
||||||
|
storage_classes: []const enum { @"const" } = &.{},
|
||||||
|
inner_type: ?*Description = null,
|
||||||
|
bounds: ?[]const u8 = null, // Literal or variable
|
||||||
|
builtin_type: ?Builtin = null,
|
||||||
|
name: ?[]const u8 = null,
|
||||||
|
|
||||||
|
const Builtin = enum {
|
||||||
|
void,
|
||||||
|
char,
|
||||||
|
unsigned_char,
|
||||||
|
short,
|
||||||
|
unsigned_short,
|
||||||
|
int,
|
||||||
|
unsigned_int,
|
||||||
|
long,
|
||||||
|
unsigned_long,
|
||||||
|
long_long,
|
||||||
|
unsigned_long_long,
|
||||||
|
float,
|
||||||
|
double,
|
||||||
|
long_double,
|
||||||
|
bool,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
// Allocator and command line args
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{
|
||||||
|
.thread_safe = false,
|
||||||
|
}){};
|
||||||
|
defer std.debug.assert(gpa.deinit() == .ok);
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
|
var args = try std.process.argsWithAllocator(allocator);
|
||||||
|
std.debug.assert(args.skip());
|
||||||
|
const in_path = args.next().?;
|
||||||
|
const out_path = args.next().?;
|
||||||
|
const prefix_path = args.next();
|
||||||
|
const postfix_path = args.next();
|
||||||
|
std.debug.assert(args.next() == null);
|
||||||
|
|
||||||
|
const out = try std.fs.cwd().createFile(out_path, .{});
|
||||||
|
defer out.close();
|
||||||
|
var buf = std.io.bufferedWriter(out.writer());
|
||||||
|
const writer = buf.writer();
|
||||||
|
|
||||||
|
// Write the prefix
|
||||||
|
if (prefix_path) |p| {
|
||||||
|
const prefix_source = try std.fs.cwd().readFileAlloc(allocator, p, max_size);
|
||||||
|
defer allocator.free(prefix_source);
|
||||||
|
try writer.writeAll(prefix_source);
|
||||||
|
try writer.writeAll("\n// End of prefix\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the source
|
||||||
|
{
|
||||||
|
const source = try std.fs.cwd().readFileAlloc(allocator, in_path, max_size);
|
||||||
|
defer allocator.free(source);
|
||||||
|
|
||||||
|
const header = try std.json.parseFromSlice(Header, allocator, source, .{
|
||||||
|
.ignore_unknown_fields = true,
|
||||||
|
});
|
||||||
|
defer header.deinit();
|
||||||
|
|
||||||
|
// We need the list of declarations up front.
|
||||||
|
var declarations = try getDeclarations(allocator, &header.value);
|
||||||
|
defer declarations.deinit();
|
||||||
|
|
||||||
|
// Write all defines as private constants.
|
||||||
|
try writeDefines(writer, &header.value);
|
||||||
|
|
||||||
|
// Write all typedefs as private constants.
|
||||||
|
try writeTypedefs(writer, &header.value, &declarations);
|
||||||
|
|
||||||
|
// Write all cimgui functions as private extern functions.
|
||||||
|
try writeExternFunctions(writer, &header.value, &declarations);
|
||||||
|
|
||||||
|
// Alias cimgui free functions under Zig friendly names.
|
||||||
|
try writeFreeFunctions(writer, &header.value);
|
||||||
|
|
||||||
|
// Get a list of cimgui methods. These were already written as externs, and can be aliased
|
||||||
|
// when we write their respective types.
|
||||||
|
var methods = try Methods.get(allocator, &header.value);
|
||||||
|
defer methods.deinit();
|
||||||
|
|
||||||
|
// Write cimgui enums as Zig enums.
|
||||||
|
try writeEnums(allocator, writer, &header.value);
|
||||||
|
|
||||||
|
// Write cimgui structs as Zig structs and unions.
|
||||||
|
try writeStructs(writer, &header.value, &declarations, &methods);
|
||||||
|
|
||||||
|
// Write helpers used by the other generated code.
|
||||||
|
try writeHelpers(writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the postfix
|
||||||
|
if (postfix_path) |p| {
|
||||||
|
const postfix_source = try std.fs.cwd().readFileAlloc(allocator, p, max_size);
|
||||||
|
defer allocator.free(postfix_source);
|
||||||
|
try writer.writeAll("\n// Start of postfix\n\n");
|
||||||
|
try writer.writeAll(postfix_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush and exit
|
||||||
|
try buf.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getDeclarations(allocator: Allocator, header: *const Header) !Declarations {
|
||||||
|
var declarations = Declarations.init(allocator);
|
||||||
|
errdefer declarations.deinit();
|
||||||
|
for (header.structs) |ty| {
|
||||||
|
if (skip(ty.conditionals)) continue;
|
||||||
|
|
||||||
|
// Check if we're a forward decl, or a packed struct.
|
||||||
|
//
|
||||||
|
// We *could* write out the Zig code to pack them by passing the widths in when writing
|
||||||
|
// the type (and overwriting number types with the actual width, asserting otherwise.)
|
||||||
|
//
|
||||||
|
// However, we'd need to decide the correct backing type for the packed struct to make
|
||||||
|
// it compatible with C. I'm also not 100% what the guarantees are for packed struct
|
||||||
|
// layout in C.
|
||||||
|
var is_opaque = false;
|
||||||
|
if (ty.forward_declaration) {
|
||||||
|
is_opaque = true;
|
||||||
|
} else if (ty.kind == .@"struct") {
|
||||||
|
for (ty.fields) |field| if (field.width != null) {
|
||||||
|
is_opaque = true;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const trimmed = std.mem.trimRight(u8, ty.name, "_");
|
||||||
|
try declarations.put(trimmed, .{ .is_opaque = is_opaque });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (header.enums) |e| {
|
||||||
|
if (e.is_internal) continue;
|
||||||
|
if (skip(e.conditionals)) continue;
|
||||||
|
|
||||||
|
const trimmed = std.mem.trimRight(u8, e.name, "_");
|
||||||
|
try declarations.put(trimmed, .{ .is_opaque = false });
|
||||||
|
}
|
||||||
|
|
||||||
|
return declarations;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeDefines(writer: anytype, header: *const Header) !void {
|
||||||
|
for (header.defines) |define| {
|
||||||
|
if (define.is_internal) continue;
|
||||||
|
if (skip(define.conditionals)) continue;
|
||||||
|
if (define.content) |content| {
|
||||||
|
try writer.print("const {s} = {s};\n", .{ define.name, content });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeTypedefs(writer: anytype, header: *const Header, declarations: *const Declarations) !void {
|
||||||
|
for (header.typedefs) |typedef| {
|
||||||
|
// Skip typedefs skipped by the preprocessor
|
||||||
|
if (skip(typedef.conditionals)) continue;
|
||||||
|
|
||||||
|
// Skip duplicate declarations (e.g. naming enums as ints in C)
|
||||||
|
if (declarations.contains(typedef.name)) continue;
|
||||||
|
|
||||||
|
// Write the typedef
|
||||||
|
try writer.writeAll("const ");
|
||||||
|
try writeTypeName(writer, typedef.name);
|
||||||
|
try writer.writeAll(" = ");
|
||||||
|
try writeType(writer, typedef.type, declarations, .{});
|
||||||
|
try writer.writeAll(";\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeExternFunctions(
|
||||||
|
writer: anytype,
|
||||||
|
header: *const Header,
|
||||||
|
declarations: *const Declarations,
|
||||||
|
) !void {
|
||||||
|
for (header.functions) |function| {
|
||||||
|
if (skip(function.conditionals)) continue;
|
||||||
|
if (argsContainsVaList(function.arguments)) continue;
|
||||||
|
|
||||||
|
try writer.print("extern fn {s}(", .{function.name});
|
||||||
|
for (function.arguments) |argument| {
|
||||||
|
if (argument.type) |ty| {
|
||||||
|
std.debug.assert(!argument.is_varargs);
|
||||||
|
try writeType(writer, ty, declarations, .{
|
||||||
|
.is_instance_pointer = argument.is_instance_pointer,
|
||||||
|
.is_argument = true,
|
||||||
|
.default_null = if (argument.default_value) |d| std.mem.eql(u8, d, "NULL") else false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
std.debug.assert(argument.is_varargs);
|
||||||
|
try writer.writeAll("...");
|
||||||
|
}
|
||||||
|
try writer.writeAll(", ");
|
||||||
|
}
|
||||||
|
try writer.writeAll(") callconv(.C) ");
|
||||||
|
try writeType(writer, function.return_type, declarations, .{ .is_result = true });
|
||||||
|
try writer.writeAll(";\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn argsContainsVaList(arguments: []const Header.Function.Argument) bool {
|
||||||
|
for (arguments) |argument| {
|
||||||
|
if (argument.type) |ty| {
|
||||||
|
if (ty.description.name) |name| {
|
||||||
|
if (std.mem.eql(u8, name, "va_list")) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeFreeFunctions(writer: anytype, header: *const Header) !void {
|
||||||
|
for (header.functions) |function| {
|
||||||
|
if (skip(function.conditionals)) continue;
|
||||||
|
if (function.original_class != null) continue;
|
||||||
|
if (argsContainsVaList(function.arguments)) continue;
|
||||||
|
|
||||||
|
try writer.writeAll("pub const ");
|
||||||
|
try writeFunctionName(writer, function.name);
|
||||||
|
try writer.print(" = {s};\n", .{function.name});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Methods = struct {
|
||||||
|
types: std.StringArrayHashMap(std.ArrayList([]const u8)),
|
||||||
|
|
||||||
|
fn get(allocator: Allocator, header: *const Header) !Methods {
|
||||||
|
// Initialize an empty method list for each type
|
||||||
|
var types = std.StringArrayHashMap(std.ArrayList([]const u8)).init(allocator);
|
||||||
|
errdefer types.deinit();
|
||||||
|
errdefer for (types.values()) |methods| {
|
||||||
|
methods.deinit();
|
||||||
|
};
|
||||||
|
for (header.structs) |ty| {
|
||||||
|
const methods = std.ArrayList([]const u8).init(allocator);
|
||||||
|
errdefer methods.deinit();
|
||||||
|
try types.put(ty.name, methods);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill in the method lists
|
||||||
|
for (header.functions) |function| {
|
||||||
|
if (skip(function.conditionals)) continue;
|
||||||
|
if (argsContainsVaList(function.arguments)) continue;
|
||||||
|
|
||||||
|
if (function.original_class) |class| {
|
||||||
|
const methods = types.getPtr(class).?;
|
||||||
|
try methods.append(function.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{ .types = types };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deinit(self: *Methods) void {
|
||||||
|
for (self.types.values()) |methods| {
|
||||||
|
methods.deinit();
|
||||||
|
}
|
||||||
|
self.types.deinit();
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn writeEnums(allocator: Allocator, writer: anytype, header: *const Header) !void {
|
||||||
|
for (header.enums) |e| {
|
||||||
|
if (e.is_internal) continue;
|
||||||
|
if (skip(e.conditionals)) continue;
|
||||||
|
|
||||||
|
try writer.writeAll("pub const ");
|
||||||
|
try writeTypeName(writer, e.name);
|
||||||
|
try writer.writeAll(" = ");
|
||||||
|
|
||||||
|
if (e.is_flags_enum) {
|
||||||
|
try writeFlagsEnum(writer, e);
|
||||||
|
} else {
|
||||||
|
try writeNormalEnum(allocator, writer, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeFlagsEnum(writer: anytype, e: Header.Enum) !void {
|
||||||
|
const backing, const backing_bits = switch (e.storage_type.declaration) {
|
||||||
|
.int => .{"c_int", @typeInfo(c_int).Int.bits},
|
||||||
|
.ImU8 => .{"u8", 8},
|
||||||
|
};
|
||||||
|
try writer.print("packed struct({s}) {{\n", .{backing});
|
||||||
|
var current_offset: usize = 0;
|
||||||
|
var padding_i: usize = 0;
|
||||||
|
for (e.elements) |element| {
|
||||||
|
// Skip internal elements, and elements discarded by preprocessor
|
||||||
|
if (skip(element.conditionals)) continue;
|
||||||
|
if (element.is_internal) continue;
|
||||||
|
if (element.value == 0) continue;
|
||||||
|
|
||||||
|
// Calculate the offset of this element
|
||||||
|
const offset = std.math.log2(@as(usize, @intCast(element.value)));
|
||||||
|
|
||||||
|
// Sometimes at the end of flag sets, elements are defined that are combinations of
|
||||||
|
// existing flags. Skip these--the only reasonable representation would be constants, but
|
||||||
|
// they're hard to compose in Zig so it's not worth it.
|
||||||
|
if (offset < current_offset) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add padding to get this element to the correct offset
|
||||||
|
const padding = offset - current_offset;
|
||||||
|
if (padding > 0) {
|
||||||
|
try writer.print(" __padding{}: u{} = 0,\n", .{ padding_i, padding });
|
||||||
|
padding_i += 1;
|
||||||
|
current_offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the element
|
||||||
|
try writer.writeAll(" ");
|
||||||
|
try writeElementName(writer, e.name, element.name);
|
||||||
|
try writer.writeAll(": bool = false,\n");
|
||||||
|
current_offset += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add padding at end to make total type the correct size
|
||||||
|
const padding = backing_bits - current_offset;
|
||||||
|
if (padding > 0) {
|
||||||
|
try writer.print(" __padding{}: u{} = 0,\n", .{ padding_i, padding });
|
||||||
|
}
|
||||||
|
|
||||||
|
try writer.writeAll("};\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeNormalEnum(allocator: Allocator, writer: anytype, e: Header.Enum) !void {
|
||||||
|
var values = std.AutoArrayHashMap(i64, void).init(allocator);
|
||||||
|
defer values.deinit();
|
||||||
|
|
||||||
|
try writer.writeAll("enum(");
|
||||||
|
switch (e.storage_type.declaration) {
|
||||||
|
.int => try writer.writeAll("c_int"),
|
||||||
|
.ImU8 => try writer.writeAll("u8"),
|
||||||
|
}
|
||||||
|
try writer.writeAll(") {\n");
|
||||||
|
|
||||||
|
// Write elements
|
||||||
|
for (e.elements) |element| {
|
||||||
|
// Skip internal and count
|
||||||
|
if (element.is_internal) continue;
|
||||||
|
if (element.is_count) continue;
|
||||||
|
if (skip(element.conditionals)) continue;
|
||||||
|
|
||||||
|
// We skip duplicate values, these are sometimes present e.g. in the keys enum which
|
||||||
|
// contains the mods enum and therefore two "none" options that are identical.
|
||||||
|
if (values.contains(element.value)) continue;
|
||||||
|
try values.put(element.value, {});
|
||||||
|
|
||||||
|
// Write the element
|
||||||
|
try writer.writeAll(" ");
|
||||||
|
try writeElementName(writer, e.name, element.name);
|
||||||
|
try writer.print(" = {},\n", .{element.value});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write constants since they're used internally, but keep them private
|
||||||
|
for (e.elements) |element| {
|
||||||
|
if (element.is_count) {
|
||||||
|
try writer.writeAll(" const ");
|
||||||
|
try writeElementName(writer, e.name, element.name);
|
||||||
|
try writer.print(" = {};\n", .{element.value});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try writer.writeAll("};\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeStructs(
|
||||||
|
writer: anytype,
|
||||||
|
header: *const Header,
|
||||||
|
declarations: *const Declarations,
|
||||||
|
methods: *const Methods,
|
||||||
|
) !void {
|
||||||
|
for (header.structs) |ty| {
|
||||||
|
// Skip structs skipped by the preprocessor. We don't skip structs marked as internal,
|
||||||
|
// because many of these appear to be generally useful (it may be set incorrectly in the
|
||||||
|
// JSON?)
|
||||||
|
if (skip(ty.conditionals)) continue;
|
||||||
|
|
||||||
|
// Write the struct
|
||||||
|
try writer.writeAll("pub const ");
|
||||||
|
try writeTypeName(writer, ty.name);
|
||||||
|
try writer.writeAll(" = ");
|
||||||
|
|
||||||
|
// If we're opaque, don't try to fill out the struct fields
|
||||||
|
if (declarations.get(ty.name).?.is_opaque) {
|
||||||
|
try writer.writeAll("opaque {};\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare the struct or union
|
||||||
|
switch (ty.kind) {
|
||||||
|
.@"struct" => try writer.writeAll("extern struct {\n"),
|
||||||
|
.@"union" => try writer.writeAll("extern union {\n"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill in the fields
|
||||||
|
for (ty.fields) |field| {
|
||||||
|
// Skip fields skipped by the preprocessor.
|
||||||
|
if (skip(field.conditionals)) continue;
|
||||||
|
|
||||||
|
// Not yet used, but when it is we want to start using it. Safe to disable this assert
|
||||||
|
// if you're just trying to get things working with a different version.
|
||||||
|
if (field.default_value != null) @panic("unimplemented");
|
||||||
|
|
||||||
|
// Write the field.
|
||||||
|
try writer.writeAll(" ");
|
||||||
|
if (field.is_anonymous) {
|
||||||
|
try writer.writeAll("data");
|
||||||
|
} else {
|
||||||
|
try writeFieldName(writer, field.name);
|
||||||
|
}
|
||||||
|
try writer.writeAll(": ");
|
||||||
|
try writeType(writer, field.type, declarations, .{});
|
||||||
|
try writer.writeAll(",\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alias all relevant methods into the type.
|
||||||
|
const method_names = methods.types.getPtr(ty.name).?;
|
||||||
|
for (method_names.items) |name| {
|
||||||
|
try writer.writeAll(" pub const ");
|
||||||
|
try writeFunctionName(writer, name[ty.name.len + 1..]);
|
||||||
|
try writer.print(" = {s};\n", .{name});
|
||||||
|
}
|
||||||
|
|
||||||
|
try writer.writeAll("};\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeHelpers(writer: anytype) !void {
|
||||||
|
try writer.writeAll(
|
||||||
|
\\fn toUsize(v: anytype) usize {
|
||||||
|
\\ if (@typeInfo(@TypeOf(v)) == .Enum) return @intFromEnum(v);
|
||||||
|
\\ return @intCast(v);
|
||||||
|
\\}
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type hints aren't required, but they help us figure out the best pointer types to use.
|
||||||
|
const WriteTypeHints = packed struct {
|
||||||
|
is_instance_pointer: bool = false,
|
||||||
|
is_argument: bool = false,
|
||||||
|
default_null: bool = false,
|
||||||
|
is_result: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Write a cimgui type as a Zig type.
|
||||||
|
fn writeType(
|
||||||
|
writer: anytype,
|
||||||
|
ty: Header.Type,
|
||||||
|
declarations: *const Declarations,
|
||||||
|
hints: WriteTypeHints,
|
||||||
|
) @TypeOf(writer).Error!void {
|
||||||
|
// Handle function pointers which are stored separately.
|
||||||
|
if (ty.type_details) |details| switch (details.flavour) {
|
||||||
|
.function_pointer => return writeFunctionPointer(writer, details, declarations),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle all other types
|
||||||
|
switch (ty.description.kind) {
|
||||||
|
.Builtin => return writeBuiltinType(writer, ty.description.builtin_type.?),
|
||||||
|
.Array => return writeArrayType(writer, ty.description, declarations),
|
||||||
|
.Pointer => return writePointerType(writer, ty, declarations, hints),
|
||||||
|
.User => try writeTypeName(writer, ty.description.name.?),
|
||||||
|
.Type, .Function => @panic("unimplemented"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeFunctionPointer(
|
||||||
|
writer: anytype,
|
||||||
|
details: Header.Type.Details,
|
||||||
|
declarations: *const Declarations,
|
||||||
|
) !void {
|
||||||
|
try writer.writeAll("*const fn(");
|
||||||
|
for (details.arguments) |argument| {
|
||||||
|
try writeType(writer, argument.type, declarations, .{});
|
||||||
|
try writer.writeAll(", ");
|
||||||
|
}
|
||||||
|
try writer.writeAll(") callconv(.C) ");
|
||||||
|
try writeType(writer, details.return_type.*, declarations, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeBuiltinType(writer: anytype, builtin_type: Header.Type.Description.Builtin) !void {
|
||||||
|
switch (builtin_type) {
|
||||||
|
.void => try writer.writeAll("void"),
|
||||||
|
.char, .unsigned_char => try writer.writeAll("u8"),
|
||||||
|
.short => try writer.writeAll("c_short"),
|
||||||
|
.unsigned_short => try writer.writeAll("c_ushort"),
|
||||||
|
.int => try writer.writeAll("c_int"),
|
||||||
|
.unsigned_int => try writer.writeAll("c_uint"),
|
||||||
|
.long => try writer.writeAll("c_long"),
|
||||||
|
.unsigned_long => try writer.writeAll("c_ulong"),
|
||||||
|
.long_long => try writer.writeAll("c_longlong"),
|
||||||
|
.unsigned_long_long => try writer.writeAll("c_ulonglong"),
|
||||||
|
.float => try writer.writeAll("f32"),
|
||||||
|
.double => try writer.writeAll("f64"),
|
||||||
|
.long_double => try writer.writeAll("c_longdouble"),
|
||||||
|
.bool => try writer.writeAll("bool"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeArrayType(
|
||||||
|
writer: anytype,
|
||||||
|
description: Header.Type.Description,
|
||||||
|
declarations: *const Declarations,
|
||||||
|
) !void {
|
||||||
|
if (description.bounds) |bounds| {
|
||||||
|
try writeArrayBounds(writer, bounds);
|
||||||
|
} else {
|
||||||
|
try writer.writeAll("[*]");
|
||||||
|
}
|
||||||
|
try writeType(
|
||||||
|
writer,
|
||||||
|
.{ .description = description.inner_type.?.* },
|
||||||
|
declarations,
|
||||||
|
.{},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array bounds sometimes include expressions, so we need to tokenize them and convert the
|
||||||
|
// expressions to Zig syntax.
|
||||||
|
fn writeArrayBounds(writer: anytype, bounds: []const u8) !void {
|
||||||
|
try writer.writeByte('[');
|
||||||
|
var token_start: usize = 0;
|
||||||
|
while (token_start < bounds.len) {
|
||||||
|
// Calculate token start
|
||||||
|
if (bounds[token_start] == ' ') {
|
||||||
|
token_start += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special handling for one character tokens
|
||||||
|
switch (bounds[token_start]) {
|
||||||
|
'(' => {
|
||||||
|
const token = bounds[token_start .. token_start + 1];
|
||||||
|
token_start += 1;
|
||||||
|
try writer.writeAll(token);
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
'+', '/' => {
|
||||||
|
const token = bounds[token_start .. token_start + 1];
|
||||||
|
token_start += 1;
|
||||||
|
try writer.print(" {s} ", .{token});
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate token end
|
||||||
|
var token_end = token_start + 1;
|
||||||
|
while (token_end < bounds.len) : (token_end += 1) {
|
||||||
|
switch (bounds[token_end]) {
|
||||||
|
' ', '+', ')', '/' => break,
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const token = bounds[token_start..token_end];
|
||||||
|
token_start = token_end;
|
||||||
|
|
||||||
|
// As of the time of writing, tokens here are either numbers or enum values.
|
||||||
|
if (std.mem.indexOfScalar(u8, token, '_')) |underscore| {
|
||||||
|
// If it's all uppercase, it's a define. Otherwise it's an enum.
|
||||||
|
const is_define = for (token) |c| {
|
||||||
|
switch (c) {
|
||||||
|
'a'...'z' => break false,
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
} else true;
|
||||||
|
|
||||||
|
if (is_define) {
|
||||||
|
try writer.writeAll(token);
|
||||||
|
} else {
|
||||||
|
try writer.writeAll("toUsize(");
|
||||||
|
const type_name = token[0..underscore];
|
||||||
|
try writeTypeName(writer, type_name);
|
||||||
|
try writer.writeByte('.');
|
||||||
|
try writeElementName(writer, type_name, token);
|
||||||
|
try writer.writeAll(")");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Otherwise, just write the value as is.
|
||||||
|
try writer.writeAll(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try writer.writeByte(']');
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writePointerType(
|
||||||
|
writer: anytype,
|
||||||
|
ty: Header.Type,
|
||||||
|
declarations: *const Declarations,
|
||||||
|
hints: WriteTypeHints,
|
||||||
|
) !void {
|
||||||
|
// Check if we're a pointer to an opaque type
|
||||||
|
var is_opaque = false;
|
||||||
|
if (ty.description.inner_type.?.name) |name| {
|
||||||
|
if (declarations.get(name)) |decl| {
|
||||||
|
is_opaque = decl.is_opaque;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we're a pointer to void
|
||||||
|
const is_void = ty.description.inner_type.?.builtin_type == .void;
|
||||||
|
|
||||||
|
// Check if we're a string
|
||||||
|
const is_string = b: {
|
||||||
|
if (ty.description.inner_type.?.builtin_type != .char) break :b false;
|
||||||
|
for (ty.description.inner_type.?.*.storage_classes) |storage_class| switch (storage_class) {
|
||||||
|
.@"const" => break :b true,
|
||||||
|
};
|
||||||
|
break :b false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use the hints to decide what kind of pointer we are
|
||||||
|
if (is_string) {
|
||||||
|
// We currently write all strings as c pointers since some are null terminated, and some are
|
||||||
|
// not.
|
||||||
|
try writer.writeAll("[*c]");
|
||||||
|
} else if (hints.is_instance_pointer) {
|
||||||
|
// We assume all instance pointers do *not* allow null.
|
||||||
|
std.debug.assert(!hints.default_null);
|
||||||
|
try writer.writeByte('*');
|
||||||
|
} else if (hints.is_argument) {
|
||||||
|
// Arguments are assumed to be single value pointers, because many value pointers when
|
||||||
|
// passed as arguments get marked as arrays and handled separately from pointers.
|
||||||
|
if (hints.default_null) {
|
||||||
|
// If the default value is set to null, we must be nullable. Otherwise we conservatively
|
||||||
|
// assume null is not allowed, which tends to be correct in practice more often than
|
||||||
|
// not.
|
||||||
|
try writer.writeByte('?');
|
||||||
|
}
|
||||||
|
try writer.writeByte('*');
|
||||||
|
} else if (hints.is_result) {
|
||||||
|
// Results are assumed to be single value pointers, since otherwise, we'd have no way of
|
||||||
|
// knowing the length. We conservatively assume these are nullable since we have no way of
|
||||||
|
// knowing. Ideally dear bindings would mark this eventually (there is a field for it but
|
||||||
|
// it is unused), if it becomes annoying we can always add a whitelist.
|
||||||
|
try writer.writeAll("?*");
|
||||||
|
} else {
|
||||||
|
// If we've reached this case, we're a struct field, and we don't know whether it's many
|
||||||
|
// value or nullable. We fall back to a c pointer so that the user can decide how to
|
||||||
|
// interpret it with less friction, unless it's an opaque type in which case that's not
|
||||||
|
// allowed so we assume it's a nullable single value pointer.
|
||||||
|
if (is_opaque or is_void) {
|
||||||
|
try writer.writeAll("?*");
|
||||||
|
} else {
|
||||||
|
try writer.writeAll("[*c]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write any storage classes
|
||||||
|
for (ty.description.inner_type.?.*.storage_classes) |storage_class| switch (storage_class) {
|
||||||
|
.@"const" => try writer.writeAll("const "),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Write the actual type
|
||||||
|
if (is_void) {
|
||||||
|
// Treat pointers to c void as pointers to anyopaque
|
||||||
|
try writer.writeAll("anyopaque");
|
||||||
|
} else {
|
||||||
|
try writeType(
|
||||||
|
writer,
|
||||||
|
.{ .description = ty.description.inner_type.?.* },
|
||||||
|
declarations,
|
||||||
|
.{},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if we should skip due to a conditional
|
||||||
|
fn skip(conditionals: []const Header.Conditional) bool {
|
||||||
|
for (conditionals) |conditional| {
|
||||||
|
const defined = switch (conditional.expression) {
|
||||||
|
.IMGUI_DISABLE_OBSOLETE_FUNCTIONS,
|
||||||
|
.IMGUI_DISABLE_OBSOLETE_KEYIO,
|
||||||
|
.CIMGUI_API,
|
||||||
|
.CIMGUI_IMPL_API,
|
||||||
|
.@"defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES)&&!defined(VK_NO_PROTOTYPES)",
|
||||||
|
.@"defined(VK_USE_PLATFORM_WIN32_KHR)&&!defined(NOMINMAX)",
|
||||||
|
.IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING,
|
||||||
|
.@"defined(VK_VERSION_1_3)|| defined(VK_KHR_dynamic_rendering)",
|
||||||
|
=> true,
|
||||||
|
.IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT,
|
||||||
|
.IMGUI_USE_WCHAR32,
|
||||||
|
.ImTextureID,
|
||||||
|
.ImDrawIdx,
|
||||||
|
.ImDrawCallback,
|
||||||
|
.@"defined(_MSC_VER)&&!defined(__clang__)&&!defined(__INTEL_COMPILER)&&!defined(IMGUI_DEBUG_PARANOID)",
|
||||||
|
.@"defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS)&&!defined(IMGUI_DISABLE_OBSOLETE_KEYIO)",
|
||||||
|
.IMGUI_DEFINE_MATH_OPERATORS,
|
||||||
|
.IM_COL32_R_SHIFT,
|
||||||
|
.IMGUI_USE_BGRA_PACKED_COLOR,
|
||||||
|
.IM_DRAWLIST_TEX_LINES_WIDTH_MAX,
|
||||||
|
.@"defined(IMGUI_DISABLE_METRICS_WINDOW)&&!defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS)&&!defined(IMGUI_DISABLE_DEBUG_TOOLS)",
|
||||||
|
.@"defined(IMGUI_HAS_IMSTR)",
|
||||||
|
.IMGUI_HAS_IMSTR,
|
||||||
|
=> false,
|
||||||
|
};
|
||||||
|
switch (conditional.condition) {
|
||||||
|
.ifdef, .@"if" => if (!defined) return true,
|
||||||
|
.ifndef, .ifnot => if (defined) return true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts a cimgui type name to a Zig type name
|
||||||
|
fn writeTypeName(writer: anytype, raw: []const u8) !void {
|
||||||
|
// These are considered user types
|
||||||
|
if (std.mem.eql(u8, raw, "size_t")) {
|
||||||
|
try writer.writeAll("usize");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, raw, "uint32_t")) {
|
||||||
|
try writer.writeAll("u32");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We skip all declarations that contain `va_list`, so this shouldn't trigger.
|
||||||
|
if (std.mem.eql(u8, raw, "va_list")) unreachable;
|
||||||
|
|
||||||
|
// Remove prefixes
|
||||||
|
var name = raw;
|
||||||
|
{
|
||||||
|
// Imgui prefixes
|
||||||
|
{
|
||||||
|
const prefixes: []const []const u8 = &.{ "ImGui", "Im" };
|
||||||
|
for (prefixes) |prefix| {
|
||||||
|
if (std.mem.startsWith(u8, name, prefix)) {
|
||||||
|
name = name[prefix.len..];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backend prefixes
|
||||||
|
{
|
||||||
|
const prefixes: []const []const u8 = &.{ "_ImplVulkanH", "_ImplVulkan" };
|
||||||
|
for (prefixes) |prefix| {
|
||||||
|
if (std.mem.startsWith(u8, name, prefix)) {
|
||||||
|
name = name[prefix.len..];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (name) |c| switch (c) {
|
||||||
|
'_' => {},
|
||||||
|
else => try writer.writeByte(c),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a cimgui field name to a Zig field name
|
||||||
|
fn writeFieldName(writer: anytype, name: []const u8) !void {
|
||||||
|
var escape = false;
|
||||||
|
for (name, 0..) |c, i| {
|
||||||
|
switch (c) {
|
||||||
|
'0'...'9' => {
|
||||||
|
if (i == 0) {
|
||||||
|
escape = true;
|
||||||
|
try writer.writeAll("@\"");
|
||||||
|
} else switch (name[i - 1]) {
|
||||||
|
'0'...'9' => {},
|
||||||
|
else => try writer.writeByte('_'),
|
||||||
|
}
|
||||||
|
try writer.writeByte(c);
|
||||||
|
},
|
||||||
|
'a'...'z' => try writer.writeByte(c),
|
||||||
|
'A'...'Z' => {
|
||||||
|
if (i > 0) switch (name[i - 1]) {
|
||||||
|
'A'...'Z', '_' => {},
|
||||||
|
else => try writer.writeByte('_'),
|
||||||
|
};
|
||||||
|
try writer.writeByte(c + 32);
|
||||||
|
},
|
||||||
|
'_' => if (i != name.len - 1) try writer.writeByte('_'),
|
||||||
|
else => std.debug.panic("unexpected char in name: {c}", .{c}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (escape) try writer.writeAll("\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a cimgui element name to a Zig element name
|
||||||
|
fn writeElementName(writer: anytype, type_name: []const u8, raw: []const u8) !void {
|
||||||
|
var name = if (std.mem.startsWith(u8, raw, type_name)) raw[type_name.len..] else raw;
|
||||||
|
name = if (std.mem.startsWith(u8, name, "ImGui")) name["ImGui".len..] else name;
|
||||||
|
name = if (name[0] == '_') name[1..] else name;
|
||||||
|
try writeFieldName(writer, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a cimgui function name as a Zig function name
|
||||||
|
fn writeFunctionName(writer: anytype, raw: []const u8) !void {
|
||||||
|
var name = raw;
|
||||||
|
|
||||||
|
// Imgui prefixes
|
||||||
|
{
|
||||||
|
const prefixes: []const []const u8 = &.{ "cImGui_ImplVulkan", "ImGui", "Im" };
|
||||||
|
for (prefixes) |prefix| {
|
||||||
|
if (std.mem.startsWith(u8, name, prefix)) {
|
||||||
|
name = name[prefix.len..];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name[0] == '_') name = name[1..];
|
||||||
|
|
||||||
|
// Lowercase the first set of contiguous capital letters
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < name.len and name[i] >= 'A' and name[i] <= 'Z') : (i += 1) {
|
||||||
|
try writer.writeByte(name[i] + 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The rest of the string is usually already camelcase, write it as is unless we encounter an
|
||||||
|
// underscore in which case we should skip it an uppercase the next letter.
|
||||||
|
if (i < name.len) {
|
||||||
|
var uppercase_next = false;
|
||||||
|
for (name[i..]) |c| {
|
||||||
|
// If we're an underscore, skip it and uppercase the next letter
|
||||||
|
if (c == '_') {
|
||||||
|
uppercase_next = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the next character, adjusting the case as necessary
|
||||||
|
switch (c) {
|
||||||
|
'a'...'z' => try writer.writeByte(if (uppercase_next) c - 32 else c),
|
||||||
|
else => try writer.writeByte(c),
|
||||||
|
}
|
||||||
|
uppercase_next = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/templates/cimgui_postfix.zig.template
Normal file
8
src/templates/cimgui_postfix.zig.template
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
// It's unclear to me where the generator gets this define (maybe it's directly from the original
|
||||||
|
// cpp header?), so we generate it manually.
|
||||||
|
pub fn col32(r: u32, g: u32, b: u32, a: u32) u32 {
|
||||||
|
return a << IM_COL32_A_SHIFT |
|
||||||
|
b << IM_COL32_B_SHIFT |
|
||||||
|
g << IM_COL32_G_SHIFT |
|
||||||
|
r << IM_COL32_R_SHIFT;
|
||||||
|
}
|
||||||
0
src/templates/cimgui_prefix.zig.template
Normal file
0
src/templates/cimgui_prefix.zig.template
Normal file
2
src/templates/impl_vulkan_postfix.zig.template
Normal file
2
src/templates/impl_vulkan_postfix.zig.template
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
};
|
||||||
|
}
|
||||||
68
src/templates/impl_vulkan_prefix.zig.template
Normal file
68
src/templates/impl_vulkan_prefix.zig.template
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
const Options = struct {
|
||||||
|
PFNvkVoidFunction: type,
|
||||||
|
VkAllocationCallbacks: type,
|
||||||
|
VkClearValue: type,
|
||||||
|
VkColorSpaceKHR: type,
|
||||||
|
VkCommandBuffer: type,
|
||||||
|
VkCommandPool: type,
|
||||||
|
VkDescriptorPool: type,
|
||||||
|
VkDescriptorSet: type,
|
||||||
|
VkDevice: type,
|
||||||
|
VkDeviceSize: type,
|
||||||
|
VkFence: type,
|
||||||
|
VkFramebuffer: type,
|
||||||
|
VkImage: type,
|
||||||
|
VkImageLayout: type,
|
||||||
|
VkImageView: type,
|
||||||
|
VkInstance: type,
|
||||||
|
VkPhysicalDevice: type,
|
||||||
|
VkPipeline: type,
|
||||||
|
VkPipelineCache: type,
|
||||||
|
VkPipelineRenderingCreateInfoKHR: type,
|
||||||
|
VkPresentModeKHR: type,
|
||||||
|
VkQueue: type,
|
||||||
|
VkRenderPass: type,
|
||||||
|
VkResult: type,
|
||||||
|
VkSampleCountFlagBits: type,
|
||||||
|
VkSampler: type,
|
||||||
|
VkSemaphore: type,
|
||||||
|
VkSurfaceFormatKHR: type,
|
||||||
|
VkSurfaceKHR: type,
|
||||||
|
VkSwapchainKHR: type,
|
||||||
|
VkFormat: type,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn get(options: Options) type {
|
||||||
|
const VkFormat = options.VkFormat;
|
||||||
|
const PFNvkVoidFunction = options.PFNvkVoidFunction;
|
||||||
|
const VkAllocationCallbacks = options.VkAllocationCallbacks;
|
||||||
|
const VkClearValue = options.VkClearValue;
|
||||||
|
const VkColorSpaceKHR = options.VkColorSpaceKHR;
|
||||||
|
const VkCommandBuffer = options.VkCommandBuffer;
|
||||||
|
const VkCommandPool = options.VkCommandPool;
|
||||||
|
const VkDescriptorPool = options.VkDescriptorPool;
|
||||||
|
const VkDescriptorSet = options.VkDescriptorSet;
|
||||||
|
const VkDevice = options.VkDevice;
|
||||||
|
const VkDeviceSize = options.VkDeviceSize;
|
||||||
|
const VkFence = options.VkFence;
|
||||||
|
const VkFramebuffer = options.VkFramebuffer;
|
||||||
|
const VkImage = options.VkImage;
|
||||||
|
const VkImageLayout = options.VkImageLayout;
|
||||||
|
const VkImageView = options.VkImageView;
|
||||||
|
const VkInstance = options.VkInstance;
|
||||||
|
const VkPhysicalDevice = options.VkPhysicalDevice;
|
||||||
|
const VkPipeline = options.VkInstance;
|
||||||
|
const VkPipelineCache = options.VkPipelineCache;
|
||||||
|
const VkPipelineRenderingCreateInfoKHR = options.VkPipelineRenderingCreateInfoKHR;
|
||||||
|
const VkPresentModeKHR = options.VkPresentModeKHR;
|
||||||
|
const VkQueue = options.VkQueue;
|
||||||
|
const VkRenderPass = options.VkRenderPass;
|
||||||
|
const VkResult = options.VkResult;
|
||||||
|
const VkSampleCountFlagBits = options.VkSampleCountFlagBits;
|
||||||
|
const VkSampler = options.VkSampler;
|
||||||
|
const VkSemaphore = options.VkSemaphore;
|
||||||
|
const VkSurfaceFormatKHR = options.VkSurfaceFormatKHR;
|
||||||
|
const VkSurfaceKHR = options.VkSurfaceKHR;
|
||||||
|
const VkSwapchainKHR = options.VkSwapchainKHR;
|
||||||
|
|
||||||
|
return struct {
|
||||||
Loading…
Add table
Add a link
Reference in a new issue