chore: update to 2a0400ade5535001bfc09e0e7531515e232be6e7

This commit is contained in:
Rekai Nyangadzayi Musuka 2024-07-03 19:55:17 -05:00
parent e009f01d38
commit c461e29eaa
91 changed files with 39733 additions and 3468 deletions

5
.gitignore vendored
View File

@ -1,2 +1,3 @@
/zig-cache .zig-cache/
/zig-out zig-cache/
zig-out/

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2022 Michal Ziulek
Copyright (c) 2024 zig-gamedev 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.

View File

@ -1,4 +1,4 @@
# zgui v0.1.0 - dear imgui bindings # zgui v0.2.0 - dear imgui bindings
Easy to use, hand-crafted API with default arguments, named parameters and Zig style text formatting. [Here](https://github.com/michal-z/zig-gamedev/tree/main/samples/minimal_zgpu_zgui) is a simple sample application, and [here](https://github.com/michal-z/zig-gamedev/tree/main/samples/gui_test_wgpu) is a full one. Easy to use, hand-crafted API with default arguments, named parameters and Zig style text formatting. [Here](https://github.com/michal-z/zig-gamedev/tree/main/samples/minimal_zgpu_zgui) is a simple sample application, and [here](https://github.com/michal-z/zig-gamedev/tree/main/samples/gui_test_wgpu) is a full one.
@ -8,6 +8,13 @@ Easy to use, hand-crafted API with default arguments, named parameters and Zig s
* All memory allocations go through user provided Zig allocator * All memory allocations go through user provided Zig allocator
* [DrawList API](#drawlist-api) for vector graphics, text rendering and custom widgets * [DrawList API](#drawlist-api) for vector graphics, text rendering and custom widgets
* [Plot API](#plot-api) for advanced data visualizations * [Plot API](#plot-api) for advanced data visualizations
* [Test engine API](#test-engine-api) for automatic testing
## Versions
* [ImGui](https://github.com/ocornut/imgui/tree/v1.90.4-docking) `1.90.4-docking`
* [ImGui test engine](https://github.com/ocornut/imgui_test_engine/tree/v1.90.4) `1.90.4`
* [ImPlot](https://github.com/epezent/implot) `O.17`
## Getting started ## Getting started
@ -37,11 +44,11 @@ pub fn build(b: *std.Build) void {
exe.linkLibrary(zglfw.artifact("glfw")); exe.linkLibrary(zglfw.artifact("glfw"));
const zpool = b.dependency("zpool", .{}); const zpool = b.dependency("zpool", .{});
exe.root_module.addImport("zpool", zglfw.module("root")); exe.root_module.addImport("zpool", zpool.module("root"));
const zgpu = b.dependency("zgpu", .{}); const zgpu = b.dependency("zgpu", .{});
exe.root_module.addImport("zgpu", zglfw.module("root")); exe.root_module.addImport("zgpu", zgpu.module("root"));
exe.linkLibrary(zglfw.artifact("wgpu")); exe.linkLibrary(zgpu.artifact("zdawn"));
} }
} }
``` ```
@ -147,3 +154,48 @@ if (zgui.plot.beginPlot("Line Plot", .{ .h = -1.0 })) {
zgui.plot.endPlot(); zgui.plot.endPlot();
} }
``` ```
### Test Engine API
Zig wraper for [ImGUI test engine](https://github.com/ocornut/imgui_test_engine).
```zig
var check_b = false;
var _te: *zgui.te.TestEngine = zgui.te.getTestEngine().?;
fn registerTests() void {
_ = _te.registerTest(
"Awesome",
"should_do_some_another_magic",
@src(),
struct {
pub fn gui(ctx: *zgui.te.TestContext) !void {
_ = ctx; // autofix
_ = zgui.begin("Test Window", .{ .flags = .{ .no_saved_settings = true } });
defer zgui.end();
zgui.text("Hello, automation world", .{});
_ = zgui.button("Click Me", .{});
if (zgui.treeNode("Node")) {
defer zgui.treePop();
_ = zgui.checkbox("Checkbox", .{ .v = &check_b });
}
}
pub fn run(ctx: *zgui.te.TestContext) !void {
ctx.setRef("/Test Window");
ctx.windowFocus("");
ctx.itemAction(.click, "Click Me", .{}, null);
ctx.itemAction(.open, "Node", .{}, null);
ctx.itemAction(.check, "Node/Checkbox", .{}, null);
ctx.itemAction(.uncheck, "Node/Checkbox", .{}, null);
std.testing.expect(true) catch |err| {
zgui.te.checkTestError(@src(), err);
return;
};
}
},
);
}
```

112
build.zig
View File

@ -6,6 +6,7 @@ pub const Backend = enum {
glfw_opengl3, glfw_opengl3,
glfw_dx12, glfw_dx12,
win32_dx12, win32_dx12,
glfw,
}; };
pub fn build(b: *std.Build) void { pub fn build(b: *std.Build) void {
@ -24,6 +25,16 @@ pub fn build(b: *std.Build) void {
"with_implot", "with_implot",
"Build with bundled implot source", "Build with bundled implot source",
) orelse true, ) orelse true,
.with_te = b.option(
bool,
"with_te",
"Build with bundled test engine support",
) orelse false,
.use_wchar32 = b.option(
bool,
"use_wchar32",
"Extended unicode support",
) orelse false,
}; };
const options_step = b.addOptions(); const options_step = b.addOptions();
@ -34,7 +45,7 @@ pub fn build(b: *std.Build) void {
const options_module = options_step.createModule(); const options_module = options_step.createModule();
_ = b.addModule("root", .{ _ = b.addModule("root", .{
.root_source_file = .{ .path = "src/gui.zig" }, .root_source_file = b.path("src/gui.zig"),
.imports = &.{ .imports = &.{
.{ .name = "zgui_options", .module = options_module }, .{ .name = "zgui_options", .module = options_module },
}, },
@ -49,7 +60,6 @@ pub fn build(b: *std.Build) void {
.optimize = optimize, .optimize = optimize,
}); });
b.installArtifact(lib);
if (target.result.os.tag == .windows) { if (target.result.os.tag == .windows) {
lib.defineCMacro("IMGUI_API", "__declspec(dllexport)"); lib.defineCMacro("IMGUI_API", "__declspec(dllexport)");
lib.defineCMacro("IMPLOT_API", "__declspec(dllexport)"); lib.defineCMacro("IMPLOT_API", "__declspec(dllexport)");
@ -69,15 +79,15 @@ pub fn build(b: *std.Build) void {
b.installArtifact(imgui); b.installArtifact(imgui);
imgui.addIncludePath(.{ .path = "libs" }); imgui.addIncludePath(b.path("libs"));
imgui.addIncludePath(.{ .path = "libs/imgui" }); imgui.addIncludePath(b.path("libs/imgui"));
imgui.linkLibC(); imgui.linkLibC();
if (target.result.abi != .msvc) if (target.result.abi != .msvc)
imgui.linkLibCpp(); imgui.linkLibCpp();
imgui.addCSourceFile(.{ imgui.addCSourceFile(.{
.file = .{ .path = "src/zgui.cpp" }, .file = b.path("src/zgui.cpp"),
.flags = cflags, .flags = cflags,
}); });
@ -106,12 +116,79 @@ pub fn build(b: *std.Build) void {
imgui.defineCMacro("ZGUI_IMPLOT", "0"); imgui.defineCMacro("ZGUI_IMPLOT", "0");
} }
if (options.use_wchar32) {
imgui.defineCMacro("IMGUI_USE_WCHAR32", "1");
}
if (options.with_te) {
imgui.defineCMacro("ZGUI_TE", "1");
imgui.defineCMacro("IMGUI_ENABLE_TEST_ENGINE", null);
imgui.defineCMacro("IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL", "1");
imgui.addIncludePath(b.path("libs/imgui_test_engine/"));
imgui.addCSourceFile(.{ .file = b.path("libs/imgui_test_engine/imgui_capture_tool.cpp"), .flags = cflags });
imgui.addCSourceFile(.{ .file = b.path("libs/imgui_test_engine/imgui_te_context.cpp"), .flags = cflags });
imgui.addCSourceFile(.{ .file = b.path("libs/imgui_test_engine/imgui_te_coroutine.cpp"), .flags = cflags });
imgui.addCSourceFile(.{ .file = b.path("libs/imgui_test_engine/imgui_te_engine.cpp"), .flags = cflags });
imgui.addCSourceFile(.{ .file = b.path("libs/imgui_test_engine/imgui_te_exporters.cpp"), .flags = cflags });
imgui.addCSourceFile(.{ .file = b.path("libs/imgui_test_engine/imgui_te_perftool.cpp"), .flags = cflags });
imgui.addCSourceFile(.{ .file = b.path("libs/imgui_test_engine/imgui_te_ui.cpp"), .flags = cflags });
imgui.addCSourceFile(.{ .file = b.path("libs/imgui_test_engine/imgui_te_utils.cpp"), .flags = cflags });
// TODO: Workaround because zig on win64 doesn have phtreads
// TODO: Implement corutine in zig can solve this
if (target.result.os.tag == .windows) {
const src: []const []const u8 = &.{
"libs/winpthreads/src/nanosleep.c",
"libs/winpthreads/src/cond.c",
"libs/winpthreads/src/barrier.c",
"libs/winpthreads/src/misc.c",
"libs/winpthreads/src/clock.c",
"libs/winpthreads/src/libgcc/dll_math.c",
"libs/winpthreads/src/spinlock.c",
"libs/winpthreads/src/thread.c",
"libs/winpthreads/src/mutex.c",
"libs/winpthreads/src/sem.c",
"libs/winpthreads/src/sched.c",
"libs/winpthreads/src/ref.c",
"libs/winpthreads/src/rwlock.c",
};
const winpthreads = b.addStaticLibrary(.{
.name = "winpthreads",
.optimize = optimize,
.target = target,
});
winpthreads.want_lto = false;
winpthreads.root_module.sanitize_c = false;
if (optimize == .Debug or optimize == .ReleaseSafe)
winpthreads.bundle_compiler_rt = true
else
winpthreads.root_module.strip = true;
winpthreads.addCSourceFiles(.{ .files = src, .flags = &.{
"-Wall",
"-Wextra",
} });
winpthreads.defineCMacro("__USE_MINGW_ANSI_STDIO", "1");
winpthreads.addIncludePath(b.path("libs/winpthreads/include"));
winpthreads.addIncludePath(b.path("libs/winpthreads/src"));
winpthreads.linkLibC();
b.installArtifact(winpthreads);
imgui.linkLibrary(winpthreads);
imgui.addSystemIncludePath(b.path("libs/winpthreads/include"));
}
} else {
imgui.defineCMacro("ZGUI_TE", "0");
}
switch (options.backend) { switch (options.backend) {
.glfw_wgpu => { .glfw_wgpu => {
const zglfw = b.dependency("zglfw", .{}); const zglfw = b.dependency("zglfw", .{});
const zgpu = b.dependency("zgpu", .{}); const zgpu = b.dependency("zgpu", .{});
imgui.addIncludePath(.{ .path = zglfw.path("libs/glfw/include").getPath(b) }); imgui.addIncludePath(zglfw.path("libs/glfw/include"));
imgui.addIncludePath(.{ .path = zgpu.path("libs/dawn/include").getPath(b) }); imgui.addIncludePath(zgpu.path("libs/dawn/include"));
imgui.addCSourceFiles(.{ imgui.addCSourceFiles(.{
.files = &.{ .files = &.{
"libs/imgui/backends/imgui_impl_glfw.cpp", "libs/imgui/backends/imgui_impl_glfw.cpp",
@ -122,7 +199,7 @@ pub fn build(b: *std.Build) void {
}, },
.glfw_opengl3 => { .glfw_opengl3 => {
const zglfw = b.dependency("zglfw", .{}); const zglfw = b.dependency("zglfw", .{});
imgui.addIncludePath(.{ .path = zglfw.path("libs/glfw/include").getPath(b) }); imgui.addIncludePath(zglfw.path("libs/glfw/include"));
imgui.addCSourceFiles(.{ imgui.addCSourceFiles(.{
.files = &.{ .files = &.{
"libs/imgui/backends/imgui_impl_glfw.cpp", "libs/imgui/backends/imgui_impl_glfw.cpp",
@ -133,7 +210,7 @@ pub fn build(b: *std.Build) void {
}, },
.glfw_dx12 => { .glfw_dx12 => {
const zglfw = b.dependency("zglfw", .{}); const zglfw = b.dependency("zglfw", .{});
imgui.addIncludePath(.{ .path = zglfw.path("libs/glfw/include").getPath(b) }); imgui.addIncludePath(zglfw.path("libs/glfw/include"));
imgui.addCSourceFiles(.{ imgui.addCSourceFiles(.{
.files = &.{ .files = &.{
"libs/imgui/backends/imgui_impl_glfw.cpp", "libs/imgui/backends/imgui_impl_glfw.cpp",
@ -153,6 +230,21 @@ pub fn build(b: *std.Build) void {
}); });
imgui.linkSystemLibrary("d3dcompiler_47"); imgui.linkSystemLibrary("d3dcompiler_47");
imgui.linkSystemLibrary("dwmapi"); imgui.linkSystemLibrary("dwmapi");
switch (target.result.abi) {
.msvc => imgui.linkSystemLibrary("Gdi32"),
.gnu => imgui.linkSystemLibrary("gdi32"),
else => {},
}
},
.glfw => {
const zglfw = b.dependency("zglfw", .{});
imgui.addIncludePath(zglfw.path("libs/glfw/include"));
imgui.addCSourceFiles(.{
.files = &.{
"libs/imgui/backends/imgui_impl_glfw.cpp",
},
.flags = cflags,
});
}, },
.no_backend => {}, .no_backend => {},
} }
@ -161,7 +253,7 @@ pub fn build(b: *std.Build) void {
const tests = b.addTest(.{ const tests = b.addTest(.{
.name = "zgui-tests", .name = "zgui-tests",
.root_source_file = .{ .path = "src/gui.zig" }, .root_source_file = b.path("src/gui.zig"),
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
}); });

View File

@ -1,6 +1,6 @@
.{ .{
.name = "zgui", .name = "zgui",
.version = "0.1.0", .version = "0.2.0",
.paths = .{ .paths = .{
"build.zig", "build.zig",
"build.zig.zon", "build.zig.zon",

View File

@ -3,7 +3,9 @@
// Implemented features: // Implemented features:
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. // [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// FIXME: The transition from removing a viewport and moving the window in an existing hosted viewport tends to flicker.
// Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'. // Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*. // This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*.
@ -15,11 +17,15 @@
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // 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. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Learn about Dear ImGui:
// Read online: https://github.com/ocornut/imgui/tree/master/docs // - 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
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2021-05-19: DirectX12: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-05-19: DirectX12: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
@ -39,6 +45,7 @@
// 2018-02-22: Merged into master with all Win32 code synchronized to other examples. // 2018-02-22: Merged into master with all Win32 code synchronized to other examples.
#include "imgui.h" #include "imgui.h"
#ifndef IMGUI_DISABLE
#include "imgui_impl_dx12.h" #include "imgui_impl_dx12.h"
// DirectX // DirectX
@ -47,43 +54,22 @@
#include <d3dcompiler.h> #include <d3dcompiler.h>
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. #pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
#else // mziulek: PFN_D3D12_SERIALIZE_ROOT_SIGNATURE is not defined when using MinGW
typedef HRESULT (WINAPI* PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)(
const D3D12_ROOT_SIGNATURE_DESC* pRootSignature,
D3D_ROOT_SIGNATURE_VERSION Version,
ID3DBlob** ppBlob,
ID3DBlob** ppErrorBlob);
#endif #endif
// DirectX data // DirectX data
struct ImGui_ImplDX12_RenderBuffers
{
ID3D12Resource* IndexBuffer;
ID3D12Resource* VertexBuffer;
int IndexBufferSize;
int VertexBufferSize;
};
struct ImGui_ImplDX12_Data struct ImGui_ImplDX12_Data
{ {
ID3D12Device* pd3dDevice; ID3D12Device* pd3dDevice;
ID3D12RootSignature* pRootSignature; ID3D12RootSignature* pRootSignature;
ID3D12PipelineState* pPipelineState; ID3D12PipelineState* pPipelineState;
DXGI_FORMAT RTVFormat; DXGI_FORMAT RTVFormat;
ID3D12Resource* pFontTextureResource; ID3D12Resource* pFontTextureResource;
D3D12_CPU_DESCRIPTOR_HANDLE hFontSrvCpuDescHandle; D3D12_CPU_DESCRIPTOR_HANDLE hFontSrvCpuDescHandle;
D3D12_GPU_DESCRIPTOR_HANDLE hFontSrvGpuDescHandle; D3D12_GPU_DESCRIPTOR_HANDLE hFontSrvGpuDescHandle;
ID3D12DescriptorHeap* pd3dSrvDescHeap;
UINT numFramesInFlight;
ImGui_ImplDX12_RenderBuffers* pFrameResources; ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); }
UINT numFramesInFlight;
UINT frameIndex;
ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); frameIndex = UINT_MAX; }
};
struct VERTEX_CONSTANT_BUFFER_DX12
{
float mvp[4][4];
}; };
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts // Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
@ -93,6 +79,97 @@ static ImGui_ImplDX12_Data* ImGui_ImplDX12_GetBackendData()
return ImGui::GetCurrentContext() ? (ImGui_ImplDX12_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; return ImGui::GetCurrentContext() ? (ImGui_ImplDX12_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
} }
// Buffers used during the rendering of a frame
struct ImGui_ImplDX12_RenderBuffers
{
ID3D12Resource* IndexBuffer;
ID3D12Resource* VertexBuffer;
int IndexBufferSize;
int VertexBufferSize;
};
// Buffers used for secondary viewports created by the multi-viewports systems
struct ImGui_ImplDX12_FrameContext
{
ID3D12CommandAllocator* CommandAllocator;
ID3D12Resource* RenderTarget;
D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetCpuDescriptors;
};
// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data.
// Main viewport created by application will only use the Resources field.
// Secondary viewports created by this backend will use all the fields (including Window fields),
struct ImGui_ImplDX12_ViewportData
{
// Window
ID3D12CommandQueue* CommandQueue;
ID3D12GraphicsCommandList* CommandList;
ID3D12DescriptorHeap* RtvDescHeap;
IDXGISwapChain3* SwapChain;
ID3D12Fence* Fence;
UINT64 FenceSignaledValue;
HANDLE FenceEvent;
UINT NumFramesInFlight;
ImGui_ImplDX12_FrameContext* FrameCtx;
// Render buffers
UINT FrameIndex;
ImGui_ImplDX12_RenderBuffers* FrameRenderBuffers;
ImGui_ImplDX12_ViewportData(UINT num_frames_in_flight)
{
CommandQueue = nullptr;
CommandList = nullptr;
RtvDescHeap = nullptr;
SwapChain = nullptr;
Fence = nullptr;
FenceSignaledValue = 0;
FenceEvent = nullptr;
NumFramesInFlight = num_frames_in_flight;
FrameCtx = new ImGui_ImplDX12_FrameContext[NumFramesInFlight];
FrameIndex = UINT_MAX;
FrameRenderBuffers = new ImGui_ImplDX12_RenderBuffers[NumFramesInFlight];
for (UINT i = 0; i < NumFramesInFlight; ++i)
{
FrameCtx[i].CommandAllocator = nullptr;
FrameCtx[i].RenderTarget = nullptr;
// Create buffers with a default size (they will later be grown as needed)
FrameRenderBuffers[i].IndexBuffer = nullptr;
FrameRenderBuffers[i].VertexBuffer = nullptr;
FrameRenderBuffers[i].VertexBufferSize = 5000;
FrameRenderBuffers[i].IndexBufferSize = 10000;
}
}
~ImGui_ImplDX12_ViewportData()
{
IM_ASSERT(CommandQueue == nullptr && CommandList == nullptr);
IM_ASSERT(RtvDescHeap == nullptr);
IM_ASSERT(SwapChain == nullptr);
IM_ASSERT(Fence == nullptr);
IM_ASSERT(FenceEvent == nullptr);
for (UINT i = 0; i < NumFramesInFlight; ++i)
{
IM_ASSERT(FrameCtx[i].CommandAllocator == nullptr && FrameCtx[i].RenderTarget == nullptr);
IM_ASSERT(FrameRenderBuffers[i].IndexBuffer == nullptr && FrameRenderBuffers[i].VertexBuffer == nullptr);
}
delete[] FrameCtx; FrameCtx = nullptr;
delete[] FrameRenderBuffers; FrameRenderBuffers = nullptr;
}
};
struct VERTEX_CONSTANT_BUFFER_DX12
{
float mvp[4][4];
};
// Forward Declarations
static void ImGui_ImplDX12_InitPlatformInterface();
static void ImGui_ImplDX12_ShutdownPlatformInterface();
// Functions // Functions
static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx, ImGui_ImplDX12_RenderBuffers* fr) static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx, ImGui_ImplDX12_RenderBuffers* fr)
{ {
@ -166,11 +243,10 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return; return;
// FIXME: I'm assuming that this only gets called once per frame!
// If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator.
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
bd->frameIndex = bd->frameIndex + 1; ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)draw_data->OwnerViewport->RendererUserData;
ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[bd->frameIndex % bd->numFramesInFlight]; vd->FrameIndex++;
ImGui_ImplDX12_RenderBuffers* fr = &vd->FrameRenderBuffers[vd->FrameIndex % bd->numFramesInFlight];
// Create and grow vertex/index buffers if needed // Create and grow vertex/index buffers if needed
if (fr->VertexBuffer == nullptr || fr->VertexBufferSize < draw_data->TotalVtxCount) if (fr->VertexBuffer == nullptr || fr->VertexBufferSize < draw_data->TotalVtxCount)
@ -522,6 +598,19 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
return false; return false;
} }
// fix(zig-gamedev): TODO workaround from https://github.com/ocornut/imgui/pull/4604/files
#ifdef __MINGW32__
PFN_D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE D3D12SerializeVersionedRootSignatureFn =
(PFN_D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE)::GetProcAddress(d3d12_dll, "D3D12SerializeVersionedRootSignature");
if (D3D12SerializeVersionedRootSignatureFn == nullptr)
return false;
ID3DBlob* blob = nullptr;
D3D12_VERSIONED_ROOT_SIGNATURE_DESC versioned_desc = {.Version = D3D_ROOT_SIGNATURE_VERSION_1_0, .Desc_1_0 = desc};
if (D3D12SerializeVersionedRootSignatureFn(&versioned_desc, &blob, nullptr) != S_OK)
return false;
#else
PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignatureFn = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)::GetProcAddress(d3d12_dll, "D3D12SerializeRootSignature"); PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignatureFn = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)::GetProcAddress(d3d12_dll, "D3D12SerializeRootSignature");
if (D3D12SerializeRootSignatureFn == nullptr) if (D3D12SerializeRootSignatureFn == nullptr)
return false; return false;
@ -529,6 +618,7 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
ID3DBlob* blob = nullptr; ID3DBlob* blob = nullptr;
if (D3D12SerializeRootSignatureFn(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, nullptr) != S_OK) if (D3D12SerializeRootSignatureFn(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, nullptr) != S_OK)
return false; return false;
#endif
bd->pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignature)); bd->pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignature));
blob->Release(); blob->Release();
@ -591,9 +681,9 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
// Create the input layout // Create the input layout
static D3D12_INPUT_ELEMENT_DESC local_layout[] = static D3D12_INPUT_ELEMENT_DESC local_layout[] =
{ {
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(ImDrawVert, pos), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(ImDrawVert, uv), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)offsetof(ImDrawVert, col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
}; };
psoDesc.InputLayout = { local_layout, 3 }; psoDesc.InputLayout = { local_layout, 3 };
} }
@ -677,24 +767,24 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
return true; return true;
} }
static void ImGui_ImplDX12_DestroyRenderBuffers(ImGui_ImplDX12_RenderBuffers* render_buffers)
{
SafeRelease(render_buffers->IndexBuffer);
SafeRelease(render_buffers->VertexBuffer);
render_buffers->IndexBufferSize = render_buffers->VertexBufferSize = 0;
}
void ImGui_ImplDX12_InvalidateDeviceObjects() void ImGui_ImplDX12_InvalidateDeviceObjects()
{ {
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
if (!bd || !bd->pd3dDevice) if (!bd || !bd->pd3dDevice)
return; return;
ImGuiIO& io = ImGui::GetIO();
ImGuiIO& io = ImGui::GetIO();
SafeRelease(bd->pRootSignature); SafeRelease(bd->pRootSignature);
SafeRelease(bd->pPipelineState); SafeRelease(bd->pPipelineState);
SafeRelease(bd->pFontTextureResource); SafeRelease(bd->pFontTextureResource);
io.Fonts->SetTexID(0); // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well. io.Fonts->SetTexID(0); // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well.
for (UINT i = 0; i < bd->numFramesInFlight; i++)
{
ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[i];
SafeRelease(fr->IndexBuffer);
SafeRelease(fr->VertexBuffer);
}
} }
bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap, bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap,
@ -708,25 +798,21 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO
io.BackendRendererUserData = (void*)bd; io.BackendRendererUserData = (void*)bd;
io.BackendRendererName = "imgui_impl_dx12"; io.BackendRendererName = "imgui_impl_dx12";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
ImGui_ImplDX12_InitPlatformInterface();
bd->pd3dDevice = device; bd->pd3dDevice = device;
bd->RTVFormat = rtv_format; bd->RTVFormat = rtv_format;
bd->hFontSrvCpuDescHandle = font_srv_cpu_desc_handle; bd->hFontSrvCpuDescHandle = font_srv_cpu_desc_handle;
bd->hFontSrvGpuDescHandle = font_srv_gpu_desc_handle; bd->hFontSrvGpuDescHandle = font_srv_gpu_desc_handle;
bd->pFrameResources = new ImGui_ImplDX12_RenderBuffers[num_frames_in_flight];
bd->numFramesInFlight = num_frames_in_flight; bd->numFramesInFlight = num_frames_in_flight;
bd->frameIndex = UINT_MAX; bd->pd3dSrvDescHeap = cbv_srv_heap;
IM_UNUSED(cbv_srv_heap); // Unused in master branch (will be used by multi-viewports)
// Create buffers with a default size (they will later be grown as needed) // Create a dummy ImGui_ImplDX12_ViewportData holder for the main viewport,
for (int i = 0; i < num_frames_in_flight; i++) // Since this is created and managed by the application, we will only use the ->Resources[] fields.
{ ImGuiViewport* main_viewport = ImGui::GetMainViewport();
ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[i]; main_viewport->RendererUserData = IM_NEW(ImGui_ImplDX12_ViewportData)(bd->numFramesInFlight);
fr->IndexBuffer = nullptr;
fr->VertexBuffer = nullptr;
fr->IndexBufferSize = 10000;
fr->VertexBufferSize = 5000;
}
return true; return true;
} }
@ -737,10 +823,24 @@ void ImGui_ImplDX12_Shutdown()
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
// Manually delete main viewport render resources in-case we haven't initialized for viewports
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
if (ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)main_viewport->RendererUserData)
{
// We could just call ImGui_ImplDX12_DestroyWindow(main_viewport) as a convenience but that would be misleading since we only use data->Resources[]
for (UINT i = 0; i < bd->numFramesInFlight; i++)
ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]);
IM_DELETE(vd);
main_viewport->RendererUserData = nullptr;
}
// Clean up windows and device objects
ImGui_ImplDX12_ShutdownPlatformInterface();
ImGui_ImplDX12_InvalidateDeviceObjects(); ImGui_ImplDX12_InvalidateDeviceObjects();
delete[] bd->pFrameResources;
io.BackendRendererName = nullptr; io.BackendRendererName = nullptr;
io.BackendRendererUserData = nullptr; io.BackendRendererUserData = nullptr;
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasViewports);
IM_DELETE(bd); IM_DELETE(bd);
} }
@ -752,3 +852,247 @@ void ImGui_ImplDX12_NewFrame()
if (!bd->pPipelineState) if (!bd->pPipelineState)
ImGui_ImplDX12_CreateDeviceObjects(); ImGui_ImplDX12_CreateDeviceObjects();
} }
//--------------------------------------------------------------------------------------------------------
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
//--------------------------------------------------------------------------------------------------------
static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport)
{
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
ImGui_ImplDX12_ViewportData* vd = IM_NEW(ImGui_ImplDX12_ViewportData)(bd->numFramesInFlight);
viewport->RendererUserData = vd;
// PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*).
// Some backends will leave PlatformHandleRaw == 0, in which case we assume PlatformHandle will contain the HWND.
HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle;
IM_ASSERT(hwnd != 0);
vd->FrameIndex = UINT_MAX;
// Create command queue.
D3D12_COMMAND_QUEUE_DESC queue_desc = {};
queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
HRESULT res = S_OK;
res = bd->pd3dDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&vd->CommandQueue));
IM_ASSERT(res == S_OK);
// Create command allocator.
for (UINT i = 0; i < bd->numFramesInFlight; ++i)
{
res = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&vd->FrameCtx[i].CommandAllocator));
IM_ASSERT(res == S_OK);
}
// Create command list.
res = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, vd->FrameCtx[0].CommandAllocator, nullptr, IID_PPV_ARGS(&vd->CommandList));
IM_ASSERT(res == S_OK);
vd->CommandList->Close();
// Create fence.
res = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&vd->Fence));
IM_ASSERT(res == S_OK);
vd->FenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
IM_ASSERT(vd->FenceEvent != nullptr);
// Create swap chain
// FIXME-VIEWPORT: May want to copy/inherit swap chain settings from the user/application.
DXGI_SWAP_CHAIN_DESC1 sd1;
ZeroMemory(&sd1, sizeof(sd1));
sd1.BufferCount = bd->numFramesInFlight;
sd1.Width = (UINT)viewport->Size.x;
sd1.Height = (UINT)viewport->Size.y;
sd1.Format = bd->RTVFormat;
sd1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd1.SampleDesc.Count = 1;
sd1.SampleDesc.Quality = 0;
sd1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
sd1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
sd1.Scaling = DXGI_SCALING_NONE;
sd1.Stereo = FALSE;
IDXGIFactory4* dxgi_factory = nullptr;
res = ::CreateDXGIFactory1(IID_PPV_ARGS(&dxgi_factory));
IM_ASSERT(res == S_OK);
IDXGISwapChain1* swap_chain = nullptr;
res = dxgi_factory->CreateSwapChainForHwnd(vd->CommandQueue, hwnd, &sd1, nullptr, nullptr, &swap_chain);
IM_ASSERT(res == S_OK);
dxgi_factory->Release();
// Or swapChain.As(&mSwapChain)
IM_ASSERT(vd->SwapChain == nullptr);
swap_chain->QueryInterface(IID_PPV_ARGS(&vd->SwapChain));
swap_chain->Release();
// Create the render targets
if (vd->SwapChain)
{
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
desc.NumDescriptors = bd->numFramesInFlight;
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
desc.NodeMask = 1;
HRESULT hr = bd->pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&vd->RtvDescHeap));
IM_ASSERT(hr == S_OK);
SIZE_T rtv_descriptor_size = bd->pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = vd->RtvDescHeap->GetCPUDescriptorHandleForHeapStart();
for (UINT i = 0; i < bd->numFramesInFlight; i++)
{
vd->FrameCtx[i].RenderTargetCpuDescriptors = rtv_handle;
rtv_handle.ptr += rtv_descriptor_size;
}
ID3D12Resource* back_buffer;
for (UINT i = 0; i < bd->numFramesInFlight; i++)
{
IM_ASSERT(vd->FrameCtx[i].RenderTarget == nullptr);
vd->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer));
bd->pd3dDevice->CreateRenderTargetView(back_buffer, nullptr, vd->FrameCtx[i].RenderTargetCpuDescriptors);
vd->FrameCtx[i].RenderTarget = back_buffer;
}
}
for (UINT i = 0; i < bd->numFramesInFlight; i++)
ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]);
}
static void ImGui_WaitForPendingOperations(ImGui_ImplDX12_ViewportData* vd)
{
HRESULT hr = S_FALSE;
if (vd && vd->CommandQueue && vd->Fence && vd->FenceEvent)
{
hr = vd->CommandQueue->Signal(vd->Fence, ++vd->FenceSignaledValue);
IM_ASSERT(hr == S_OK);
::WaitForSingleObject(vd->FenceEvent, 0); // Reset any forgotten waits
hr = vd->Fence->SetEventOnCompletion(vd->FenceSignaledValue, vd->FenceEvent);
IM_ASSERT(hr == S_OK);
::WaitForSingleObject(vd->FenceEvent, INFINITE);
}
}
static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport)
{
// The main viewport (owned by the application) will always have RendererUserData == 0 since we didn't create the data for it.
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
if (ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData)
{
ImGui_WaitForPendingOperations(vd);
SafeRelease(vd->CommandQueue);
SafeRelease(vd->CommandList);
SafeRelease(vd->SwapChain);
SafeRelease(vd->RtvDescHeap);
SafeRelease(vd->Fence);
::CloseHandle(vd->FenceEvent);
vd->FenceEvent = nullptr;
for (UINT i = 0; i < bd->numFramesInFlight; i++)
{
SafeRelease(vd->FrameCtx[i].RenderTarget);
SafeRelease(vd->FrameCtx[i].CommandAllocator);
ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]);
}
IM_DELETE(vd);
}
viewport->RendererUserData = nullptr;
}
static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
{
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData;
ImGui_WaitForPendingOperations(vd);
for (UINT i = 0; i < bd->numFramesInFlight; i++)
SafeRelease(vd->FrameCtx[i].RenderTarget);
if (vd->SwapChain)
{
ID3D12Resource* back_buffer = nullptr;
vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0);
for (UINT i = 0; i < bd->numFramesInFlight; i++)
{
vd->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer));
bd->pd3dDevice->CreateRenderTargetView(back_buffer, nullptr, vd->FrameCtx[i].RenderTargetCpuDescriptors);
vd->FrameCtx[i].RenderTarget = back_buffer;
}
}
}
static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*)
{
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData;
ImGui_ImplDX12_FrameContext* frame_context = &vd->FrameCtx[vd->FrameIndex % bd->numFramesInFlight];
UINT back_buffer_idx = vd->SwapChain->GetCurrentBackBufferIndex();
const ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource = vd->FrameCtx[back_buffer_idx].RenderTarget;
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
// Draw
ID3D12GraphicsCommandList* cmd_list = vd->CommandList;
frame_context->CommandAllocator->Reset();
cmd_list->Reset(frame_context->CommandAllocator, nullptr);
cmd_list->ResourceBarrier(1, &barrier);
cmd_list->OMSetRenderTargets(1, &vd->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, FALSE, nullptr);
if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
cmd_list->ClearRenderTargetView(vd->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, (float*)&clear_color, 0, nullptr);
cmd_list->SetDescriptorHeaps(1, &bd->pd3dSrvDescHeap);
ImGui_ImplDX12_RenderDrawData(viewport->DrawData, cmd_list);
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
cmd_list->ResourceBarrier(1, &barrier);
cmd_list->Close();
vd->CommandQueue->Wait(vd->Fence, vd->FenceSignaledValue);
vd->CommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmd_list);
vd->CommandQueue->Signal(vd->Fence, ++vd->FenceSignaledValue);
}
static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport, void*)
{
ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData;
vd->SwapChain->Present(0, 0);
while (vd->Fence->GetCompletedValue() < vd->FenceSignaledValue)
::SwitchToThread();
}
void ImGui_ImplDX12_InitPlatformInterface()
{
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Renderer_CreateWindow = ImGui_ImplDX12_CreateWindow;
platform_io.Renderer_DestroyWindow = ImGui_ImplDX12_DestroyWindow;
platform_io.Renderer_SetWindowSize = ImGui_ImplDX12_SetWindowSize;
platform_io.Renderer_RenderWindow = ImGui_ImplDX12_RenderWindow;
platform_io.Renderer_SwapBuffers = ImGui_ImplDX12_SwapBuffers;
}
void ImGui_ImplDX12_ShutdownPlatformInterface()
{
ImGui::DestroyPlatformWindows();
}
//-----------------------------------------------------------------------------
#endif // #ifndef IMGUI_DISABLE

View File

@ -3,18 +3,23 @@
// Implemented features: // Implemented features:
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. // [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'. // Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
// See imgui_impl_dx12.cpp file for details. // See imgui_impl_dx12.cpp file for details.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // 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. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Learn about Dear ImGui:
// Read online: https://github.com/ocornut/imgui/tree/master/docs // - 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
#pragma once #pragma once
#include "imgui.h" // IMGUI_IMPL_API #include "imgui.h" // IMGUI_IMPL_API
#ifndef IMGUI_DISABLE
#include <dxgiformat.h> // DXGI_FORMAT #include <dxgiformat.h> // DXGI_FORMAT
struct ID3D12Device; struct ID3D12Device;
@ -23,7 +28,8 @@ struct ID3D12GraphicsCommandList;
struct D3D12_CPU_DESCRIPTOR_HANDLE; struct D3D12_CPU_DESCRIPTOR_HANDLE;
struct D3D12_GPU_DESCRIPTOR_HANDLE; struct D3D12_GPU_DESCRIPTOR_HANDLE;
extern "C" { // mziulek // FIX(zig-gamedev)
extern "C" {
// cmd_list is the command list that the implementation will use to render imgui draw lists. // cmd_list is the command list that the implementation will use to render imgui draw lists.
// Before calling the render function, caller must prepare cmd_list by resetting it and setting the appropriate // Before calling the render function, caller must prepare cmd_list by resetting it and setting the appropriate
@ -40,3 +46,5 @@ IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects();
IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects(); IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects();
} }
#endif // #ifndef IMGUI_DISABLE

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,63 @@
// dear imgui: Platform Backend for GLFW
// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..)
// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)
// (Requires: GLFW 3.1+. Prefer GLFW 3.3+ for full feature support.)
// Implemented features:
// [X] Platform: Clipboard support.
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only).
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// Issues:
// [ ] Platform: Multi-viewport support: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor).
// 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
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
#ifndef IMGUI_DISABLE
struct GLFWwindow;
struct GLFWmonitor;
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks);
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks);
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks);
IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown();
IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame();
// Emscripten related initialization phase methods
#ifdef __EMSCRIPTEN__
IMGUI_IMPL_API void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector);
#endif
// GLFW callbacks install
// - When calling Init with 'install_callbacks=true': ImGui_ImplGlfw_InstallCallbacks() is called. GLFW callbacks will be installed for you. They will chain-call user's previously installed callbacks, if any.
// - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call individual function yourself from your own GLFW callbacks.
IMGUI_IMPL_API void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window);
IMGUI_IMPL_API void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window);
// GFLW callbacks options:
// - Set 'chain_for_all_windows=true' to enable chaining callbacks for all windows (including secondary viewports created by backends or by user)
IMGUI_IMPL_API void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows);
// GLFW callbacks (individual callbacks to call yourself if you didn't install callbacks)
IMGUI_IMPL_API void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); // Since 1.84
IMGUI_IMPL_API void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); // Since 1.84
IMGUI_IMPL_API void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y); // Since 1.87
IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c);
IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event);
#endif // #ifndef IMGUI_DISABLE

View File

@ -14,11 +14,17 @@
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // 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. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Learn about Dear ImGui:
// Read online: https://github.com/ocornut/imgui/tree/master/docs // - 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
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2024-01-09: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" and variants, fixing regression on distros missing a symlink.
// 2023-11-08: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" instead of "libGL.so.1", accommodating for NetBSD systems having only "libGL.so.3" available. (#6983)
// 2023-10-05: OpenGL: Rename symbols in our internal loader so that LTO compilation with another copy of gl3w is possible. (#6875, #6668, #4445)
// 2023-06-20: OpenGL: Fixed erroneous use glGetIntegerv(GL_CONTEXT_PROFILE_MASK) on contexts lower than 3.2. (#6539, #6333) // 2023-06-20: OpenGL: Fixed erroneous use glGetIntegerv(GL_CONTEXT_PROFILE_MASK) on contexts lower than 3.2. (#6539, #6333)
// 2023-05-09: OpenGL: Support for glBindSampler() backup/restore on ES3. (#6375) // 2023-05-09: OpenGL: Support for glBindSampler() backup/restore on ES3. (#6375)
// 2023-04-18: OpenGL: Restore front and back polygon mode separately when supported by context. (#6333) // 2023-04-18: OpenGL: Restore front and back polygon mode separately when supported by context. (#6333)
@ -103,9 +109,8 @@
#define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS
#endif #endif
#ifndef IMGUI_DISABLE
#include "imgui.h" #include "imgui.h"
#ifndef IMGUI_DISABLE
#include "imgui_impl_opengl3.h" #include "imgui_impl_opengl3.h"
#include <stdio.h> #include <stdio.h>
#include <stdint.h> // intptr_t #include <stdint.h> // intptr_t
@ -175,9 +180,20 @@ extern "C" {
#define GL_VERTEX_ARRAY_BINDING GL_VERTEX_ARRAY_BINDING_OES #define GL_VERTEX_ARRAY_BINDING GL_VERTEX_ARRAY_BINDING_OES
#endif #endif
// Desktop GL 2.0+ has glPolygonMode() which GL ES and WebGL don't have. // Desktop GL 2.0+ has extension and glPolygonMode() which GL ES and WebGL don't have..
#ifdef GL_POLYGON_MODE #if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)
#define IMGUI_IMPL_HAS_POLYGON_MODE #define IMGUI_IMPL_OPENGL_HAS_EXTENSIONS // has glGetIntegerv(GL_NUM_EXTENSIONS)
#define IMGUI_IMPL_OPENGL_HAS_POLYGON_MODE // has glPolygonMode()
#endif
// Desktop GL 2.1+ and GL ES 3.0+ have glBindBuffer() with GL_PIXEL_UNPACK_BUFFER target.
#if !defined(IMGUI_IMPL_OPENGL_ES2)
#define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK
#endif
// Desktop GL 3.1+ has GL_PRIMITIVE_RESTART state
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_1)
#define IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
#endif #endif
// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have. // Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
@ -190,16 +206,6 @@ extern "C" {
#define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER #define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
#endif #endif
// Desktop GL 3.1+ has GL_PRIMITIVE_RESTART state
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_1)
#define IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
#endif
// Desktop GL use extension detection
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)
#define IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS
#endif
// [Debugging] // [Debugging]
//#define IMGUI_IMPL_OPENGL_DEBUG //#define IMGUI_IMPL_OPENGL_DEBUG
#ifdef IMGUI_IMPL_OPENGL_DEBUG #ifdef IMGUI_IMPL_OPENGL_DEBUG
@ -272,13 +278,13 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
// Initialize our loader // Initialize our loader
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) #if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
if (imgl3wInit() != 0) if (imgl3wInit() != 0)
{ {
fprintf(stderr, "Failed to initialize OpenGL loader!\n"); fprintf(stderr, "Failed to initialize OpenGL loader!\n");
return false; return false;
} }
#endif #endif
// Setup backend capabilities flags // Setup backend capabilities flags
ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)(); ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)();
@ -358,7 +364,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
// Detect extensions we support // Detect extensions we support
bd->HasClipOrigin = (bd->GlVersion >= 450); bd->HasClipOrigin = (bd->GlVersion >= 450);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS #ifdef IMGUI_IMPL_OPENGL_HAS_EXTENSIONS
GLint num_extensions = 0; GLint num_extensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
for (GLint i = 0; i < num_extensions; i++) for (GLint i = 0; i < num_extensions; i++)
@ -410,7 +416,7 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
if (bd->GlVersion >= 310) if (bd->GlVersion >= 310)
glDisable(GL_PRIMITIVE_RESTART); glDisable(GL_PRIMITIVE_RESTART);
#endif #endif
#ifdef IMGUI_IMPL_HAS_POLYGON_MODE #ifdef IMGUI_IMPL_OPENGL_HAS_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif #endif
@ -462,9 +468,9 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxPos)); GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxPos));
GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxUV)); GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxUV));
GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxColor)); GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxColor));
GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos))); GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, pos)));
GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv))); GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, uv)));
GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col))); GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, col)));
} }
// OpenGL3 Render function. // OpenGL3 Render function.
@ -499,7 +505,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY #ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object); GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object);
#endif #endif
#ifdef IMGUI_IMPL_HAS_POLYGON_MODE #ifdef IMGUI_IMPL_OPENGL_HAS_POLYGON_MODE
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
#endif #endif
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
@ -638,7 +644,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
if (bd->GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); } if (bd->GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); }
#endif #endif
#ifdef IMGUI_IMPL_HAS_POLYGON_MODE #ifdef IMGUI_IMPL_OPENGL_HAS_POLYGON_MODE
// Desktop OpenGL 3.0 and OpenGL 3.1 had separate polygon draw modes for front-facing and back-facing faces of polygons // Desktop OpenGL 3.0 and OpenGL 3.1 had separate polygon draw modes for front-facing and back-facing faces of polygons
if (bd->GlVersion <= 310 || bd->GlProfileIsCompat) if (bd->GlVersion <= 310 || bd->GlProfileIsCompat)
{ {
@ -649,7 +655,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
{ {
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
} }
#endif // IMGUI_IMPL_HAS_POLYGON_MODE #endif // IMGUI_IMPL_OPENGL_HAS_POLYGON_MODE
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
@ -746,6 +752,10 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
GLint last_texture, last_array_buffer; GLint last_texture, last_array_buffer;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK
GLint last_pixel_unpack_buffer;
if (bd->GlVersion >= 210) { glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &last_pixel_unpack_buffer); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); }
#endif
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY #ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
GLint last_vertex_array; GLint last_vertex_array;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
@ -919,6 +929,9 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
// Restore modified GL state // Restore modified GL state
glBindTexture(GL_TEXTURE_2D, last_texture); glBindTexture(GL_TEXTURE_2D, last_texture);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK
if (bd->GlVersion >= 210) { glBindBuffer(GL_PIXEL_UNPACK_BUFFER, last_pixel_unpack_buffer); }
#endif
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY #ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
glBindVertexArray(last_vertex_array); glBindVertexArray(last_vertex_array);
#endif #endif

View File

@ -14,8 +14,11 @@
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // 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. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Learn about Dear ImGui:
// Read online: https://github.com/ocornut/imgui/tree/master/docs // - 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
// About GLSL version: // About GLSL version:
// The 'glsl_version' initialization parameter should be nullptr (default) or a "#version XXX" string. // The 'glsl_version' initialization parameter should be nullptr (default) or a "#version XXX" string.
@ -26,20 +29,19 @@
#include "imgui.h" // IMGUI_IMPL_API #include "imgui.h" // IMGUI_IMPL_API
#ifndef IMGUI_DISABLE #ifndef IMGUI_DISABLE
// FIX(zig-gamedev)
extern "C" { extern "C" {
// Backend API
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr);
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
// Backend API // (Optional) Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr); IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
// (Optional) Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
} }
// Specific OpenGL ES versions // Specific OpenGL ES versions

View File

@ -1,5 +1,3 @@
// zig-gamedev changes marked with `FIX(zig-gamedev)`
// dear imgui: Renderer for WebGPU // dear imgui: Renderer for WebGPU
// This needs to be used along with a Platform Binding (e.g. GLFW) // This needs to be used along with a Platform Binding (e.g. GLFW)
// (Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break.) // (Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break.)
@ -7,14 +5,24 @@
// Implemented features: // Implemented features:
// [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. // [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
// Missing features:
// [ ] Renderer: Multi-viewport support (multiple windows). Not meaningful on the web.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // 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. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Learn about Dear ImGui:
// Read online: https://github.com/ocornut/imgui/tree/master/docs // - 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
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2024-01-22: Added configurable PipelineMultisampleState struct. (#7240)
// 2024-01-22: (Breaking) ImGui_ImplWGPU_Init() now takes a ImGui_ImplWGPU_InitInfo structure instead of variety of parameters, allowing for easier further changes.
// 2024-01-22: Fixed pipeline layout leak. (#7245)
// 2024-01-17: Explicitly fill all of WGPUDepthStencilState since standard removed defaults.
// 2023-07-13: Use WGPUShaderModuleWGSLDescriptor's code instead of source. use WGPUMipmapFilterMode_Linear instead of WGPUFilterMode_Linear. (#6602)
// 2023-04-11: Align buffer sizes. Use WGSL shaders instead of precompiled SPIR-V. // 2023-04-11: Align buffer sizes. Use WGSL shaders instead of precompiled SPIR-V.
// 2023-04-11: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2023-04-11: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2023-01-25: Revert automatic pipeline layout generation (see https://github.com/gpuweb/gpuweb/issues/2470) // 2023-01-25: Revert automatic pipeline layout generation (see https://github.com/gpuweb/gpuweb/issues/2470)
@ -30,6 +38,11 @@
// 2021-01-28: Initial version. // 2021-01-28: Initial version.
#include "imgui.h" #include "imgui.h"
#ifndef IMGUI_DISABLE
// FIX(zig-gamedev):
//#include "imgui_impl_wgpu.h"
#include <limits.h> #include <limits.h>
#include <webgpu/webgpu.h> #include <webgpu/webgpu.h>
@ -40,14 +53,31 @@ extern ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed = 0);
// FIX(zig-gamedev): We removed header file and declare all our external functions here. // FIX(zig-gamedev): We removed header file and declare all our external functions here.
extern "C" { extern "C" {
IMGUI_IMPL_API bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format, WGPUTextureFormat depth_format = WGPUTextureFormat_Undefined); // Initialization data, for ImGui_ImplWGPU_Init()
IMGUI_IMPL_API void ImGui_ImplWGPU_Shutdown(void); struct ImGui_ImplWGPU_InitInfo
IMGUI_IMPL_API void ImGui_ImplWGPU_NewFrame(void); {
WGPUDevice Device;
int NumFramesInFlight = 3;
WGPUTextureFormat RenderTargetFormat = WGPUTextureFormat_Undefined;
WGPUTextureFormat DepthStencilFormat = WGPUTextureFormat_Undefined;
WGPUMultisampleState PipelineMultisampleState = {};
ImGui_ImplWGPU_InitInfo()
{
PipelineMultisampleState.count = 1;
PipelineMultisampleState.mask = -1u;
PipelineMultisampleState.alphaToCoverageEnabled = false;
}
};
IMGUI_IMPL_API bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info);
IMGUI_IMPL_API void ImGui_ImplWGPU_Shutdown();
IMGUI_IMPL_API void ImGui_ImplWGPU_NewFrame();
IMGUI_IMPL_API void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder); IMGUI_IMPL_API void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder);
// Use if you want to reset your rendering device without losing Dear ImGui state. // Use if you want to reset your rendering device without losing Dear ImGui state.
IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects(void); IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects();
IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects(void); IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects();
} // extern "C" } // extern "C"
@ -82,16 +112,17 @@ struct Uniforms
struct ImGui_ImplWGPU_Data struct ImGui_ImplWGPU_Data
{ {
WGPUDevice wgpuDevice = nullptr; ImGui_ImplWGPU_InitInfo initInfo;
WGPUQueue defaultQueue = nullptr; WGPUDevice wgpuDevice = nullptr;
WGPUTextureFormat renderTargetFormat = WGPUTextureFormat_Undefined; WGPUQueue defaultQueue = nullptr;
WGPUTextureFormat depthStencilFormat = WGPUTextureFormat_Undefined; WGPUTextureFormat renderTargetFormat = WGPUTextureFormat_Undefined;
WGPURenderPipeline pipelineState = nullptr; WGPUTextureFormat depthStencilFormat = WGPUTextureFormat_Undefined;
WGPURenderPipeline pipelineState = nullptr;
RenderResources renderResources; RenderResources renderResources;
FrameResources* pFrameResources = nullptr; FrameResources* pFrameResources = nullptr;
unsigned int numFramesInFlight = 0; unsigned int numFramesInFlight = 0;
unsigned int frameIndex = UINT_MAX; unsigned int frameIndex = UINT_MAX;
}; };
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts // Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
@ -189,13 +220,12 @@ static void SafeRelease(WGPUBuffer& res)
wgpuBufferRelease(res); wgpuBufferRelease(res);
res = nullptr; res = nullptr;
} }
// FIX(zig-gamedev): https://github.com/ocornut/imgui/commit/9266c0d2d1390e50d2d8070896932c2564594407
static void SafeRelease(WGPUPipelineLayout& res) static void SafeRelease(WGPUPipelineLayout& res)
{ {
if (res) if (res)
wgpuPipelineLayoutRelease(res); wgpuPipelineLayoutRelease(res);
res = nullptr; res = nullptr;
} }
static void SafeRelease(WGPURenderPipeline& res) static void SafeRelease(WGPURenderPipeline& res)
{ {
if (res) if (res)
@ -252,7 +282,6 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const c
WGPUShaderModuleWGSLDescriptor wgsl_desc = {}; WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor; wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
// FiX(zig-gamedev): `.source` renamed to `.code`
wgsl_desc.code = wgsl_source; wgsl_desc.code = wgsl_source;
WGPUShaderModuleDescriptor desc = {}; WGPUShaderModuleDescriptor desc = {};
@ -347,7 +376,9 @@ static void ImGui_ImplWGPU_SetupRenderState(ImDrawData* draw_data, WGPURenderPas
void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder) void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder)
{ {
// Avoid rendering when minimized // Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (fb_width <= 0 || fb_height <= 0 || draw_data->CmdListsCount == 0)
return; return;
// FIXME: Assuming that this only gets called once per frame! // FIXME: Assuming that this only gets called once per frame!
@ -466,15 +497,15 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
// Project scissor/clipping rectangles into framebuffer space // Project scissor/clipping rectangles into framebuffer space
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
// Clamp to viewport as wgpuRenderPassEncoderSetScissorRect() won't accept values that are off bounds
if (clip_min.x < 0.0f) { clip_min.x = 0.0f; }
if (clip_min.y < 0.0f) { clip_min.y = 0.0f; }
if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; }
if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; }
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
continue; continue;
// FIX(zig-gamedev): Fixes 'Popups and Modal windows->Modals->Stacked modals..' from showDemoWindow().
if (clip_min.x < 0.0f) clip_min.x = 0.0f;
if (clip_min.y < 0.0f) clip_min.y = 0.0f;
if (clip_max.x > draw_data->DisplaySize.x) clip_max.x = draw_data->DisplaySize.x;
if (clip_max.y > draw_data->DisplaySize.y) clip_max.y = draw_data->DisplaySize.y;
// Apply scissor/clipping rectangle, Draw // Apply scissor/clipping rectangle, Draw
wgpuRenderPassEncoderSetScissorRect(pass_encoder, (uint32_t)clip_min.x, (uint32_t)clip_min.y, (uint32_t)(clip_max.x - clip_min.x), (uint32_t)(clip_max.y - clip_min.y)); wgpuRenderPassEncoderSetScissorRect(pass_encoder, (uint32_t)clip_min.x, (uint32_t)clip_min.y, (uint32_t)(clip_max.x - clip_min.x), (uint32_t)(clip_max.y - clip_min.y));
wgpuRenderPassEncoderDrawIndexed(pass_encoder, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); wgpuRenderPassEncoderDrawIndexed(pass_encoder, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
@ -540,7 +571,6 @@ static void ImGui_ImplWGPU_CreateFontsTexture()
WGPUSamplerDescriptor sampler_desc = {}; WGPUSamplerDescriptor sampler_desc = {};
sampler_desc.minFilter = WGPUFilterMode_Linear; sampler_desc.minFilter = WGPUFilterMode_Linear;
sampler_desc.magFilter = WGPUFilterMode_Linear; sampler_desc.magFilter = WGPUFilterMode_Linear;
// FIX(zig-gamedev): WGPUFilterMode_Linear should be WGPUMipmapFilterMode_Linear
sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Linear; sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Linear;
sampler_desc.addressModeU = WGPUAddressMode_Repeat; sampler_desc.addressModeU = WGPUAddressMode_Repeat;
sampler_desc.addressModeV = WGPUAddressMode_Repeat; sampler_desc.addressModeV = WGPUAddressMode_Repeat;
@ -582,9 +612,7 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
graphics_pipeline_desc.primitive.stripIndexFormat = WGPUIndexFormat_Undefined; graphics_pipeline_desc.primitive.stripIndexFormat = WGPUIndexFormat_Undefined;
graphics_pipeline_desc.primitive.frontFace = WGPUFrontFace_CW; graphics_pipeline_desc.primitive.frontFace = WGPUFrontFace_CW;
graphics_pipeline_desc.primitive.cullMode = WGPUCullMode_None; graphics_pipeline_desc.primitive.cullMode = WGPUCullMode_None;
graphics_pipeline_desc.multisample.count = 1; graphics_pipeline_desc.multisample = bd->initInfo.PipelineMultisampleState;
graphics_pipeline_desc.multisample.mask = UINT_MAX;
graphics_pipeline_desc.multisample.alphaToCoverageEnabled = false;
// Bind group layouts // Bind group layouts
WGPUBindGroupLayoutEntry common_bg_layout_entries[2] = {}; WGPUBindGroupLayoutEntry common_bg_layout_entries[2] = {};
@ -626,9 +654,9 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
// Vertex input configuration // Vertex input configuration
WGPUVertexAttribute attribute_desc[] = WGPUVertexAttribute attribute_desc[] =
{ {
{ WGPUVertexFormat_Float32x2, (uint64_t)IM_OFFSETOF(ImDrawVert, pos), 0 }, { WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, pos), 0 },
{ WGPUVertexFormat_Float32x2, (uint64_t)IM_OFFSETOF(ImDrawVert, uv), 1 }, { WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, uv), 1 },
{ WGPUVertexFormat_Unorm8x4, (uint64_t)IM_OFFSETOF(ImDrawVert, col), 2 }, { WGPUVertexFormat_Unorm8x4, (uint64_t)offsetof(ImDrawVert, col), 2 },
}; };
WGPUVertexBufferLayout buffer_layouts[1]; WGPUVertexBufferLayout buffer_layouts[1];
@ -671,12 +699,10 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
depth_stencil_state.depthWriteEnabled = false; depth_stencil_state.depthWriteEnabled = false;
depth_stencil_state.depthCompare = WGPUCompareFunction_Always; depth_stencil_state.depthCompare = WGPUCompareFunction_Always;
depth_stencil_state.stencilFront.compare = WGPUCompareFunction_Always; depth_stencil_state.stencilFront.compare = WGPUCompareFunction_Always;
// FIX(zig-gamedev): https://github.com/ocornut/imgui/commit/03417cc77d15100b18c486b55db409ee5e9c363e
depth_stencil_state.stencilFront.failOp = WGPUStencilOperation_Keep; depth_stencil_state.stencilFront.failOp = WGPUStencilOperation_Keep;
depth_stencil_state.stencilFront.depthFailOp = WGPUStencilOperation_Keep; depth_stencil_state.stencilFront.depthFailOp = WGPUStencilOperation_Keep;
depth_stencil_state.stencilFront.passOp = WGPUStencilOperation_Keep; depth_stencil_state.stencilFront.passOp = WGPUStencilOperation_Keep;
depth_stencil_state.stencilBack.compare = WGPUCompareFunction_Always; depth_stencil_state.stencilBack.compare = WGPUCompareFunction_Always;
// FIX(zig-gamedev): https://github.com/ocornut/imgui/commit/03417cc77d15100b18c486b55db409ee5e9c363e
depth_stencil_state.stencilBack.failOp = WGPUStencilOperation_Keep; depth_stencil_state.stencilBack.failOp = WGPUStencilOperation_Keep;
depth_stencil_state.stencilBack.depthFailOp = WGPUStencilOperation_Keep; depth_stencil_state.stencilBack.depthFailOp = WGPUStencilOperation_Keep;
depth_stencil_state.stencilBack.passOp = WGPUStencilOperation_Keep; depth_stencil_state.stencilBack.passOp = WGPUStencilOperation_Keep;
@ -709,7 +735,6 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
SafeRelease(vertex_shader_desc.module); SafeRelease(vertex_shader_desc.module);
SafeRelease(pixel_shader_desc.module); SafeRelease(pixel_shader_desc.module);
// FIX(zig-gamedev): https://github.com/ocornut/imgui/commit/9266c0d2d1390e50d2d8070896932c2564594407
SafeRelease(graphics_pipeline_desc.layout); SafeRelease(graphics_pipeline_desc.layout);
SafeRelease(bg_layouts[0]); SafeRelease(bg_layouts[0]);
@ -732,7 +757,7 @@ void ImGui_ImplWGPU_InvalidateDeviceObjects()
SafeRelease(bd->pFrameResources[i]); SafeRelease(bd->pFrameResources[i]);
} }
bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format, WGPUTextureFormat depth_format) bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info)
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
@ -743,11 +768,12 @@ bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextur
io.BackendRendererName = "imgui_impl_webgpu"; io.BackendRendererName = "imgui_impl_webgpu";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
bd->wgpuDevice = device; bd->initInfo = *init_info;
bd->wgpuDevice = init_info->Device;
bd->defaultQueue = wgpuDeviceGetQueue(bd->wgpuDevice); bd->defaultQueue = wgpuDeviceGetQueue(bd->wgpuDevice);
bd->renderTargetFormat = rt_format; bd->renderTargetFormat = init_info->RenderTargetFormat;
bd->depthStencilFormat = depth_format; bd->depthStencilFormat = init_info->DepthStencilFormat;
bd->numFramesInFlight = num_frames_in_flight; bd->numFramesInFlight = init_info->NumFramesInFlight;
bd->frameIndex = UINT_MAX; bd->frameIndex = UINT_MAX;
bd->renderResources.FontTexture = nullptr; bd->renderResources.FontTexture = nullptr;
@ -760,8 +786,8 @@ bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextur
bd->renderResources.ImageBindGroupLayout = nullptr; bd->renderResources.ImageBindGroupLayout = nullptr;
// Create buffers with a default size (they will later be grown as needed) // Create buffers with a default size (they will later be grown as needed)
bd->pFrameResources = new FrameResources[num_frames_in_flight]; bd->pFrameResources = new FrameResources[bd->numFramesInFlight];
for (int i = 0; i < num_frames_in_flight; i++) for (int i = 0; i < bd->numFramesInFlight; i++)
{ {
FrameResources* fr = &bd->pFrameResources[i]; FrameResources* fr = &bd->pFrameResources[i];
fr->IndexBuffer = nullptr; fr->IndexBuffer = nullptr;
@ -801,3 +827,7 @@ void ImGui_ImplWGPU_NewFrame()
if (!bd->pipelineState) if (!bd->pipelineState)
ImGui_ImplWGPU_CreateDeviceObjects(); ImGui_ImplWGPU_CreateDeviceObjects();
} }
//-----------------------------------------------------------------------------
#endif // #ifndef IMGUI_DISABLE

View File

@ -0,0 +1,51 @@
// dear imgui: Renderer for WebGPU
// This needs to be used along with a Platform Binding (e.g. GLFW)
// (Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break.)
// Implemented features:
// [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
// Missing features:
// [ ] Renderer: Multi-viewport support (multiple windows). Not meaningful on the web.
// 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
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
#ifndef IMGUI_DISABLE
#include <webgpu/webgpu.h>
// Initialization data, for ImGui_ImplWGPU_Init()
struct ImGui_ImplWGPU_InitInfo
{
WGPUDevice Device;
int NumFramesInFlight = 3;
WGPUTextureFormat RenderTargetFormat = WGPUTextureFormat_Undefined;
WGPUTextureFormat DepthStencilFormat = WGPUTextureFormat_Undefined;
WGPUMultisampleState PipelineMultisampleState = {};
ImGui_ImplWGPU_InitInfo()
{
PipelineMultisampleState.count = 1;
PipelineMultisampleState.mask = -1u;
PipelineMultisampleState.alphaToCoverageEnabled = false;
}
};
IMGUI_IMPL_API bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info);
IMGUI_IMPL_API void ImGui_ImplWGPU_Shutdown();
IMGUI_IMPL_API void ImGui_ImplWGPU_NewFrame();
IMGUI_IMPL_API void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder);
// Use if you want to reset your rendering device without losing Dear ImGui state.
IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects();
IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects();
#endif // #ifndef IMGUI_DISABLE

View File

@ -3,17 +3,24 @@
// Implemented features: // Implemented features:
// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui) // [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // 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. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Learn about Dear ImGui:
// Read online: https://github.com/ocornut/imgui/tree/master/docs // - 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
#include "imgui.h" #include "imgui.h"
#include "imgui_impl_win32.h" #ifndef IMGUI_DISABLE
// FIX(zig-gamedev):
// #include "imgui_impl_win32.h"
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif #endif
@ -32,8 +39,48 @@ typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILIT
typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*);
#endif #endif
// FIX(zig-gamedev):
extern "C" {
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd);
IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd);
IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();
IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame();
// Win32 message handler your application need to call.
// - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on <windows.h> from this helper.
// - You should COPY the line below into your .cpp code to forward declare the function and then you can call it.
// - Call from your application's message handler. Keep calling your message handler unless this function returns TRUE.
#if 0
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
#endif
// DPI-related helpers (optional)
// - Use to enable DPI awareness without having to create an application manifest.
// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps.
// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc.
// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime,
// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies.
IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness();
IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd
IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor
// Transparency related helpers (optional) [experimental]
// - Use to enable alpha compositing transparency with the desktop.
// - Use together with e.g. clearing your framebuffer with zero-alpha.
IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd
}; // extern "C"
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys.
// 2023-09-25: Inputs: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it (same behavior as GLFW/SDL).
// 2023-09-07: Inputs: Added support for keyboard codepage conversion for when application is compiled in MBCS mode and using a non-Unicode window.
// 2023-04-19: Added ImGui_ImplWin32_InitForOpenGL() to facilitate combining raw Win32/Winapi with OpenGL. (#3218)
// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen. (#2702)
// 2023-02-15: Inputs: Use WM_NCMOUSEMOVE / WM_NCMOUSELEAVE to track mouse position over non-client area (e.g. OS decorations) when app is not focused. (#6045, #6162)
// 2023-02-02: Inputs: Flipping WM_MOUSEHWHEEL (horizontal mouse-wheel) value to match other backends and offer consistent horizontal scrolling direction. (#4019, #6096, #1463)
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
// 2022-09-28: Inputs: Convert WM_CHAR values with MultiByteToWideChar() when window class was registered as MBCS (not Unicode). // 2022-09-28: Inputs: Convert WM_CHAR values with MultiByteToWideChar() when window class was registered as MBCS (not Unicode).
// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported). // 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).
@ -79,15 +126,22 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*);
// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging. // 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging.
// 2016-11-12: Inputs: Only call Win32 ::SetCursor(nullptr) when io.MouseDrawCursor is set. // 2016-11-12: Inputs: Only call Win32 ::SetCursor(nullptr) when io.MouseDrawCursor is set.
// Forward Declarations
static void ImGui_ImplWin32_InitPlatformInterface(bool platformHasOwnDC);
static void ImGui_ImplWin32_ShutdownPlatformInterface();
static void ImGui_ImplWin32_UpdateMonitors();
struct ImGui_ImplWin32_Data struct ImGui_ImplWin32_Data
{ {
HWND hWnd; HWND hWnd;
HWND MouseHwnd; HWND MouseHwnd;
bool MouseTracked; int MouseTrackedArea; // 0: not tracked, 1: client are, 2: non-client area
int MouseButtonsDown; int MouseButtonsDown;
INT64 Time; INT64 Time;
INT64 TicksPerSecond; INT64 TicksPerSecond;
ImGuiMouseCursor LastMouseCursor; ImGuiMouseCursor LastMouseCursor;
UINT32 KeyboardCodePage;
bool WantUpdateMonitors;
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
bool HasGamepad; bool HasGamepad;
@ -110,7 +164,17 @@ static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData()
} }
// Functions // Functions
bool ImGui_ImplWin32_Init(void* hwnd) static void ImGui_ImplWin32_UpdateKeyboardCodePage()
{
// Retrieve keyboard code page, required for handling of non-Unicode Windows.
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
HKL keyboard_layout = ::GetKeyboardLayout(0);
LCID keyboard_lcid = MAKELCID(HIWORD(keyboard_layout), SORT_DEFAULT);
if (::GetLocaleInfoA(keyboard_lcid, (LOCALE_RETURN_NUMBER | LOCALE_IDEFAULTANSICODEPAGE), (LPSTR)&bd->KeyboardCodePage, sizeof(bd->KeyboardCodePage)) == 0)
bd->KeyboardCodePage = CP_ACP; // Fallback to default ANSI code page when fails.
}
static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc)
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
@ -127,14 +191,21 @@ bool ImGui_ImplWin32_Init(void* hwnd)
io.BackendPlatformName = "imgui_impl_win32"; io.BackendPlatformName = "imgui_impl_win32";
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional)
io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional)
bd->hWnd = (HWND)hwnd; bd->hWnd = (HWND)hwnd;
bd->WantUpdateMonitors = true;
bd->TicksPerSecond = perf_frequency; bd->TicksPerSecond = perf_frequency;
bd->Time = perf_counter; bd->Time = perf_counter;
bd->LastMouseCursor = ImGuiMouseCursor_COUNT; bd->LastMouseCursor = ImGuiMouseCursor_COUNT;
ImGui_ImplWin32_UpdateKeyboardCodePage();
// Set platform dependent data in viewport // Our mouse update function expect PlatformHandle to be filled for the main viewport
ImGui::GetMainViewport()->PlatformHandleRaw = (void*)hwnd; ImGuiViewport* main_viewport = ImGui::GetMainViewport();
main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (void*)bd->hWnd;
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
ImGui_ImplWin32_InitPlatformInterface(platform_has_own_dc);
// Dynamically load XInput library // Dynamically load XInput library
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
@ -160,12 +231,25 @@ bool ImGui_ImplWin32_Init(void* hwnd)
return true; return true;
} }
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd)
{
return ImGui_ImplWin32_InitEx(hwnd, false);
}
IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd)
{
// OpenGL needs CS_OWNDC
return ImGui_ImplWin32_InitEx(hwnd, true);
}
void ImGui_ImplWin32_Shutdown() void ImGui_ImplWin32_Shutdown()
{ {
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui_ImplWin32_ShutdownPlatformInterface();
// Unload XInput library // Unload XInput library
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
if (bd->XInputDLL) if (bd->XInputDLL)
@ -174,6 +258,7 @@ void ImGui_ImplWin32_Shutdown()
io.BackendPlatformName = nullptr; io.BackendPlatformName = nullptr;
io.BackendPlatformUserData = nullptr; io.BackendPlatformUserData = nullptr;
io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_PlatformHasViewports | ImGuiBackendFlags_HasMouseHoveredViewport);
IM_DELETE(bd); IM_DELETE(bd);
} }
@ -247,31 +332,59 @@ static void ImGui_ImplWin32_UpdateKeyModifiers()
io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_APPS)); io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_APPS));
} }
// This code supports multi-viewports (multiple OS Windows mapped into different Dear ImGui viewports)
// Because of that, it is a little more complicated than your typical single-viewport binding code!
static void ImGui_ImplWin32_UpdateMouseData() static void ImGui_ImplWin32_UpdateMouseData()
{ {
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(bd->hWnd != 0); IM_ASSERT(bd->hWnd != 0);
const bool is_app_focused = (::GetForegroundWindow() == bd->hWnd); POINT mouse_screen_pos;
bool has_mouse_screen_pos = ::GetCursorPos(&mouse_screen_pos) != 0;
HWND focused_window = ::GetForegroundWindow();
const bool is_app_focused = (focused_window && (focused_window == bd->hWnd || ::IsChild(focused_window, bd->hWnd) || ImGui::FindViewportByPlatformHandle((void*)focused_window)));
if (is_app_focused) if (is_app_focused)
{ {
// (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
// When multi-viewports are enabled, all Dear ImGui positions are same as OS positions.
if (io.WantSetMousePos) if (io.WantSetMousePos)
{ {
POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
if (::ClientToScreen(bd->hWnd, &pos)) if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0)
::SetCursorPos(pos.x, pos.y); ::ClientToScreen(focused_window, &pos);
::SetCursorPos(pos.x, pos.y);
} }
// (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured) // (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured)
if (!io.WantSetMousePos && !bd->MouseTracked) // This also fills a short gap when clicking non-client area: WM_NCMOUSELEAVE -> modal OS move -> gap -> WM_NCMOUSEMOVE
if (!io.WantSetMousePos && bd->MouseTrackedArea == 0 && has_mouse_screen_pos)
{ {
POINT pos; // Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)
if (::GetCursorPos(&pos) && ::ScreenToClient(bd->hWnd, &pos)) // (This is the position you can get with ::GetCursorPos() + ::ScreenToClient() or WM_MOUSEMOVE.)
io.AddMousePosEvent((float)pos.x, (float)pos.y); // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor)
// (This is the position you can get with ::GetCursorPos() or WM_MOUSEMOVE + ::ClientToScreen(). In theory adding viewport->Pos to a client position would also be the same.)
POINT mouse_pos = mouse_screen_pos;
if (!(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable))
::ScreenToClient(bd->hWnd, &mouse_pos);
io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y);
} }
} }
// (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering.
// If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic.
// - [X] Win32 backend correctly ignore viewports with the _NoInputs flag (here using ::WindowFromPoint with WM_NCHITTEST + HTTRANSPARENT in WndProc does that)
// Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window
// for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported
// by the backend, and use its flawed heuristic to guess the viewport behind.
// - [X] Win32 backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target).
ImGuiID mouse_viewport_id = 0;
if (has_mouse_screen_pos)
if (HWND hovered_hwnd = ::WindowFromPoint(mouse_screen_pos))
if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd))
mouse_viewport_id = viewport->ID;
io.AddMouseViewportEvent(mouse_viewport_id);
} }
// Gamepad navigation mapping // Gamepad navigation mapping
@ -331,6 +444,35 @@ static void ImGui_ImplWin32_UpdateGamepads()
#endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
} }
static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, HDC, LPRECT, LPARAM)
{
MONITORINFO info = {};
info.cbSize = sizeof(MONITORINFO);
if (!::GetMonitorInfo(monitor, &info))
return TRUE;
ImGuiPlatformMonitor imgui_monitor;
imgui_monitor.MainPos = ImVec2((float)info.rcMonitor.left, (float)info.rcMonitor.top);
imgui_monitor.MainSize = ImVec2((float)(info.rcMonitor.right - info.rcMonitor.left), (float)(info.rcMonitor.bottom - info.rcMonitor.top));
imgui_monitor.WorkPos = ImVec2((float)info.rcWork.left, (float)info.rcWork.top);
imgui_monitor.WorkSize = ImVec2((float)(info.rcWork.right - info.rcWork.left), (float)(info.rcWork.bottom - info.rcWork.top));
imgui_monitor.DpiScale = ImGui_ImplWin32_GetDpiScaleForMonitor(monitor);
imgui_monitor.PlatformHandle = (void*)monitor;
ImGuiPlatformIO& io = ImGui::GetPlatformIO();
if (info.dwFlags & MONITORINFOF_PRIMARY)
io.Monitors.push_front(imgui_monitor);
else
io.Monitors.push_back(imgui_monitor);
return TRUE;
}
static void ImGui_ImplWin32_UpdateMonitors()
{
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
ImGui::GetPlatformIO().Monitors.resize(0);
::EnumDisplayMonitors(nullptr, nullptr, ImGui_ImplWin32_UpdateMonitors_EnumFunc, 0);
bd->WantUpdateMonitors = false;
}
void ImGui_ImplWin32_NewFrame() void ImGui_ImplWin32_NewFrame()
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
@ -341,6 +483,8 @@ void ImGui_ImplWin32_NewFrame()
RECT rect = { 0, 0, 0, 0 }; RECT rect = { 0, 0, 0, 0 };
::GetClientRect(bd->hWnd, &rect); ::GetClientRect(bd->hWnd, &rect);
io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));
if (bd->WantUpdateMonitors)
ImGui_ImplWin32_UpdateMonitors();
// Setup time step // Setup time step
INT64 current_time = 0; INT64 current_time = 0;
@ -478,6 +622,20 @@ static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam)
case VK_F10: return ImGuiKey_F10; case VK_F10: return ImGuiKey_F10;
case VK_F11: return ImGuiKey_F11; case VK_F11: return ImGuiKey_F11;
case VK_F12: return ImGuiKey_F12; case VK_F12: return ImGuiKey_F12;
case VK_F13: return ImGuiKey_F13;
case VK_F14: return ImGuiKey_F14;
case VK_F15: return ImGuiKey_F15;
case VK_F16: return ImGuiKey_F16;
case VK_F17: return ImGuiKey_F17;
case VK_F18: return ImGuiKey_F18;
case VK_F19: return ImGuiKey_F19;
case VK_F20: return ImGuiKey_F20;
case VK_F21: return ImGuiKey_F21;
case VK_F22: return ImGuiKey_F22;
case VK_F23: return ImGuiKey_F23;
case VK_F24: return ImGuiKey_F24;
case VK_BROWSER_BACK: return ImGuiKey_AppBack;
case VK_BROWSER_FORWARD: return ImGuiKey_AppForward;
default: return ImGuiKey_None; default: return ImGuiKey_None;
} }
} }
@ -502,6 +660,19 @@ static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam)
// Copy this line into your .cpp file to forward declare the function. // Copy this line into your .cpp file to forward declare the function.
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
#endif #endif
// See https://learn.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages
// Prefer to call this at the top of the message handler to avoid the possibility of other Win32 calls interfering with this.
static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo()
{
LPARAM extra_info = ::GetMessageExtraInfo();
if ((extra_info & 0xFFFFFF80) == 0xFF515700)
return ImGuiMouseSource_Pen;
if ((extra_info & 0xFFFFFF80) == 0xFF515780)
return ImGuiMouseSource_TouchScreen;
return ImGuiMouseSource_Mouse;
}
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{ {
if (ImGui::GetCurrentContext() == nullptr) if (ImGui::GetCurrentContext() == nullptr)
@ -513,27 +684,50 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
switch (msg) switch (msg)
{ {
case WM_MOUSEMOVE: case WM_MOUSEMOVE:
case WM_NCMOUSEMOVE:
{
// We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events // We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events
ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
const int area = (msg == WM_MOUSEMOVE) ? 1 : 2;
bd->MouseHwnd = hwnd; bd->MouseHwnd = hwnd;
if (!bd->MouseTracked) if (bd->MouseTrackedArea != area)
{ {
TRACKMOUSEEVENT tme = { sizeof(tme), TME_LEAVE, hwnd, 0 }; TRACKMOUSEEVENT tme_cancel = { sizeof(tme_cancel), TME_CANCEL, hwnd, 0 };
::TrackMouseEvent(&tme); TRACKMOUSEEVENT tme_track = { sizeof(tme_track), (DWORD)((area == 2) ? (TME_LEAVE | TME_NONCLIENT) : TME_LEAVE), hwnd, 0 };
bd->MouseTracked = true; if (bd->MouseTrackedArea != 0)
::TrackMouseEvent(&tme_cancel);
::TrackMouseEvent(&tme_track);
bd->MouseTrackedArea = area;
} }
io.AddMousePosEvent((float)GET_X_LPARAM(lParam), (float)GET_Y_LPARAM(lParam)); POINT mouse_pos = { (LONG)GET_X_LPARAM(lParam), (LONG)GET_Y_LPARAM(lParam) };
bool want_absolute_pos = (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) != 0;
if (msg == WM_MOUSEMOVE && want_absolute_pos) // WM_MOUSEMOVE are client-relative coordinates.
::ClientToScreen(hwnd, &mouse_pos);
if (msg == WM_NCMOUSEMOVE && !want_absolute_pos) // WM_NCMOUSEMOVE are absolute coordinates.
::ScreenToClient(hwnd, &mouse_pos);
io.AddMouseSourceEvent(mouse_source);
io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y);
break; break;
}
case WM_MOUSELEAVE: case WM_MOUSELEAVE:
if (bd->MouseHwnd == hwnd) case WM_NCMOUSELEAVE:
bd->MouseHwnd = nullptr; {
bd->MouseTracked = false; const int area = (msg == WM_MOUSELEAVE) ? 1 : 2;
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); if (bd->MouseTrackedArea == area)
{
if (bd->MouseHwnd == hwnd)
bd->MouseHwnd = nullptr;
bd->MouseTrackedArea = 0;
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
}
break; break;
}
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
{ {
ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
int button = 0; int button = 0;
if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; } if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; }
if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; } if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; }
@ -542,6 +736,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
if (bd->MouseButtonsDown == 0 && ::GetCapture() == nullptr) if (bd->MouseButtonsDown == 0 && ::GetCapture() == nullptr)
::SetCapture(hwnd); ::SetCapture(hwnd);
bd->MouseButtonsDown |= 1 << button; bd->MouseButtonsDown |= 1 << button;
io.AddMouseSourceEvent(mouse_source);
io.AddMouseButtonEvent(button, true); io.AddMouseButtonEvent(button, true);
return 0; return 0;
} }
@ -550,6 +745,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
case WM_MBUTTONUP: case WM_MBUTTONUP:
case WM_XBUTTONUP: case WM_XBUTTONUP:
{ {
ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
int button = 0; int button = 0;
if (msg == WM_LBUTTONUP) { button = 0; } if (msg == WM_LBUTTONUP) { button = 0; }
if (msg == WM_RBUTTONUP) { button = 1; } if (msg == WM_RBUTTONUP) { button = 1; }
@ -558,6 +754,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
bd->MouseButtonsDown &= ~(1 << button); bd->MouseButtonsDown &= ~(1 << button);
if (bd->MouseButtonsDown == 0 && ::GetCapture() == hwnd) if (bd->MouseButtonsDown == 0 && ::GetCapture() == hwnd)
::ReleaseCapture(); ::ReleaseCapture();
io.AddMouseSourceEvent(mouse_source);
io.AddMouseButtonEvent(button, false); io.AddMouseButtonEvent(button, false);
return 0; return 0;
} }
@ -565,7 +762,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
io.AddMouseWheelEvent(0.0f, (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA); io.AddMouseWheelEvent(0.0f, (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA);
return 0; return 0;
case WM_MOUSEHWHEEL: case WM_MOUSEHWHEEL:
io.AddMouseWheelEvent((float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f); io.AddMouseWheelEvent(-(float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f);
return 0; return 0;
case WM_KEYDOWN: case WM_KEYDOWN:
case WM_KEYUP: case WM_KEYUP:
@ -583,10 +780,14 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
int vk = (int)wParam; int vk = (int)wParam;
if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED)) if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED))
vk = IM_VK_KEYPAD_ENTER; vk = IM_VK_KEYPAD_ENTER;
// Submit key event
const ImGuiKey key = ImGui_ImplWin32_VirtualKeyToImGuiKey(vk); const ImGuiKey key = ImGui_ImplWin32_VirtualKeyToImGuiKey(vk);
const int scancode = (int)LOBYTE(HIWORD(lParam)); const int scancode = (int)LOBYTE(HIWORD(lParam));
// Special behavior for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit the key down event.
if (key == ImGuiKey_PrintScreen && !is_key_down)
ImGui_ImplWin32_AddKeyEvent(key, true, vk, scancode);
// Submit key event
if (key != ImGuiKey_None) if (key != ImGuiKey_None)
ImGui_ImplWin32_AddKeyEvent(key, is_key_down, vk, scancode); ImGui_ImplWin32_AddKeyEvent(key, is_key_down, vk, scancode);
@ -614,6 +815,9 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
case WM_KILLFOCUS: case WM_KILLFOCUS:
io.AddFocusEvent(msg == WM_SETFOCUS); io.AddFocusEvent(msg == WM_SETFOCUS);
return 0; return 0;
case WM_INPUTLANGCHANGE:
ImGui_ImplWin32_UpdateKeyboardCodePage();
return 0;
case WM_CHAR: case WM_CHAR:
if (::IsWindowUnicode(hwnd)) if (::IsWindowUnicode(hwnd))
{ {
@ -624,7 +828,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
else else
{ {
wchar_t wch = 0; wchar_t wch = 0;
::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (char*)&wParam, 1, &wch, 1); ::MultiByteToWideChar(bd->KeyboardCodePage, MB_PRECOMPOSED, (char*)&wParam, 1, &wch, 1);
io.AddInputCharacter(wch); io.AddInputCharacter(wch);
} }
return 0; return 0;
@ -639,6 +843,9 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
bd->WantUpdateHasGamepad = true; bd->WantUpdateHasGamepad = true;
#endif #endif
return 0; return 0;
case WM_DISPLAYCHANGE:
bd->WantUpdateMonitors = true;
return 0;
} }
return 0; return 0;
} }
@ -703,6 +910,10 @@ typedef DPI_AWARENESS_CONTEXT(WINAPI* PFN_SetThreadDpiAwarenessContext)(DPI_AWAR
// Helper function to enable DPI awareness without setting up a manifest // Helper function to enable DPI awareness without setting up a manifest
void ImGui_ImplWin32_EnableDpiAwareness() void ImGui_ImplWin32_EnableDpiAwareness()
{ {
// Make sure monitors will be updated with latest correct scaling
if (ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData())
bd->WantUpdateMonitors = true;
if (_IsWindows10OrGreater()) if (_IsWindows10OrGreater())
{ {
static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process
@ -803,3 +1014,347 @@ void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd)
} }
//--------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
//--------------------------------------------------------------------------------------------------------
// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data.
struct ImGui_ImplWin32_ViewportData
{
HWND Hwnd;
HWND HwndParent;
bool HwndOwned;
DWORD DwStyle;
DWORD DwExStyle;
ImGui_ImplWin32_ViewportData() { Hwnd = HwndParent = nullptr; HwndOwned = false; DwStyle = DwExStyle = 0; }
~ImGui_ImplWin32_ViewportData() { IM_ASSERT(Hwnd == nullptr); }
};
static void ImGui_ImplWin32_GetWin32StyleFromViewportFlags(ImGuiViewportFlags flags, DWORD* out_style, DWORD* out_ex_style)
{
if (flags & ImGuiViewportFlags_NoDecoration)
*out_style = WS_POPUP;
else
*out_style = WS_OVERLAPPEDWINDOW;
if (flags & ImGuiViewportFlags_NoTaskBarIcon)
*out_ex_style = WS_EX_TOOLWINDOW;
else
*out_ex_style = WS_EX_APPWINDOW;
if (flags & ImGuiViewportFlags_TopMost)
*out_ex_style |= WS_EX_TOPMOST;
}
static HWND ImGui_ImplWin32_GetHwndFromViewportID(ImGuiID viewport_id)
{
if (viewport_id != 0)
if (ImGuiViewport* viewport = ImGui::FindViewportByID(viewport_id))
return (HWND)viewport->PlatformHandle;
return nullptr;
}
static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport)
{
ImGui_ImplWin32_ViewportData* vd = IM_NEW(ImGui_ImplWin32_ViewportData)();
viewport->PlatformUserData = vd;
// Select style and parent window
ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &vd->DwStyle, &vd->DwExStyle);
vd->HwndParent = ImGui_ImplWin32_GetHwndFromViewportID(viewport->ParentViewportId);
// Create window
RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) };
::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle);
vd->Hwnd = ::CreateWindowEx(
vd->DwExStyle, _T("ImGui Platform"), _T("Untitled"), vd->DwStyle, // Style, class name, window name
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area
vd->HwndParent, nullptr, ::GetModuleHandle(nullptr), nullptr); // Owner window, Menu, Instance, Param
vd->HwndOwned = true;
viewport->PlatformRequestResize = false;
viewport->PlatformHandle = viewport->PlatformHandleRaw = vd->Hwnd;
}
static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport)
{
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
if (ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData)
{
if (::GetCapture() == vd->Hwnd)
{
// Transfer capture so if we started dragging from a window that later disappears, we'll still receive the MOUSEUP event.
::ReleaseCapture();
::SetCapture(bd->hWnd);
}
if (vd->Hwnd && vd->HwndOwned)
::DestroyWindow(vd->Hwnd);
vd->Hwnd = nullptr;
IM_DELETE(vd);
}
viewport->PlatformUserData = viewport->PlatformHandle = nullptr;
}
static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport)
{
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
IM_ASSERT(vd->Hwnd != 0);
if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing)
::ShowWindow(vd->Hwnd, SW_SHOWNA);
else
::ShowWindow(vd->Hwnd, SW_SHOW);
}
static void ImGui_ImplWin32_UpdateWindow(ImGuiViewport* viewport)
{
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
IM_ASSERT(vd->Hwnd != 0);
// Update Win32 parent if it changed _after_ creation
// Unlike style settings derived from configuration flags, this is more likely to change for advanced apps that are manipulating ParentViewportID manually.
HWND new_parent = ImGui_ImplWin32_GetHwndFromViewportID(viewport->ParentViewportId);
if (new_parent != vd->HwndParent)
{
// Win32 windows can either have a "Parent" (for WS_CHILD window) or an "Owner" (which among other thing keeps window above its owner).
// Our Dear Imgui-side concept of parenting only mostly care about what Win32 call "Owner".
// The parent parameter of CreateWindowEx() sets up Parent OR Owner depending on WS_CHILD flag. In our case an Owner as we never use WS_CHILD.
// Calling ::SetParent() here would be incorrect: it will create a full child relation, alter coordinate system and clipping.
// Calling ::SetWindowLongPtr() with GWLP_HWNDPARENT seems correct although poorly documented.
// https://devblogs.microsoft.com/oldnewthing/20100315-00/?p=14613
vd->HwndParent = new_parent;
::SetWindowLongPtr(vd->Hwnd, GWLP_HWNDPARENT, (LONG_PTR)vd->HwndParent);
}
// (Optional) Update Win32 style if it changed _after_ creation.
// Generally they won't change unless configuration flags are changed, but advanced uses (such as manually rewriting viewport flags) make this useful.
DWORD new_style;
DWORD new_ex_style;
ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &new_style, &new_ex_style);
// Only reapply the flags that have been changed from our point of view (as other flags are being modified by Windows)
if (vd->DwStyle != new_style || vd->DwExStyle != new_ex_style)
{
// (Optional) Update TopMost state if it changed _after_ creation
bool top_most_changed = (vd->DwExStyle & WS_EX_TOPMOST) != (new_ex_style & WS_EX_TOPMOST);
HWND insert_after = top_most_changed ? ((viewport->Flags & ImGuiViewportFlags_TopMost) ? HWND_TOPMOST : HWND_NOTOPMOST) : 0;
UINT swp_flag = top_most_changed ? 0 : SWP_NOZORDER;
// Apply flags and position (since it is affected by flags)
vd->DwStyle = new_style;
vd->DwExStyle = new_ex_style;
::SetWindowLong(vd->Hwnd, GWL_STYLE, vd->DwStyle);
::SetWindowLong(vd->Hwnd, GWL_EXSTYLE, vd->DwExStyle);
RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) };
::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle); // Client to Screen
::SetWindowPos(vd->Hwnd, insert_after, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, swp_flag | SWP_NOACTIVATE | SWP_FRAMECHANGED);
::ShowWindow(vd->Hwnd, SW_SHOWNA); // This is necessary when we alter the style
viewport->PlatformRequestMove = viewport->PlatformRequestResize = true;
}
}
static ImVec2 ImGui_ImplWin32_GetWindowPos(ImGuiViewport* viewport)
{
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
IM_ASSERT(vd->Hwnd != 0);
POINT pos = { 0, 0 };
::ClientToScreen(vd->Hwnd, &pos);
return ImVec2((float)pos.x, (float)pos.y);
}
static void ImGui_ImplWin32_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos)
{
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
IM_ASSERT(vd->Hwnd != 0);
RECT rect = { (LONG)pos.x, (LONG)pos.y, (LONG)pos.x, (LONG)pos.y };
::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle);
::SetWindowPos(vd->Hwnd, nullptr, rect.left, rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
}
static ImVec2 ImGui_ImplWin32_GetWindowSize(ImGuiViewport* viewport)
{
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
IM_ASSERT(vd->Hwnd != 0);
RECT rect;
::GetClientRect(vd->Hwnd, &rect);
return ImVec2(float(rect.right - rect.left), float(rect.bottom - rect.top));
}
static void ImGui_ImplWin32_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
{
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
IM_ASSERT(vd->Hwnd != 0);
RECT rect = { 0, 0, (LONG)size.x, (LONG)size.y };
::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle); // Client to Screen
::SetWindowPos(vd->Hwnd, nullptr, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
}
static void ImGui_ImplWin32_SetWindowFocus(ImGuiViewport* viewport)
{
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
IM_ASSERT(vd->Hwnd != 0);
::BringWindowToTop(vd->Hwnd);
::SetForegroundWindow(vd->Hwnd);
::SetFocus(vd->Hwnd);
}
static bool ImGui_ImplWin32_GetWindowFocus(ImGuiViewport* viewport)
{
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
IM_ASSERT(vd->Hwnd != 0);
return ::GetForegroundWindow() == vd->Hwnd;
}
static bool ImGui_ImplWin32_GetWindowMinimized(ImGuiViewport* viewport)
{
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
IM_ASSERT(vd->Hwnd != 0);
return ::IsIconic(vd->Hwnd) != 0;
}
static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* title)
{
// ::SetWindowTextA() doesn't properly handle UTF-8 so we explicitely convert our string.
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
IM_ASSERT(vd->Hwnd != 0);
int n = ::MultiByteToWideChar(CP_UTF8, 0, title, -1, nullptr, 0);
ImVector<wchar_t> title_w;
title_w.resize(n);
::MultiByteToWideChar(CP_UTF8, 0, title, -1, title_w.Data, n);
::SetWindowTextW(vd->Hwnd, title_w.Data);
}
static void ImGui_ImplWin32_SetWindowAlpha(ImGuiViewport* viewport, float alpha)
{
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
IM_ASSERT(vd->Hwnd != 0);
IM_ASSERT(alpha >= 0.0f && alpha <= 1.0f);
if (alpha < 1.0f)
{
DWORD style = ::GetWindowLongW(vd->Hwnd, GWL_EXSTYLE) | WS_EX_LAYERED;
::SetWindowLongW(vd->Hwnd, GWL_EXSTYLE, style);
::SetLayeredWindowAttributes(vd->Hwnd, 0, (BYTE)(255 * alpha), LWA_ALPHA);
}
else
{
DWORD style = ::GetWindowLongW(vd->Hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED;
::SetWindowLongW(vd->Hwnd, GWL_EXSTYLE, style);
}
}
static float ImGui_ImplWin32_GetWindowDpiScale(ImGuiViewport* viewport)
{
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
IM_ASSERT(vd->Hwnd != 0);
return ImGui_ImplWin32_GetDpiScaleForHwnd(vd->Hwnd);
}
// FIXME-DPI: Testing DPI related ideas
static void ImGui_ImplWin32_OnChangedViewport(ImGuiViewport* viewport)
{
(void)viewport;
#if 0
ImGuiStyle default_style;
//default_style.WindowPadding = ImVec2(0, 0);
//default_style.WindowBorderSize = 0.0f;
//default_style.ItemSpacing.y = 3.0f;
//default_style.FramePadding = ImVec2(0, 0);
default_style.ScaleAllSizes(viewport->DpiScale);
ImGuiStyle& style = ImGui::GetStyle();
style = default_style;
#endif
}
static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
return true;
if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hWnd))
{
switch (msg)
{
case WM_CLOSE:
viewport->PlatformRequestClose = true;
return 0;
case WM_MOVE:
viewport->PlatformRequestMove = true;
break;
case WM_SIZE:
viewport->PlatformRequestResize = true;
break;
case WM_MOUSEACTIVATE:
if (viewport->Flags & ImGuiViewportFlags_NoFocusOnClick)
return MA_NOACTIVATE;
break;
case WM_NCHITTEST:
// Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() correctly. (which is optional).
// The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging.
// If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in
// your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system.
if (viewport->Flags & ImGuiViewportFlags_NoInputs)
return HTTRANSPARENT;
break;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
static void ImGui_ImplWin32_InitPlatformInterface(bool platform_has_own_dc)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW | (platform_has_own_dc ? CS_OWNDC : 0);
wcex.lpfnWndProc = ImGui_ImplWin32_WndProcHandler_PlatformWindow;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = ::GetModuleHandle(nullptr);
wcex.hIcon = nullptr;
wcex.hCursor = nullptr;
wcex.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
wcex.lpszMenuName = nullptr;
wcex.lpszClassName = _T("ImGui Platform");
wcex.hIconSm = nullptr;
::RegisterClassEx(&wcex);
ImGui_ImplWin32_UpdateMonitors();
// Register platform interface (will be coupled with a renderer interface)
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Platform_CreateWindow = ImGui_ImplWin32_CreateWindow;
platform_io.Platform_DestroyWindow = ImGui_ImplWin32_DestroyWindow;
platform_io.Platform_ShowWindow = ImGui_ImplWin32_ShowWindow;
platform_io.Platform_SetWindowPos = ImGui_ImplWin32_SetWindowPos;
platform_io.Platform_GetWindowPos = ImGui_ImplWin32_GetWindowPos;
platform_io.Platform_SetWindowSize = ImGui_ImplWin32_SetWindowSize;
platform_io.Platform_GetWindowSize = ImGui_ImplWin32_GetWindowSize;
platform_io.Platform_SetWindowFocus = ImGui_ImplWin32_SetWindowFocus;
platform_io.Platform_GetWindowFocus = ImGui_ImplWin32_GetWindowFocus;
platform_io.Platform_GetWindowMinimized = ImGui_ImplWin32_GetWindowMinimized;
platform_io.Platform_SetWindowTitle = ImGui_ImplWin32_SetWindowTitle;
platform_io.Platform_SetWindowAlpha = ImGui_ImplWin32_SetWindowAlpha;
platform_io.Platform_UpdateWindow = ImGui_ImplWin32_UpdateWindow;
platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; // FIXME-DPI
platform_io.Platform_OnChangedViewport = ImGui_ImplWin32_OnChangedViewport; // FIXME-DPI
// Register main window handle (which is owned by the main application, not by us)
// This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports.
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
ImGui_ImplWin32_ViewportData* vd = IM_NEW(ImGui_ImplWin32_ViewportData)();
vd->Hwnd = bd->hWnd;
vd->HwndOwned = false;
main_viewport->PlatformUserData = vd;
main_viewport->PlatformHandle = (void*)bd->hWnd;
}
static void ImGui_ImplWin32_ShutdownPlatformInterface()
{
::UnregisterClass(_T("ImGui Platform"), ::GetModuleHandle(nullptr));
ImGui::DestroyPlatformWindows();
}
//---------------------------------------------------------------------------------------------------------
#endif // #ifndef IMGUI_DISABLE

View File

@ -3,21 +3,26 @@
// Implemented features: // Implemented features:
// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui) // [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // 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. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Learn about Dear ImGui:
// Read online: https://github.com/ocornut/imgui/tree/master/docs // - 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
#pragma once #pragma once
#include "imgui.h" // IMGUI_IMPL_API #include "imgui.h" // IMGUI_IMPL_API
#ifndef IMGUI_DISABLE
extern "C" { // mziulek
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd);
IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd);
IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();
IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame();
@ -45,4 +50,4 @@ IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); //
// - Use together with e.g. clearing your framebuffer with zero-alpha. // - Use together with e.g. clearing your framebuffer with zero-alpha.
IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd
} #endif // #ifndef IMGUI_DISABLE

View File

@ -153,6 +153,8 @@ typedef khronos_intptr_t GLintptr;
#define GL_ARRAY_BUFFER_BINDING 0x8894 #define GL_ARRAY_BUFFER_BINDING 0x8894
#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 #define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
#define GL_STREAM_DRAW 0x88E0 #define GL_STREAM_DRAW 0x88E0
#define GL_PIXEL_UNPACK_BUFFER 0x88EC
#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF
typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);

View File

@ -1,5 +1,5 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// COMPILE-TIME OPTIONS FOR DEAR IMGUI // DEAR IMGUI COMPILE-TIME OPTIONS
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. // Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. // You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -9,7 +9,7 @@
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp // You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures. // files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. // Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using. // Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#pragma once #pragma once
@ -26,21 +26,21 @@
//#define IMGUI_API __declspec( dllexport ) //#define IMGUI_API __declspec( dllexport )
//#define IMGUI_API __declspec( dllimport ) //#define IMGUI_API __declspec( dllimport )
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions. //#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
//---- Disable all of Dear ImGui or don't implement standard windows/tools. //---- Disable all of Dear ImGui or don't implement standard windows/tools.
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp. // It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowStackToolWindow() will be empty (this was called IMGUI_DISABLE_METRICS_WINDOW before 1.88). //#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
//---- Don't implement some functions to reduce linkage requirements. //---- Don't implement some functions to reduce linkage requirements.
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a) //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW) //#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime). //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
@ -50,21 +50,24 @@
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available //#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
//---- Include imgui_user.h at the end of imgui.h as a convenience //---- Include imgui_user.h at the end of imgui.h as a convenience
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
//#define IMGUI_INCLUDE_IMGUI_USER_H //#define IMGUI_INCLUDE_IMGUI_USER_H
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another) //---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
//#define IMGUI_USE_BGRA_PACKED_COLOR //#define IMGUI_USE_BGRA_PACKED_COLOR
//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...) //---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
//#define IMGUI_USE_WCHAR32 //#define IMGUI_USE_WCHAR32
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files. // By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if enabled //#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined) //---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h. // Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
@ -75,6 +78,12 @@
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. // On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
//#define IMGUI_ENABLE_FREETYPE //#define IMGUI_ENABLE_FREETYPE
//---- Use FreeType+lunasvg library to render OpenType SVG fonts (SVGinOT)
// Requires lunasvg headers to be available in the include path + program to be linked with the lunasvg library (not provided).
// Only works in combination with IMGUI_ENABLE_FREETYPE.
// (implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
//---- Use stb_truetype to build and rasterize the font atlas (default) //---- Use stb_truetype to build and rasterize the font atlas (default)
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend. // The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
//#define IMGUI_ENABLE_STB_TRUETYPE //#define IMGUI_ENABLE_STB_TRUETYPE
@ -105,7 +114,7 @@
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); //typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
//#define ImDrawCallback MyImDrawCallback //#define ImDrawCallback MyImDrawCallback
//---- Debug Tools: Macro to break in Debugger //---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.) // (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
//#define IM_DEBUG_BREAK IM_ASSERT(0) //#define IM_DEBUG_BREAK IM_ASSERT(0)
//#define IM_DEBUG_BREAK __debugbreak() //#define IM_DEBUG_BREAK __debugbreak()
@ -113,10 +122,10 @@
//---- Debug Tools: Enable slower asserts //---- Debug Tools: Enable slower asserts
//#define IMGUI_DEBUG_PARANOID //#define IMGUI_DEBUG_PARANOID
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. //---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
/* /*
namespace ImGui namespace ImGui
{ {
void MyFunction(const char* name, const MyMatrix44& v); void MyFunction(const char* name, MyMatrix44* mtx);
} }
*/ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// dear imgui, v1.89.6 // dear imgui, v1.90.4
// (drawing and font code) // (drawing and font code)
/* /*
@ -63,6 +63,7 @@ Index of this file:
#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier #pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier
#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. #pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
#pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter
#elif defined(__GNUC__) #elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
@ -134,7 +135,7 @@ namespace IMGUI_STB_NAMESPACE
#define STBTT_sqrt(x) ImSqrt(x) #define STBTT_sqrt(x) ImSqrt(x)
#define STBTT_pow(x,y) ImPow(x,y) #define STBTT_pow(x,y) ImPow(x,y)
#define STBTT_fabs(x) ImFabs(x) #define STBTT_fabs(x) ImFabs(x)
#define STBTT_ifloor(x) ((int)ImFloorSigned(x)) #define STBTT_ifloor(x) ((int)ImFloor(x))
#define STBTT_iceil(x) ((int)ImCeil(x)) #define STBTT_iceil(x) ((int)ImCeil(x))
#define STBTT_STATIC #define STBTT_STATIC
#define STB_TRUETYPE_IMPLEMENTATION #define STB_TRUETYPE_IMPLEMENTATION
@ -213,6 +214,8 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_HeaderActive] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f);
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
@ -273,6 +276,8 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f);
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
@ -334,6 +339,8 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f);
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
@ -385,9 +392,9 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
void ImDrawList::_ResetForNewFrame() void ImDrawList::_ResetForNewFrame()
{ {
// Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory. // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory.
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0); IM_STATIC_ASSERT(offsetof(ImDrawCmd, ClipRect) == 0);
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4)); IM_STATIC_ASSERT(offsetof(ImDrawCmd, TextureId) == sizeof(ImVec4));
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID)); IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID));
if (_Splitter._Count > 1) if (_Splitter._Count > 1)
_Splitter.Merge(this); _Splitter.Merge(this);
@ -474,7 +481,7 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
} }
// Compare ClipRect, TextureId and VtxOffset with a single memcmp() // Compare ClipRect, TextureId and VtxOffset with a single memcmp()
#define ImDrawCmd_HeaderSize (IM_OFFSETOF(ImDrawCmd, VtxOffset) + sizeof(unsigned int)) #define ImDrawCmd_HeaderSize (offsetof(ImDrawCmd, VtxOffset) + sizeof(unsigned int))
#define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset #define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset
#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset #define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset
#define ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1) (CMD_0->IdxOffset + CMD_0->ElemCount == CMD_1->IdxOffset) #define ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1) (CMD_0->IdxOffset + CMD_0->ElemCount == CMD_1->IdxOffset)
@ -560,7 +567,7 @@ int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const
{ {
// Automatic segment count // Automatic segment count
const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy
if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts)) if (radius_idx >= 0 && radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts))
return _Data->CircleSegmentCounts[radius_idx]; // Use cached value return _Data->CircleSegmentCounts[radius_idx]; // Use cached value
else else
return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError); return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError);
@ -640,7 +647,7 @@ void ImDrawList::PrimReserve(int idx_count, int vtx_count)
_IdxWritePtr = IdxBuffer.Data + idx_buffer_old_size; _IdxWritePtr = IdxBuffer.Data + idx_buffer_old_size;
} }
// Release the a number of reserved vertices/indices from the end of the last reservation made with PrimReserve(). // Release the number of reserved vertices/indices from the end of the last reservation made with PrimReserve().
void ImDrawList::PrimUnreserve(int idx_count, int vtx_count) void ImDrawList::PrimUnreserve(int idx_count, int vtx_count)
{ {
IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0); IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0);
@ -1190,8 +1197,8 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
const float a_min_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f); const float a_min_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f);
const float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f); const float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f);
const int a_min_sample = a_is_reverse ? (int)ImFloorSigned(a_min_sample_f) : (int)ImCeil(a_min_sample_f); const int a_min_sample = a_is_reverse ? (int)ImFloor(a_min_sample_f) : (int)ImCeil(a_min_sample_f);
const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloorSigned(a_max_sample_f); const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloor(a_max_sample_f);
const int a_mid_samples = a_is_reverse ? ImMax(a_min_sample - a_max_sample, 0) : ImMax(a_max_sample - a_min_sample, 0); const int a_mid_samples = a_is_reverse ? ImMax(a_min_sample - a_max_sample, 0) : ImMax(a_max_sample - a_min_sample, 0);
const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX; const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
@ -1216,6 +1223,27 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
} }
} }
void ImDrawList::PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments)
{
if (num_segments <= 0)
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here.
_Path.reserve(_Path.Size + (num_segments + 1));
const float cos_rot = ImCos(rot);
const float sin_rot = ImSin(rot);
for (int i = 0; i <= num_segments; i++)
{
const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);
ImVec2 point(ImCos(a) * radius_x, ImSin(a) * radius_y);
const float rel_x = (point.x * cos_rot) - (point.y * sin_rot);
const float rel_y = (point.x * sin_rot) + (point.y * cos_rot);
point.x = rel_x + center.x;
point.y = rel_y + center.y;
_Path.push_back(point);
}
}
ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t) ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t)
{ {
float u = 1.0f - t; float u = 1.0f - t;
@ -1311,33 +1339,22 @@ void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3,
} }
} }
IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4));
static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags) static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags)
{ {
/*
IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4));
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
// Obsoleted in 1.82 (from February 2021) // Obsoleted in 1.82 (from February 2021). This code was stripped/simplified and mostly commented in 1.90 (from September 2023)
// Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All) // - Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All)
// ~0 --> ImDrawFlags_RoundCornersAll or 0 if (flags == ~0) { return ImDrawFlags_RoundCornersAll; }
if (flags == ~0) // - Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations). Read details in older version of this code.
return ImDrawFlags_RoundCornersAll; if (flags >= 0x01 && flags <= 0x0F) { return (flags << 4); }
// Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations)
// 0x01 --> ImDrawFlags_RoundCornersTopLeft (VALUE 0x01 OVERLAPS ImDrawFlags_Closed but ImDrawFlags_Closed is never valid in this path!)
// 0x02 --> ImDrawFlags_RoundCornersTopRight
// 0x03 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight
// 0x04 --> ImDrawFlags_RoundCornersBotLeft
// 0x05 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBotLeft
// ...
// 0x0F --> ImDrawFlags_RoundCornersAll or 0
// (See all values in ImDrawCornerFlags_)
if (flags >= 0x01 && flags <= 0x0F)
return (flags << 4);
// We cannot support hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f' // We cannot support hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f'
#endif #endif
*/
// If this triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values. // If this assert triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values.
// Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc... // Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc. anyway.
// See details in 1.82 Changelog as well as 2021/03/12 and 2023/09/08 entries in "API BREAKING CHANGES" section.
IM_ASSERT((flags & 0x0F) == 0 && "Misuse of legacy hardcoded ImDrawCornerFlags values!"); IM_ASSERT((flags & 0x0F) == 0 && "Misuse of legacy hardcoded ImDrawCornerFlags values!");
if ((flags & ImDrawFlags_RoundCornersMask_) == 0) if ((flags & ImDrawFlags_RoundCornersMask_) == 0)
@ -1348,10 +1365,12 @@ static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags)
void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDrawFlags flags) void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDrawFlags flags)
{ {
flags = FixRectCornerFlags(flags); if (rounding >= 0.5f)
rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f ) - 1.0f); {
rounding = ImMin(rounding, ImFabs(b.y - a.y) * ( ((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f ) - 1.0f); flags = FixRectCornerFlags(flags);
rounding = ImMin(rounding, ImFabs(b.x - a.x) * (((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f) - 1.0f);
rounding = ImMin(rounding, ImFabs(b.y - a.y) * (((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f) - 1.0f);
}
if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
{ {
PathLineTo(a); PathLineTo(a);
@ -1544,6 +1563,35 @@ void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, in
PathFillConvex(col); PathFillConvex(col);
} }
// Ellipse
void ImDrawList::AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot, int num_segments, float thickness)
{
if ((col & IM_COL32_A_MASK) == 0)
return;
if (num_segments <= 0)
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here.
// Because we are filling a closed shape we remove 1 from the count of segments/points
const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
PathEllipticalArcTo(center, radius_x, radius_y, rot, 0.0f, a_max, num_segments - 1);
PathStroke(col, true, thickness);
}
void ImDrawList::AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot, int num_segments)
{
if ((col & IM_COL32_A_MASK) == 0)
return;
if (num_segments <= 0)
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here.
// Because we are filling a closed shape we remove 1 from the count of segments/points
const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
PathEllipticalArcTo(center, radius_x, radius_y, rot, 0.0f, a_max, num_segments - 1);
PathFillConvex(col);
}
// Cubic Bezier takes 4 controls points // Cubic Bezier takes 4 controls points
void ImDrawList::AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments) void ImDrawList::AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments)
{ {
@ -1808,6 +1856,63 @@ void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx)
// [SECTION] ImDrawData // [SECTION] ImDrawData
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void ImDrawData::Clear()
{
Valid = false;
CmdListsCount = TotalIdxCount = TotalVtxCount = 0;
CmdLists.resize(0); // The ImDrawList are NOT owned by ImDrawData but e.g. by ImGuiContext, so we don't clear them.
DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.0f, 0.0f);
OwnerViewport = NULL;
}
// Important: 'out_list' is generally going to be draw_data->CmdLists, but may be another temporary list
// as long at it is expected that the result will be later merged into draw_data->CmdLists[].
void ImGui::AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
{
if (draw_list->CmdBuffer.Size == 0)
return;
if (draw_list->CmdBuffer.Size == 1 && draw_list->CmdBuffer[0].ElemCount == 0 && draw_list->CmdBuffer[0].UserCallback == NULL)
return;
// Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
// May trigger for you if you are using PrimXXX functions incorrectly.
IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
// Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
// If this assert triggers because you are drawing lots of stuff manually:
// - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
// Be mindful that the lower-level ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents.
// - If you want large meshes with more than 64K vertices, you can either:
// (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
// Most example backends already support this from 1.71. Pre-1.71 backends won't.
// Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them.
// (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
// Most example backends already support this. For example, the OpenGL example code detect index size at compile-time:
// glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
// Your own engine or render API may use different parameters or function calls to specify index sizes.
// 2 and 4 bytes indices are generally supported by most graphics API.
// - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
// the 64K limit to split your draw commands in multiple draw lists.
if (sizeof(ImDrawIdx) == 2)
IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
// Add to output list + records state in ImDrawData
out_list->push_back(draw_list);
draw_data->CmdListsCount++;
draw_data->TotalVtxCount += draw_list->VtxBuffer.Size;
draw_data->TotalIdxCount += draw_list->IdxBuffer.Size;
}
void ImDrawData::AddDrawList(ImDrawList* draw_list)
{
IM_ASSERT(CmdLists.Size == CmdListsCount);
draw_list->_PopUnusedDrawCmd();
ImGui::AddDrawListToDrawDataEx(this, &CmdLists, draw_list);
}
// For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! // For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering!
void ImDrawData::DeIndexAllBuffers() void ImDrawData::DeIndexAllBuffers()
{ {
@ -1832,15 +1937,9 @@ void ImDrawData::DeIndexAllBuffers()
// or if there is a difference between your window resolution and framebuffer resolution. // or if there is a difference between your window resolution and framebuffer resolution.
void ImDrawData::ScaleClipRects(const ImVec2& fb_scale) void ImDrawData::ScaleClipRects(const ImVec2& fb_scale)
{ {
for (int i = 0; i < CmdListsCount; i++) for (ImDrawList* draw_list : CmdLists)
{ for (ImDrawCmd& cmd : draw_list->CmdBuffer)
ImDrawList* cmd_list = CmdLists[i]; cmd.ClipRect = ImVec4(cmd.ClipRect.x * fb_scale.x, cmd.ClipRect.y * fb_scale.y, cmd.ClipRect.z * fb_scale.x, cmd.ClipRect.w * fb_scale.y);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
ImDrawCmd* cmd = &cmd_list->CmdBuffer[cmd_i];
cmd->ClipRect = ImVec4(cmd->ClipRect.x * fb_scale.x, cmd->ClipRect.y * fb_scale.y, cmd->ClipRect.z * fb_scale.x, cmd->ClipRect.w * fb_scale.y);
}
}
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -1896,6 +1995,14 @@ void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int ve
} }
} }
void ImGui::ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out)
{
ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx;
ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx;
for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex)
vertex->pos = ImRotate(vertex->pos- pivot_in, cos_a, sin_a) + pivot_out;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] ImFontConfig // [SECTION] ImFontConfig
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -1904,10 +2011,11 @@ ImFontConfig::ImFontConfig()
{ {
memset(this, 0, sizeof(*this)); memset(this, 0, sizeof(*this));
FontDataOwnedByAtlas = true; FontDataOwnedByAtlas = true;
OversampleH = 3; // FIXME: 2 may be a better default? OversampleH = 2;
OversampleV = 1; OversampleV = 1;
GlyphMaxAdvanceX = FLT_MAX; GlyphMaxAdvanceX = FLT_MAX;
RasterizerMultiply = 1.0f; RasterizerMultiply = 1.0f;
RasterizerDensity = 1.0f;
EllipsisChar = (ImWchar)-1; EllipsisChar = (ImWchar)-1;
} }
@ -1981,19 +2089,19 @@ ImFontAtlas::~ImFontAtlas()
void ImFontAtlas::ClearInputData() void ImFontAtlas::ClearInputData()
{ {
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
for (int i = 0; i < ConfigData.Size; i++) for (ImFontConfig& font_cfg : ConfigData)
if (ConfigData[i].FontData && ConfigData[i].FontDataOwnedByAtlas) if (font_cfg.FontData && font_cfg.FontDataOwnedByAtlas)
{ {
IM_FREE(ConfigData[i].FontData); IM_FREE(font_cfg.FontData);
ConfigData[i].FontData = NULL; font_cfg.FontData = NULL;
} }
// When clearing this we lose access to the font name and other information used to build the font. // When clearing this we lose access to the font name and other information used to build the font.
for (int i = 0; i < Fonts.Size; i++) for (ImFont* font : Fonts)
if (Fonts[i]->ConfigData >= ConfigData.Data && Fonts[i]->ConfigData < ConfigData.Data + ConfigData.Size) if (font->ConfigData >= ConfigData.Data && font->ConfigData < ConfigData.Data + ConfigData.Size)
{ {
Fonts[i]->ConfigData = NULL; font->ConfigData = NULL;
Fonts[i]->ConfigDataCount = 0; font->ConfigDataCount = 0;
} }
ConfigData.clear(); ConfigData.clear();
CustomRects.clear(); CustomRects.clear();
@ -2090,6 +2198,8 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
if (new_font_cfg.DstFont->EllipsisChar == (ImWchar)-1) if (new_font_cfg.DstFont->EllipsisChar == (ImWchar)-1)
new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar; new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar;
ImFontAtlasUpdateConfigDataPointers(this);
// Invalidate texture // Invalidate texture
TexReady = false; TexReady = false;
ClearTexData(); ClearTexData();
@ -2126,7 +2236,7 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
if (font_cfg.Name[0] == '\0') if (font_cfg.Name[0] == '\0')
ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf, %dpx", (int)font_cfg.SizePixels); ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf, %dpx", (int)font_cfg.SizePixels);
font_cfg.EllipsisChar = (ImWchar)0x0085; font_cfg.EllipsisChar = (ImWchar)0x0085;
font_cfg.GlyphOffset.y = 1.0f * IM_FLOOR(font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units font_cfg.GlyphOffset.y = 1.0f * IM_TRUNC(font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units
const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85(); const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault(); const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault();
@ -2156,13 +2266,14 @@ ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels,
} }
// NB: Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build(). // NB: Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build().
ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
{ {
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
IM_ASSERT(font_cfg.FontData == NULL); IM_ASSERT(font_cfg.FontData == NULL);
font_cfg.FontData = ttf_data; IM_ASSERT(font_data_size > 100 && "Incorrect value for font_data_size!"); // Heuristic to prevent accidentally passing a wrong value to font_data_size.
font_cfg.FontDataSize = ttf_size; font_cfg.FontData = font_data;
font_cfg.FontDataSize = font_data_size;
font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels; font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels;
if (glyph_ranges) if (glyph_ranges)
font_cfg.GlyphRanges = glyph_ranges; font_cfg.GlyphRanges = glyph_ranges;
@ -2377,7 +2488,10 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo); const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo);
IM_ASSERT(font_offset >= 0 && "FontData is incorrect, or FontNo cannot be found."); IM_ASSERT(font_offset >= 0 && "FontData is incorrect, or FontNo cannot be found.");
if (!stbtt_InitFont(&src_tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset)) if (!stbtt_InitFont(&src_tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset))
{
IM_ASSERT(0 && "stbtt_InitFont(): failed to parse FontData. It is correct and complete? Check FontDataSize.");
return false; return false;
}
// Measure highest codepoints // Measure highest codepoints
ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex]; ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
@ -2459,7 +2573,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
// Convert our ranges in the format stb_truetype wants // Convert our ranges in the format stb_truetype wants
ImFontConfig& cfg = atlas->ConfigData[src_i]; ImFontConfig& cfg = atlas->ConfigData[src_i];
src_tmp.PackRange.font_size = cfg.SizePixels; src_tmp.PackRange.font_size = cfg.SizePixels * cfg.RasterizerDensity;
src_tmp.PackRange.first_unicode_codepoint_in_range = 0; src_tmp.PackRange.first_unicode_codepoint_in_range = 0;
src_tmp.PackRange.array_of_unicode_codepoints = src_tmp.GlyphsList.Data; src_tmp.PackRange.array_of_unicode_codepoints = src_tmp.GlyphsList.Data;
src_tmp.PackRange.num_chars = src_tmp.GlyphsList.Size; src_tmp.PackRange.num_chars = src_tmp.GlyphsList.Size;
@ -2468,7 +2582,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
src_tmp.PackRange.v_oversample = (unsigned char)cfg.OversampleV; src_tmp.PackRange.v_oversample = (unsigned char)cfg.OversampleV;
// Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects) // Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects)
const float scale = (cfg.SizePixels > 0) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels); const float scale = (cfg.SizePixels > 0.0f) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels * cfg.RasterizerDensity) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels * cfg.RasterizerDensity);
const int padding = atlas->TexGlyphPadding; const int padding = atlas->TexGlyphPadding;
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++) for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)
{ {
@ -2564,12 +2678,14 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
int unscaled_ascent, unscaled_descent, unscaled_line_gap; int unscaled_ascent, unscaled_descent, unscaled_line_gap;
stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap); stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);
const float ascent = ImFloor(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1)); const float ascent = ImTrunc(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1));
const float descent = ImFloor(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1)); const float descent = ImTrunc(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1));
ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent); ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
const float font_off_x = cfg.GlyphOffset.x; const float font_off_x = cfg.GlyphOffset.x;
const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent); const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent);
const float inv_rasterization_scale = 1.0f / cfg.RasterizerDensity;
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++) for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
{ {
// Register glyph // Register glyph
@ -2578,7 +2694,11 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
stbtt_aligned_quad q; stbtt_aligned_quad q;
float unused_x = 0.0f, unused_y = 0.0f; float unused_x = 0.0f, unused_y = 0.0f;
stbtt_GetPackedQuad(src_tmp.PackedChars, atlas->TexWidth, atlas->TexHeight, glyph_i, &unused_x, &unused_y, &q, 0); stbtt_GetPackedQuad(src_tmp.PackedChars, atlas->TexWidth, atlas->TexHeight, glyph_i, &unused_x, &unused_y, &q, 0);
dst_font->AddGlyph(&cfg, (ImWchar)codepoint, q.x0 + font_off_x, q.y0 + font_off_y, q.x1 + font_off_x, q.y1 + font_off_y, q.s0, q.t0, q.s1, q.t1, pc.xadvance); float x0 = q.x0 * inv_rasterization_scale + font_off_x;
float y0 = q.y0 * inv_rasterization_scale + font_off_y;
float x1 = q.x1 * inv_rasterization_scale + font_off_x;
float y1 = q.y1 * inv_rasterization_scale + font_off_y;
dst_font->AddGlyph(&cfg, (ImWchar)codepoint, x0, y0, x1, y1, q.s0, q.t0, q.s1, q.t1, pc.xadvance * inv_rasterization_scale);
} }
} }
@ -2598,19 +2718,31 @@ const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype()
#endif // IMGUI_ENABLE_STB_TRUETYPE #endif // IMGUI_ENABLE_STB_TRUETYPE
void ImFontAtlasUpdateConfigDataPointers(ImFontAtlas* atlas)
{
for (ImFontConfig& font_cfg : atlas->ConfigData)
{
ImFont* font = font_cfg.DstFont;
if (!font_cfg.MergeMode)
{
font->ConfigData = &font_cfg;
font->ConfigDataCount = 0;
}
font->ConfigDataCount++;
}
}
void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent) void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent)
{ {
if (!font_config->MergeMode) if (!font_config->MergeMode)
{ {
font->ClearOutputData(); font->ClearOutputData();
font->FontSize = font_config->SizePixels; font->FontSize = font_config->SizePixels;
font->ConfigData = font_config; IM_ASSERT(font->ConfigData == font_config);
font->ConfigDataCount = 0;
font->ContainerAtlas = atlas; font->ContainerAtlas = atlas;
font->Ascent = ascent; font->Ascent = ascent;
font->Descent = descent; font->Descent = descent;
} }
font->ConfigDataCount++;
} }
void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque) void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque)
@ -2757,6 +2889,13 @@ static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas)
// Note: this is called / shared by both the stb_truetype and the FreeType builder // Note: this is called / shared by both the stb_truetype and the FreeType builder
void ImFontAtlasBuildInit(ImFontAtlas* atlas) void ImFontAtlasBuildInit(ImFontAtlas* atlas)
{ {
// Round font size
// - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet.
// - Note that using io.FontGlobalScale or SetWindowFontScale(), with are legacy-ish, partially supported features, can still lead to unrounded sizes.
// - We may support it better later and remove this rounding.
for (ImFontConfig& cfg : atlas->ConfigData)
cfg.SizePixels = ImTrunc(cfg.SizePixels);
// Register texture region for mouse cursors or standard white pixels // Register texture region for mouse cursors or standard white pixels
if (atlas->PackIdMouseCursors < 0) if (atlas->PackIdMouseCursors < 0)
{ {
@ -2798,9 +2937,9 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas)
} }
// Build all fonts lookup tables // Build all fonts lookup tables
for (int i = 0; i < atlas->Fonts.Size; i++) for (ImFont* font : atlas->Fonts)
if (atlas->Fonts[i]->DirtyLookupTables) if (font->DirtyLookupTables)
atlas->Fonts[i]->BuildLookupTable(); font->BuildLookupTable();
atlas->TexReady = true; atlas->TexReady = true;
} }
@ -3165,6 +3304,7 @@ void ImFont::BuildLookupTable()
max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint); max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint);
// Build lookup table // Build lookup table
IM_ASSERT(Glyphs.Size > 0 && "Font has not loaded glyph!");
IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved
IndexAdvanceX.clear(); IndexAdvanceX.clear();
IndexLookup.clear(); IndexLookup.clear();
@ -3281,7 +3421,7 @@ void ImFont::AddGlyph(const ImFontConfig* cfg, ImWchar codepoint, float x0, floa
advance_x = ImClamp(advance_x, cfg->GlyphMinAdvanceX, cfg->GlyphMaxAdvanceX); advance_x = ImClamp(advance_x, cfg->GlyphMinAdvanceX, cfg->GlyphMaxAdvanceX);
if (advance_x != advance_x_original) if (advance_x != advance_x_original)
{ {
float char_off_x = cfg->PixelSnapH ? ImFloor((advance_x - advance_x_original) * 0.5f) : (advance_x - advance_x_original) * 0.5f; float char_off_x = cfg->PixelSnapH ? ImTrunc((advance_x - advance_x_original) * 0.5f) : (advance_x - advance_x_original) * 0.5f;
x0 += char_off_x; x0 += char_off_x;
x1 += char_off_x; x1 += char_off_x;
} }
@ -3549,8 +3689,8 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im
if (glyph->Colored) if (glyph->Colored)
col |= ~IM_COL32_A_MASK; col |= ~IM_COL32_A_MASK;
float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f; float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f;
float x = IM_FLOOR(pos.x); float x = IM_TRUNC(pos.x);
float y = IM_FLOOR(pos.y); float y = IM_TRUNC(pos.y);
draw_list->PrimReserve(6, 4); draw_list->PrimReserve(6, 4);
draw_list->PrimRectUV(ImVec2(x + glyph->X0 * scale, y + glyph->Y0 * scale), ImVec2(x + glyph->X1 * scale, y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col); draw_list->PrimRectUV(ImVec2(x + glyph->X0 * scale, y + glyph->Y0 * scale), ImVec2(x + glyph->X1 * scale, y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col);
} }
@ -3562,8 +3702,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls. text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls.
// Align to be pixel perfect // Align to be pixel perfect
float x = IM_FLOOR(pos.x); float x = IM_TRUNC(pos.x);
float y = IM_FLOOR(pos.y); float y = IM_TRUNC(pos.y);
if (y > clip_rect.w) if (y > clip_rect.w)
return; return;
@ -3747,6 +3887,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
// - RenderArrow() // - RenderArrow()
// - RenderBullet() // - RenderBullet()
// - RenderCheckMark() // - RenderCheckMark()
// - RenderArrowDockMenu()
// - RenderArrowPointingAt() // - RenderArrowPointingAt()
// - RenderRectFilledRangeH() // - RenderRectFilledRangeH()
// - RenderRectFilledWithHole() // - RenderRectFilledWithHole()
@ -3821,6 +3962,14 @@ void ImGui::RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half
} }
} }
// This is less wide than RenderArrow() and we use in dock nodes instead of the regular RenderArrow() to denote a change of functionality,
// and because the saved space means that the left-most tab label can stay at exactly the same position as the label of a loose window.
void ImGui::RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col)
{
draw_list->AddRectFilled(p_min + ImVec2(sz * 0.20f, sz * 0.15f), p_min + ImVec2(sz * 0.80f, sz * 0.30f), col);
RenderArrowPointingAt(draw_list, p_min + ImVec2(sz * 0.50f, sz * 0.85f), ImVec2(sz * 0.30f, sz * 0.40f), ImGuiDir_Down, col);
}
static inline float ImAcos01(float x) static inline float ImAcos01(float x)
{ {
if (x <= 0.0f) return IM_PI * 0.5f; if (x <= 0.0f) return IM_PI * 0.5f;
@ -3863,8 +4012,8 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im
} }
else else
{ {
draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding, IM_PI - arc0_e, IM_PI - arc0_b, 3); // BL draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding, IM_PI - arc0_e, IM_PI - arc0_b); // BL
draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e, 3); // TR draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e); // TR
} }
if (p1.x > rect.Min.x + rounding) if (p1.x > rect.Min.x + rounding)
{ {
@ -3883,8 +4032,8 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im
} }
else else
{ {
draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b, 3); // TR draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b); // TR
draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e, 3); // BR draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e); // BR
} }
} }
draw_list->PathFillConvex(col); draw_list->PathFillConvex(col);
@ -3906,6 +4055,17 @@ void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer,
if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomRight); if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomRight);
} }
ImDrawFlags ImGui::CalcRoundingFlagsForRectInRect(const ImRect& r_in, const ImRect& r_outer, float threshold)
{
bool round_l = r_in.Min.x <= r_outer.Min.x + threshold;
bool round_r = r_in.Max.x >= r_outer.Max.x - threshold;
bool round_t = r_in.Min.y <= r_outer.Min.y + threshold;
bool round_b = r_in.Max.y >= r_outer.Max.y - threshold;
return ImDrawFlags_RoundCornersNone
| ((round_t && round_l) ? ImDrawFlags_RoundCornersTopLeft : 0) | ((round_t && round_r) ? ImDrawFlags_RoundCornersTopRight : 0)
| ((round_b && round_l) ? ImDrawFlags_RoundCornersBottomLeft : 0) | ((round_b && round_r) ? ImDrawFlags_RoundCornersBottomRight : 0);
}
// Helper for ColorPicker4() // Helper for ColorPicker4()
// NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that. // NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that.
// Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding altogether. // Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding altogether.
@ -4071,8 +4231,8 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// ProggyClean.ttf // ProggyClean.ttf
// Copyright (c) 2004, 2005 Tristan Grimmer // Copyright (c) 2004, 2005 Tristan Grimmer
// MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip) // MIT license (see License.txt in http://www.proggyfonts.net/index.php?menu=download)
// Download and more information at http://upperbounds.net // Download and more information at http://www.proggyfonts.net or http://upperboundsinteractive.com/fonts.php
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// File: 'ProggyClean.ttf' (41208 bytes) // File: 'ProggyClean.ttf' (41208 bytes)
// Exported using misc/fonts/binary_to_compressed_c.cpp (with compression + base85 string encoding). // Exported using misc/fonts/binary_to_compressed_c.cpp (with compression + base85 string encoding).

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
// MIT License // MIT License
// Copyright (c) 2022 Evan Pezent // Copyright (c) 2023 Evan Pezent
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@ -20,7 +20,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// ImPlot v0.14 // ImPlot v0.17
/* /*
@ -31,6 +31,9 @@ Below is a change-log of API breaking changes only. If you are using one of the
When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot files. When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot files.
You can read releases logs https://github.com/epezent/implot/releases for more details. You can read releases logs https://github.com/epezent/implot/releases for more details.
- 2023/08/20 (0.17) - ImPlotFlags_NoChild was removed as child windows are no longer needed to capture scroll. You can safely remove this flag if you were using it.
- 2023/06/26 (0.15) - Various build fixes related to updates in Dear ImGui internals.
- 2022/11/25 (0.15) - Make PlotText honor ImPlotItemFlags_NoFit.
- 2022/06/19 (0.14) - The signature of ColormapScale has changed to accommodate a new ImPlotColormapScaleFlags parameter - 2022/06/19 (0.14) - The signature of ColormapScale has changed to accommodate a new ImPlotColormapScaleFlags parameter
- 2022/06/17 (0.14) - **IMPORTANT** All PlotX functions now take an ImPlotX_Flags `flags` parameter. Where applicable, it is located before the existing `offset` and `stride` parameters. - 2022/06/17 (0.14) - **IMPORTANT** All PlotX functions now take an ImPlotX_Flags `flags` parameter. Where applicable, it is located before the existing `offset` and `stride` parameters.
If you were providing offset and stride values, you will need to update your function call to include a `flags` value. If you fail to do this, you will likely see If you were providing offset and stride values, you will need to update your function call to include a `flags` value. If you fail to do this, you will likely see
@ -133,6 +136,11 @@ You can read releases logs https://github.com/epezent/implot/releases for more d
#define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All #define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All
#endif #endif
// Support for pre-1.89.7 versions.
#if (IMGUI_VERSION_NUM < 18966)
#define ImGuiButtonFlags_AllowOverlap ImGuiButtonFlags_AllowItemOverlap
#endif
// Visual Studio warnings // Visual Studio warnings
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
@ -285,35 +293,35 @@ struct ImPlotStyleVarInfo {
static const ImPlotStyleVarInfo GPlotStyleVarInfo[] = static const ImPlotStyleVarInfo GPlotStyleVarInfo[] =
{ {
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, LineWeight) }, // ImPlotStyleVar_LineWeight { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, LineWeight) }, // ImPlotStyleVar_LineWeight
{ ImGuiDataType_S32, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker { ImGuiDataType_S32, 1, (ImU32)offsetof(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerSize) }, // ImPlotStyleVar_MarkerSize { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MarkerSize) }, // ImPlotStyleVar_MarkerSize
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, FillAlpha) }, // ImPlotStyleVar_FillAlpha { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, FillAlpha) }, // ImPlotStyleVar_FillAlpha
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarWeight) }, // ImPlotStyleVar_ErrorBarWeight { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, ErrorBarWeight) }, // ImPlotStyleVar_ErrorBarWeight
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitHeight) }, // ImPlotStyleVar_DigitalBitHeight { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, DigitalBitHeight) }, // ImPlotStyleVar_DigitalBitHeight
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitGap) }, // ImPlotStyleVar_DigitalBitGap { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, DigitalBitGap) }, // ImPlotStyleVar_DigitalBitGap
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotBorderSize) }, // ImPlotStyleVar_PlotBorderSize { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, PlotBorderSize) }, // ImPlotStyleVar_PlotBorderSize
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorAlpha) }, // ImPlotStyleVar_MinorAlpha { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MinorAlpha) }, // ImPlotStyleVar_MinorAlpha
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorTickLen) }, // ImPlotStyleVar_MajorTickLen { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorTickLen) }, // ImPlotStyleVar_MajorTickLen
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorTickLen) }, // ImPlotStyleVar_MinorTickLen { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorTickLen) }, // ImPlotStyleVar_MinorTickLen
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorTickSize) }, // ImPlotStyleVar_MajorTickSize { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorTickSize) }, // ImPlotStyleVar_MajorTickSize
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorTickSize) }, // ImPlotStyleVar_MinorTickSize { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorTickSize) }, // ImPlotStyleVar_MinorTickSize
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorGridSize) }, // ImPlotStyleVar_MajorGridSize { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorGridSize) }, // ImPlotStyleVar_MajorGridSize
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorGridSize) }, // ImPlotStyleVar_MinorGridSize { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorGridSize) }, // ImPlotStyleVar_MinorGridSize
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotPadding) }, // ImPlotStyleVar_PlotPadding { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotPadding) }, // ImPlotStyleVar_PlotPadding
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LabelPadding) }, // ImPlotStyleVar_LabelPaddine { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LabelPadding) }, // ImPlotStyleVar_LabelPaddine
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendPadding) }, // ImPlotStyleVar_LegendPadding { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendPadding) }, // ImPlotStyleVar_LegendPadding
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendInnerPadding) }, // ImPlotStyleVar_LegendInnerPadding { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendInnerPadding) }, // ImPlotStyleVar_LegendInnerPadding
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendSpacing) }, // ImPlotStyleVar_LegendSpacing { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendSpacing) }, // ImPlotStyleVar_LegendSpacing
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MousePosPadding) }, // ImPlotStyleVar_MousePosPadding { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MousePosPadding) }, // ImPlotStyleVar_MousePosPadding
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, AnnotationPadding) }, // ImPlotStyleVar_AnnotationPadding { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, AnnotationPadding) }, // ImPlotStyleVar_AnnotationPadding
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, FitPadding) }, // ImPlotStyleVar_FitPadding { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, FitPadding) }, // ImPlotStyleVar_FitPadding
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotDefaultSize) }, // ImPlotStyleVar_PlotDefaultSize { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotDefaultSize) }, // ImPlotStyleVar_PlotDefaultSize
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotMinSize) } // ImPlotStyleVar_PlotMinSize { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotMinSize) } // ImPlotStyleVar_PlotMinSize
}; };
static const ImPlotStyleVarInfo* GetPlotStyleVarInfo(ImPlotStyleVar idx) { static const ImPlotStyleVarInfo* GetPlotStyleVarInfo(ImPlotStyleVar idx) {
@ -333,8 +341,8 @@ void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char *te
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImFont* font = g.Font; ImFont* font = g.Font;
// Align to be pixel perfect // Align to be pixel perfect
pos.x = IM_FLOOR(pos.x); pos.x = ImFloor(pos.x);
pos.y = IM_FLOOR(pos.y); pos.y = ImFloor(pos.y);
const float scale = g.FontSize / font->FontSize; const float scale = g.FontSize / font->FontSize;
const char* s = text_begin; const char* s = text_begin;
int chars_exp = (int)(text_end - s); int chars_exp = (int)(text_end - s);
@ -485,10 +493,6 @@ void Initialize(ImPlotContext* ctx) {
} }
void ResetCtxForNextPlot(ImPlotContext* ctx) { void ResetCtxForNextPlot(ImPlotContext* ctx) {
// end child window if it was made
if (ctx->ChildWindowMade)
ImGui::EndChild();
ctx->ChildWindowMade = false;
// reset the next plot/item data // reset the next plot/item data
ctx->NextPlotData.Reset(); ctx->NextPlotData.Reset();
ctx->NextItemData.Reset(); ctx->NextItemData.Reset();
@ -582,6 +586,28 @@ ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& s
return legend_size; return legend_size;
} }
bool ClampLegendRect(ImRect& legend_rect, const ImRect& outer_rect, const ImVec2& pad) {
bool clamped = false;
ImRect outer_rect_pad(outer_rect.Min + pad, outer_rect.Max - pad);
if (legend_rect.Min.x < outer_rect_pad.Min.x) {
legend_rect.Min.x = outer_rect_pad.Min.x;
clamped = true;
}
if (legend_rect.Min.y < outer_rect_pad.Min.y) {
legend_rect.Min.y = outer_rect_pad.Min.y;
clamped = true;
}
if (legend_rect.Max.x > outer_rect_pad.Max.x) {
legend_rect.Max.x = outer_rect_pad.Max.x;
clamped = true;
}
if (legend_rect.Max.y > outer_rect_pad.Max.y) {
legend_rect.Max.y = outer_rect_pad.Max.y;
clamped = true;
}
return clamped;
}
int LegendSortingComp(const void* _a, const void* _b) { int LegendSortingComp(const void* _a, const void* _b) {
ImPlotItemGroup* items = GImPlot->SortItems; ImPlotItemGroup* items = GImPlot->SortItems;
const int a = *(const int*)_a; const int a = *(const int*)_a;
@ -1296,12 +1322,12 @@ bool DragFloat(const char*, F*, float, F, F) {
template <> template <>
bool DragFloat<double>(const char* label, double* v, float v_speed, double v_min, double v_max) { bool DragFloat<double>(const char* label, double* v, float v_speed, double v_min, double v_max) {
return ImGui::DragScalar(label, ImGuiDataType_Double, v, v_speed, &v_min, &v_max, "%.3f", 1); return ImGui::DragScalar(label, ImGuiDataType_Double, v, v_speed, &v_min, &v_max, "%.3g", 1);
} }
template <> template <>
bool DragFloat<float>(const char* label, float* v, float v_speed, float v_min, float v_max) { bool DragFloat<float>(const char* label, float* v, float v_speed, float v_min, float v_max) {
return ImGui::DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, "%.3f", 1); return ImGui::DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, "%.3g", 1);
} }
inline void BeginDisabledControls(bool cond) { inline void BeginDisabledControls(bool cond) {
@ -1548,7 +1574,7 @@ void ShowPlotContextMenu(ImPlotPlot& plot) {
ImFlipFlag(plot.Flags, ImPlotFlags_Crosshairs); ImFlipFlag(plot.Flags, ImPlotFlags_Crosshairs);
ImGui::EndMenu(); ImGui::EndMenu();
} }
if (gp.CurrentSubplot != nullptr && !ImHasFlag(gp.CurrentPlot->Flags, ImPlotSubplotFlags_NoMenus)) { if (gp.CurrentSubplot != nullptr && !ImHasFlag(gp.CurrentSubplot->Flags, ImPlotSubplotFlags_NoMenus)) {
ImGui::Separator(); ImGui::Separator();
if ((ImGui::BeginMenu("Subplots"))) { if ((ImGui::BeginMenu("Subplots"))) {
ShowSubplotsContextMenu(*gp.CurrentSubplot); ShowSubplotsContextMenu(*gp.CurrentSubplot);
@ -1808,7 +1834,7 @@ bool UpdateInput(ImPlotPlot& plot) {
// BUTTON STATE ----------------------------------------------------------- // BUTTON STATE -----------------------------------------------------------
const ImGuiButtonFlags plot_button_flags = ImGuiButtonFlags_AllowItemOverlap const ImGuiButtonFlags plot_button_flags = ImGuiButtonFlags_AllowOverlap
| ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClick
| ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDoubleClick
| ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonLeft
@ -1818,7 +1844,9 @@ bool UpdateInput(ImPlotPlot& plot) {
| plot_button_flags; | plot_button_flags;
const bool plot_clicked = ImGui::ButtonBehavior(plot.PlotRect,plot.ID,&plot.Hovered,&plot.Held,plot_button_flags); const bool plot_clicked = ImGui::ButtonBehavior(plot.PlotRect,plot.ID,&plot.Hovered,&plot.Held,plot_button_flags);
ImGui::SetItemAllowOverlap(); #if (IMGUI_VERSION_NUM < 18966)
ImGui::SetItemAllowOverlap(); // Handled by ButtonBehavior()
#endif
if (plot_clicked) { if (plot_clicked) {
if (!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect) && IO.MouseClicked[gp.InputMap.Select] && ImHasFlag(IO.KeyMods, gp.InputMap.SelectMod)) { if (!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect) && IO.MouseClicked[gp.InputMap.Select] && ImHasFlag(IO.KeyMods, gp.InputMap.SelectMod)) {
@ -1951,10 +1979,12 @@ bool UpdateInput(ImPlotPlot& plot) {
// SCROLL INPUT ----------------------------------------------------------- // SCROLL INPUT -----------------------------------------------------------
if (any_hov && IO.MouseWheel != 0 && ImHasFlag(IO.KeyMods, gp.InputMap.ZoomMod)) { if (any_hov && ImHasFlag(IO.KeyMods, gp.InputMap.ZoomMod)) {
float zoom_rate = gp.InputMap.ZoomRate; float zoom_rate = gp.InputMap.ZoomRate;
if (IO.MouseWheel > 0) if (IO.MouseWheel == 0.0f)
zoom_rate = 0;
else if (IO.MouseWheel > 0)
zoom_rate = (-zoom_rate) / (1.0f + (2.0f * zoom_rate)); zoom_rate = (-zoom_rate) / (1.0f + (2.0f * zoom_rate));
ImVec2 rect_size = plot.PlotRect.GetSize(); ImVec2 rect_size = plot.PlotRect.GetSize();
float tx = ImRemap(IO.MousePos.x, plot.PlotRect.Min.x, plot.PlotRect.Max.x, 0.0f, 1.0f); float tx = ImRemap(IO.MousePos.x, plot.PlotRect.Min.x, plot.PlotRect.Max.x, 0.0f, 1.0f);
@ -1965,14 +1995,17 @@ bool UpdateInput(ImPlotPlot& plot) {
const bool equal_zoom = axis_equal && x_axis.OrthoAxis != nullptr; const bool equal_zoom = axis_equal && x_axis.OrthoAxis != nullptr;
const bool equal_locked = (equal_zoom != false) && x_axis.OrthoAxis->IsInputLocked(); const bool equal_locked = (equal_zoom != false) && x_axis.OrthoAxis->IsInputLocked();
if (x_hov[i] && !x_axis.IsInputLocked() && !equal_locked) { if (x_hov[i] && !x_axis.IsInputLocked() && !equal_locked) {
float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f; ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID);
const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - rect_size.x * tx * zoom_rate * correction); if (zoom_rate != 0.0f) {
const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x + rect_size.x * (1 - tx) * zoom_rate * correction); float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f;
x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l); const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - rect_size.x * tx * zoom_rate * correction);
x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r); const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x + rect_size.x * (1 - tx) * zoom_rate * correction);
if (axis_equal && x_axis.OrthoAxis != nullptr) x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l);
x_axis.OrthoAxis->SetAspect(x_axis.GetAspect()); x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r);
changed = true; if (axis_equal && x_axis.OrthoAxis != nullptr)
x_axis.OrthoAxis->SetAspect(x_axis.GetAspect());
changed = true;
}
} }
} }
for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
@ -1980,14 +2013,17 @@ bool UpdateInput(ImPlotPlot& plot) {
const bool equal_zoom = axis_equal && y_axis.OrthoAxis != nullptr; const bool equal_zoom = axis_equal && y_axis.OrthoAxis != nullptr;
const bool equal_locked = equal_zoom && y_axis.OrthoAxis->IsInputLocked(); const bool equal_locked = equal_zoom && y_axis.OrthoAxis->IsInputLocked();
if (y_hov[i] && !y_axis.IsInputLocked() && !equal_locked) { if (y_hov[i] && !y_axis.IsInputLocked() && !equal_locked) {
float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f; ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID);
const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - rect_size.y * ty * zoom_rate * correction); if (zoom_rate != 0.0f) {
const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y + rect_size.y * (1 - ty) * zoom_rate * correction); float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f;
y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b); const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - rect_size.y * ty * zoom_rate * correction);
y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t); const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y + rect_size.y * (1 - ty) * zoom_rate * correction);
if (axis_equal && y_axis.OrthoAxis != nullptr) y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b);
y_axis.OrthoAxis->SetAspect(y_axis.GetAspect()); y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t);
changed = true; if (axis_equal && y_axis.OrthoAxis != nullptr)
y_axis.OrthoAxis->SetAspect(y_axis.GetAspect());
changed = true;
}
} }
} }
} }
@ -2269,19 +2305,19 @@ void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImP
void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags) { void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags) {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, IM_ASSERT_USER_ERROR((gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked) || (gp.CurrentSubplot != nullptr && gp.CurrentPlot == nullptr),
"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); "Setup needs to be called after BeginPlot or BeginSubplots and before any setup locking functions (e.g. PlotX)!");
IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, if (gp.CurrentItems) {
"SetupLegend() needs to be called within an itemized context!"); ImPlotLegend& legend = gp.CurrentItems->Legend;
ImPlotLegend& legend = gp.CurrentItems->Legend; // check and set location
// check and set location if (location != legend.PreviousLocation)
if (location != legend.PreviousLocation) legend.Location = location;
legend.Location = location; legend.PreviousLocation = location;
legend.PreviousLocation = location; // check and set flags
// check and set flags if (flags != legend.PreviousFlags)
if (flags != legend.PreviousFlags) legend.Flags = flags;
legend.Flags = flags; legend.PreviousFlags = flags;
legend.PreviousFlags = flags; }
} }
void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags) { void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags) {
@ -2393,22 +2429,6 @@ bool BeginPlot(const char* title_id, const ImVec2& size, ImPlotFlags flags) {
for (int i = 0; i < ImAxis_COUNT; ++i) for (int i = 0; i < ImAxis_COUNT; ++i)
ApplyNextPlotData(i); ApplyNextPlotData(i);
// capture scroll with a child region
if (!ImHasFlag(plot.Flags, ImPlotFlags_NoChild)) {
ImVec2 child_size;
if (gp.CurrentSubplot != nullptr)
child_size = gp.CurrentSubplot->CellSize;
else
child_size = ImVec2(size.x == 0 ? gp.Style.PlotDefaultSize.x : size.x, size.y == 0 ? gp.Style.PlotDefaultSize.y : size.y);
ImGui::BeginChild(title_id, child_size, false, ImGuiWindowFlags_NoScrollbar);
Window = ImGui::GetCurrentWindow();
Window->ScrollMax.y = 1.0f;
gp.ChildWindowMade = true;
}
else {
gp.ChildWindowMade = false;
}
// clear text buffers // clear text buffers
plot.ClearTextBuffer(); plot.ClearTextBuffer();
plot.SetTitle(title_id); plot.SetTitle(title_id);
@ -2550,7 +2570,7 @@ void SetupFinish() {
// (2) get y tick labels (needed for left/right pad) // (2) get y tick labels (needed for left/right pad)
for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
ImPlotAxis& axis = plot.YAxis(i); ImPlotAxis& axis = plot.YAxis(i);
if (axis.WillRender() && axis.ShowDefaultTicks) { if (axis.WillRender() && axis.ShowDefaultTicks && plot_height > 0) {
axis.Locator(axis.Ticker, axis.Range, plot_height, true, axis.Formatter, axis.FormatterData); axis.Locator(axis.Ticker, axis.Range, plot_height, true, axis.Formatter, axis.FormatterData);
} }
} }
@ -2563,7 +2583,7 @@ void SetupFinish() {
// (4) get x ticks // (4) get x ticks
for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {
ImPlotAxis& axis = plot.XAxis(i); ImPlotAxis& axis = plot.XAxis(i);
if (axis.WillRender() && axis.ShowDefaultTicks) { if (axis.WillRender() && axis.ShowDefaultTicks && plot_width > 0) {
axis.Locator(axis.Ticker, axis.Range, plot_width, false, axis.Formatter, axis.FormatterData); axis.Locator(axis.Ticker, axis.Range, plot_width, false, axis.Formatter, axis.FormatterData);
} }
} }
@ -2758,7 +2778,7 @@ void EndPlot() {
// FINAL RENDER ----------------------------------------------------------- // FINAL RENDER -----------------------------------------------------------
const bool render_border = gp.Style.PlotBorderSize > 0 && gp.Style.Colors[ImPlotCol_PlotBorder].w > 0; const bool render_border = gp.Style.PlotBorderSize > 0 && GetStyleColorVec4(ImPlotCol_PlotBorder).w > 0;
const bool any_x_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); const bool any_x_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES);
const bool any_y_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); const bool any_y_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES);
@ -3023,24 +3043,57 @@ void EndPlot() {
legend.Location, legend.Location,
legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding); legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding);
legend.Rect = ImRect(legend_pos, legend_pos + legend_size); legend.Rect = ImRect(legend_pos, legend_pos + legend_size);
// test hover legend.RectClamped = legend.Rect;
legend.Hovered = ImGui::IsWindowHovered() && legend.Rect.Contains(IO.MousePos); const bool legend_scrollable = ClampLegendRect(legend.RectClamped,
legend_out ? plot.FrameRect : plot.PlotRect,
legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding
);
const ImGuiButtonFlags legend_button_flags = ImGuiButtonFlags_AllowOverlap
| ImGuiButtonFlags_PressedOnClick
| ImGuiButtonFlags_PressedOnDoubleClick
| ImGuiButtonFlags_MouseButtonLeft
| ImGuiButtonFlags_MouseButtonRight
| ImGuiButtonFlags_MouseButtonMiddle
| ImGuiButtonFlags_FlattenChildren;
ImGui::KeepAliveID(plot.Items.ID);
ImGui::ButtonBehavior(legend.RectClamped, plot.Items.ID, &legend.Hovered, &legend.Held, legend_button_flags);
legend.Hovered = legend.Hovered || (ImGui::IsWindowHovered() && legend.RectClamped.Contains(IO.MousePos));
if (legend_out) if (legend_scrollable) {
ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true); if (legend.Hovered) {
else ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.Items.ID);
PushPlotClipRect(); if (IO.MouseWheel != 0.0f) {
ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); ImVec2 max_step = legend.Rect.GetSize() * 0.67f;
ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); float font_size = ImGui::GetCurrentWindow()->CalcFontSize();
DrawList.AddRectFilled(legend.Rect.Min, legend.Rect.Max, col_bg); float scroll_step = ImFloor(ImMin(2 * font_size, max_step.x));
DrawList.AddRect(legend.Rect.Min, legend.Rect.Max, col_bd); legend.Scroll.x += scroll_step * IO.MouseWheel;
legend.Scroll.y += scroll_step * IO.MouseWheel;
}
}
const ImVec2 min_scroll_offset = legend.RectClamped.GetSize() - legend.Rect.GetSize();
legend.Scroll.x = ImClamp(legend.Scroll.x, min_scroll_offset.x, 0.0f);
legend.Scroll.y = ImClamp(legend.Scroll.y, min_scroll_offset.y, 0.0f);
const ImVec2 scroll_offset = legend_horz ? ImVec2(legend.Scroll.x, 0) : ImVec2(0, legend.Scroll.y);
ImVec2 legend_offset = legend.RectClamped.Min - legend.Rect.Min + scroll_offset;
legend.Rect.Min += legend_offset;
legend.Rect.Max += legend_offset;
} else {
legend.Scroll = ImVec2(0,0);
}
const ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg);
const ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder);
ImGui::PushClipRect(legend.RectClamped.Min, legend.RectClamped.Max, true);
DrawList.AddRectFilled(legend.RectClamped.Min, legend.RectClamped.Max, col_bg);
bool legend_contextable = ShowLegendEntries(plot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList) bool legend_contextable = ShowLegendEntries(plot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList)
&& !ImHasFlag(legend.Flags, ImPlotLegendFlags_NoMenus); && !ImHasFlag(legend.Flags, ImPlotLegendFlags_NoMenus);
DrawList.AddRect(legend.RectClamped.Min, legend.RectClamped.Max, col_bd);
ImGui::PopClipRect();
// main ctx menu // main ctx menu
if (gp.OpenContextThisFrame && legend_contextable && !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus)) if (gp.OpenContextThisFrame && legend_contextable && !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus))
ImGui::OpenPopup("##LegendContext"); ImGui::OpenPopup("##LegendContext");
ImGui::PopClipRect();
if (ImGui::BeginPopup("##LegendContext")) { if (ImGui::BeginPopup("##LegendContext")) {
ImGui::Text("Legend"); ImGui::Separator(); ImGui::Text("Legend"); ImGui::Separator();
if (ShowLegendContextMenu(legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend))) if (ShowLegendContextMenu(legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend)))
@ -3350,7 +3403,7 @@ bool BeginSubplots(const char* title, int rows, int cols, const ImVec2& size, Im
subplot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); subplot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size);
subplot.GridRect.Min = subplot.FrameRect.Min + half_pad + ImVec2(0,pad_top); subplot.GridRect.Min = subplot.FrameRect.Min + half_pad + ImVec2(0,pad_top);
subplot.GridRect.Max = subplot.FrameRect.Max - half_pad; subplot.GridRect.Max = subplot.FrameRect.Max - half_pad;
subplot.FrameHovered = subplot.FrameRect.Contains(ImGui::GetMousePos()) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows); subplot.FrameHovered = subplot.FrameRect.Contains(ImGui::GetMousePos()) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows|ImGuiHoveredFlags_AllowWhenBlockedByActiveItem);
// outside legend adjustments (TODO: make function) // outside legend adjustments (TODO: make function)
const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems); const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems);
@ -3397,7 +3450,7 @@ bool BeginSubplots(const char* title, int rows, int cols, const ImVec2& size, Im
ImGui::KeepAliveID(sep_id); ImGui::KeepAliveID(sep_id);
const ImRect sep_bb = ImRect(subplot.GridRect.Min.x, ypos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.x, ypos+SUBPLOT_SPLITTER_HALF_THICKNESS); const ImRect sep_bb = ImRect(subplot.GridRect.Min.x, ypos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.x, ypos+SUBPLOT_SPLITTER_HALF_THICKNESS);
bool sep_hov = false, sep_hld = false; bool sep_hov = false, sep_hld = false;
const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick); const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick);
if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) { if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) {
if (sep_clk && ImGui::IsMouseDoubleClicked(0)) { if (sep_clk && ImGui::IsMouseDoubleClicked(0)) {
float p = (subplot.RowRatios[r] + subplot.RowRatios[r+1])/2; float p = (subplot.RowRatios[r] + subplot.RowRatios[r+1])/2;
@ -3427,7 +3480,7 @@ bool BeginSubplots(const char* title, int rows, int cols, const ImVec2& size, Im
ImGui::KeepAliveID(sep_id); ImGui::KeepAliveID(sep_id);
const ImRect sep_bb = ImRect(xpos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Min.y, xpos+SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.y); const ImRect sep_bb = ImRect(xpos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Min.y, xpos+SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.y);
bool sep_hov = false, sep_hld = false; bool sep_hov = false, sep_hld = false;
const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick); const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick);
if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) { if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) {
if (sep_clk && ImGui::IsMouseDoubleClicked(0)) { if (sep_clk && ImGui::IsMouseDoubleClicked(0)) {
float p = (subplot.ColRatios[c] + subplot.ColRatios[c+1])/2; float p = (subplot.ColRatios[c] + subplot.ColRatios[c+1])/2;
@ -3488,6 +3541,7 @@ void EndSubplots() {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentSubplot != nullptr, "Mismatched BeginSubplots()/EndSubplots()!"); IM_ASSERT_USER_ERROR(gp.CurrentSubplot != nullptr, "Mismatched BeginSubplots()/EndSubplots()!");
ImPlotSubplot& subplot = *gp.CurrentSubplot; ImPlotSubplot& subplot = *gp.CurrentSubplot;
const ImGuiIO& IO = ImGui::GetIO();
// set alignments // set alignments
for (int r = 0; r < subplot.Rows; ++r) for (int r = 0; r < subplot.Rows; ++r)
subplot.RowAlignmentData[r].End(); subplot.RowAlignmentData[r].End();
@ -3506,24 +3560,60 @@ void EndSubplots() {
const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems); const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems);
ImDrawList& DrawList = *ImGui::GetWindowDrawList(); ImDrawList& DrawList = *ImGui::GetWindowDrawList();
if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) { if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) {
const bool legend_horz = ImHasFlag(subplot.Items.Legend.Flags, ImPlotLegendFlags_Horizontal); ImPlotLegend& legend = subplot.Items.Legend;
const bool legend_horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal);
const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz); const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz);
const ImVec2 legend_pos = GetLocationPos(subplot.FrameRect, legend_size, subplot.Items.Legend.Location, gp.Style.PlotPadding); const ImVec2 legend_pos = GetLocationPos(subplot.FrameRect, legend_size, legend.Location, gp.Style.PlotPadding);
subplot.Items.Legend.Rect = ImRect(legend_pos, legend_pos + legend_size); legend.Rect = ImRect(legend_pos, legend_pos + legend_size);
subplot.Items.Legend.Hovered = subplot.FrameHovered && subplot.Items.Legend.Rect.Contains(ImGui::GetIO().MousePos); legend.RectClamped = legend.Rect;
ImGui::PushClipRect(subplot.FrameRect.Min, subplot.FrameRect.Max, true); const bool legend_scrollable = ClampLegendRect(legend.RectClamped,subplot.FrameRect, gp.Style.PlotPadding);
ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); const ImGuiButtonFlags legend_button_flags = ImGuiButtonFlags_AllowOverlap
ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); | ImGuiButtonFlags_PressedOnClick
DrawList.AddRectFilled(subplot.Items.Legend.Rect.Min, subplot.Items.Legend.Rect.Max, col_bg); | ImGuiButtonFlags_PressedOnDoubleClick
DrawList.AddRect(subplot.Items.Legend.Rect.Min, subplot.Items.Legend.Rect.Max, col_bd); | ImGuiButtonFlags_MouseButtonLeft
bool legend_contextable = ShowLegendEntries(subplot.Items, subplot.Items.Legend.Rect, subplot.Items.Legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList) | ImGuiButtonFlags_MouseButtonRight
&& !ImHasFlag(subplot.Items.Legend.Flags, ImPlotLegendFlags_NoMenus); | ImGuiButtonFlags_MouseButtonMiddle
| ImGuiButtonFlags_FlattenChildren;
ImGui::KeepAliveID(subplot.Items.ID);
ImGui::ButtonBehavior(legend.RectClamped, subplot.Items.ID, &legend.Hovered, &legend.Held, legend_button_flags);
legend.Hovered = legend.Hovered || (subplot.FrameHovered && legend.RectClamped.Contains(ImGui::GetIO().MousePos));
if (legend_scrollable) {
if (legend.Hovered) {
ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, subplot.Items.ID);
if (IO.MouseWheel != 0.0f) {
ImVec2 max_step = legend.Rect.GetSize() * 0.67f;
float font_size = ImGui::GetCurrentWindow()->CalcFontSize();
float scroll_step = ImFloor(ImMin(2 * font_size, max_step.x));
legend.Scroll.x += scroll_step * IO.MouseWheel;
legend.Scroll.y += scroll_step * IO.MouseWheel;
}
}
const ImVec2 min_scroll_offset = legend.RectClamped.GetSize() - legend.Rect.GetSize();
legend.Scroll.x = ImClamp(legend.Scroll.x, min_scroll_offset.x, 0.0f);
legend.Scroll.y = ImClamp(legend.Scroll.y, min_scroll_offset.y, 0.0f);
const ImVec2 scroll_offset = legend_horz ? ImVec2(legend.Scroll.x, 0) : ImVec2(0, legend.Scroll.y);
ImVec2 legend_offset = legend.RectClamped.Min - legend.Rect.Min + scroll_offset;
legend.Rect.Min += legend_offset;
legend.Rect.Max += legend_offset;
} else {
legend.Scroll = ImVec2(0,0);
}
const ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg);
const ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder);
ImGui::PushClipRect(legend.RectClamped.Min, legend.RectClamped.Max, true);
DrawList.AddRectFilled(legend.RectClamped.Min, legend.RectClamped.Max, col_bg);
bool legend_contextable = ShowLegendEntries(subplot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList)
&& !ImHasFlag(legend.Flags, ImPlotLegendFlags_NoMenus);
DrawList.AddRect(legend.RectClamped.Min, legend.RectClamped.Max, col_bd);
ImGui::PopClipRect();
if (legend_contextable && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoMenus) && ImGui::GetIO().MouseReleased[gp.InputMap.Menu]) if (legend_contextable && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoMenus) && ImGui::GetIO().MouseReleased[gp.InputMap.Menu])
ImGui::OpenPopup("##LegendContext"); ImGui::OpenPopup("##LegendContext");
ImGui::PopClipRect();
if (ImGui::BeginPopup("##LegendContext")) { if (ImGui::BeginPopup("##LegendContext")) {
ImGui::Text("Legend"); ImGui::Separator(); ImGui::Text("Legend"); ImGui::Separator();
if (ShowLegendContextMenu(subplot.Items.Legend, !ImHasFlag(subplot.Flags, ImPlotFlags_NoLegend))) if (ShowLegendContextMenu(legend, !ImHasFlag(subplot.Flags, ImPlotFlags_NoLegend)))
ImFlipFlag(subplot.Flags, ImPlotFlags_NoLegend); ImFlipFlag(subplot.Flags, ImPlotFlags_NoLegend);
ImGui::EndPopup(); ImGui::EndPopup();
} }
@ -3804,7 +3894,7 @@ IMPLOT_API void TagYV(double y, const ImVec4& color, const char* fmt, va_list ar
static const float DRAG_GRAB_HALF_SIZE = 4.0f; static const float DRAG_GRAB_HALF_SIZE = 4.0f;
bool DragPoint(int n_id, double* x, double* y, const ImVec4& col, float radius, ImPlotDragToolFlags flags) { bool DragPoint(int n_id, double* x, double* y, const ImVec4& col, float radius, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {
ImGui::PushID("#IMPLOT_DRAG_POINT"); ImGui::PushID("#IMPLOT_DRAG_POINT");
IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragPoint() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragPoint() needs to be called between BeginPlot() and EndPlot()!");
SetupLock(); SetupLock();
@ -3826,30 +3916,34 @@ bool DragPoint(int n_id, double* x, double* y, const ImVec4& col, float radius,
bool hovered = false, held = false; bool hovered = false, held = false;
ImGui::KeepAliveID(id); ImGui::KeepAliveID(id);
if (input) if (input) {
ImGui::ButtonBehavior(rect,id,&hovered,&held); bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held);
if (out_clicked) *out_clicked = clicked;
if (out_hovered) *out_hovered = hovered;
if (out_held) *out_held = held;
}
bool dragging = false; bool modified = false;
if (held && ImGui::IsMouseDragging(0)) { if (held && ImGui::IsMouseDragging(0)) {
*x = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; *x = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
*y = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; *y = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
dragging = true; modified = true;
} }
PushPlotClipRect(); PushPlotClipRect();
ImDrawList& DrawList = *GetPlotDrawList(); ImDrawList& DrawList = *GetPlotDrawList();
if ((hovered || held) && show_curs) if ((hovered || held) && show_curs)
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
if (dragging && no_delay) if (modified && no_delay)
pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO); pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO);
DrawList.AddCircleFilled(pos, radius, col32); DrawList.AddCircleFilled(pos, radius, col32);
PopPlotClipRect(); PopPlotClipRect();
ImGui::PopID(); ImGui::PopID();
return dragging; return modified;
} }
bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags) { bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {
// ImGui::PushID("#IMPLOT_DRAG_LINE_X"); // ImGui::PushID("#IMPLOT_DRAG_LINE_X");
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "DragLineX() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "DragLineX() needs to be called between BeginPlot() and EndPlot()!");
@ -3871,8 +3965,12 @@ bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPl
bool hovered = false, held = false; bool hovered = false, held = false;
ImGui::KeepAliveID(id); ImGui::KeepAliveID(id);
if (input) if (input) {
ImGui::ButtonBehavior(rect,id,&hovered,&held); bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held);
if (out_clicked) *out_clicked = clicked;
if (out_hovered) *out_hovered = hovered;
if (out_held) *out_held = held;
}
if ((hovered || held) && show_curs) if ((hovered || held) && show_curs)
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
@ -3881,15 +3979,15 @@ bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPl
ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col;
ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color);
bool dragging = false; bool modified = false;
if (held && ImGui::IsMouseDragging(0)) { if (held && ImGui::IsMouseDragging(0)) {
*value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; *value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
dragging = true; modified = true;
} }
PushPlotClipRect(); PushPlotClipRect();
ImDrawList& DrawList = *GetPlotDrawList(); ImDrawList& DrawList = *GetPlotDrawList();
if (dragging && no_delay) if (modified && no_delay)
x = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x); x = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x);
DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yb), col32, thickness); DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yb), col32, thickness);
DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yt+len), col32, 3*thickness); DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yt+len), col32, 3*thickness);
@ -3897,10 +3995,10 @@ bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPl
PopPlotClipRect(); PopPlotClipRect();
// ImGui::PopID(); // ImGui::PopID();
return dragging; return modified;
} }
bool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags) { bool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {
ImGui::PushID("#IMPLOT_DRAG_LINE_Y"); ImGui::PushID("#IMPLOT_DRAG_LINE_Y");
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "DragLineY() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "DragLineY() needs to be called between BeginPlot() and EndPlot()!");
@ -3923,8 +4021,12 @@ bool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPl
bool hovered = false, held = false; bool hovered = false, held = false;
ImGui::KeepAliveID(id); ImGui::KeepAliveID(id);
if (input) if (input) {
ImGui::ButtonBehavior(rect,id,&hovered,&held); bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held);
if (out_clicked) *out_clicked = clicked;
if (out_hovered) *out_hovered = hovered;
if (out_held) *out_held = held;
}
if ((hovered || held) && show_curs) if ((hovered || held) && show_curs)
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS);
@ -3933,15 +4035,15 @@ bool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPl
ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col;
ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color);
bool dragging = false; bool modified = false;
if (held && ImGui::IsMouseDragging(0)) { if (held && ImGui::IsMouseDragging(0)) {
*value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; *value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
dragging = true; modified = true;
} }
PushPlotClipRect(); PushPlotClipRect();
ImDrawList& DrawList = *GetPlotDrawList(); ImDrawList& DrawList = *GetPlotDrawList();
if (dragging && no_delay) if (modified && no_delay)
y = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y); y = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y);
DrawList.AddLine(ImVec2(xl,y), ImVec2(xr,y), col32, thickness); DrawList.AddLine(ImVec2(xl,y), ImVec2(xr,y), col32, thickness);
DrawList.AddLine(ImVec2(xl,y), ImVec2(xl+len,y), col32, 3*thickness); DrawList.AddLine(ImVec2(xl,y), ImVec2(xl+len,y), col32, 3*thickness);
@ -3949,10 +4051,10 @@ bool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPl
PopPlotClipRect(); PopPlotClipRect();
ImGui::PopID(); ImGui::PopID();
return dragging; return modified;
} }
bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_max, const ImVec4& col, ImPlotDragToolFlags flags) { bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_max, const ImVec4& col, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {
ImGui::PushID("#IMPLOT_DRAG_RECT"); ImGui::PushID("#IMPLOT_DRAG_RECT");
IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragRect() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragRect() needs to be called between BeginPlot() and EndPlot()!");
SetupLock(); SetupLock();
@ -3989,13 +4091,18 @@ bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_m
ImU32 col32_a = ImGui::ColorConvertFloat4ToU32(color); ImU32 col32_a = ImGui::ColorConvertFloat4ToU32(color);
const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id);
bool dragging = false; bool modified = false;
bool hovered = false, held = false; bool clicked = false, hovered = false, held = false;
ImRect b_rect(pc.x-DRAG_GRAB_HALF_SIZE,pc.y-DRAG_GRAB_HALF_SIZE,pc.x+DRAG_GRAB_HALF_SIZE,pc.y+DRAG_GRAB_HALF_SIZE); ImRect b_rect(pc.x-DRAG_GRAB_HALF_SIZE,pc.y-DRAG_GRAB_HALF_SIZE,pc.x+DRAG_GRAB_HALF_SIZE,pc.y+DRAG_GRAB_HALF_SIZE);
ImGui::KeepAliveID(id); ImGui::KeepAliveID(id);
if (input) if (input) {
ImGui::ButtonBehavior(b_rect,id,&hovered,&held); // middle point
clicked = ImGui::ButtonBehavior(b_rect,id,&hovered,&held);
if (out_clicked) *out_clicked = clicked;
if (out_hovered) *out_hovered = hovered;
if (out_held) *out_held = held;
}
if ((hovered || held) && show_curs) if ((hovered || held) && show_curs)
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll);
@ -4005,7 +4112,7 @@ bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_m
*y[i] = pp.y; *y[i] = pp.y;
*x[i] = pp.x; *x[i] = pp.x;
} }
dragging = true; modified = true;
} }
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
@ -4013,15 +4120,19 @@ bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_m
b_rect = ImRect(p[i].x-DRAG_GRAB_HALF_SIZE,p[i].y-DRAG_GRAB_HALF_SIZE,p[i].x+DRAG_GRAB_HALF_SIZE,p[i].y+DRAG_GRAB_HALF_SIZE); b_rect = ImRect(p[i].x-DRAG_GRAB_HALF_SIZE,p[i].y-DRAG_GRAB_HALF_SIZE,p[i].x+DRAG_GRAB_HALF_SIZE,p[i].y+DRAG_GRAB_HALF_SIZE);
ImGuiID p_id = id + i + 1; ImGuiID p_id = id + i + 1;
ImGui::KeepAliveID(p_id); ImGui::KeepAliveID(p_id);
if (input) if (input) {
ImGui::ButtonBehavior(b_rect,p_id,&hovered,&held); clicked = ImGui::ButtonBehavior(b_rect,p_id,&hovered,&held);
if (out_clicked) *out_clicked = *out_clicked || clicked;
if (out_hovered) *out_hovered = *out_hovered || hovered;
if (out_held) *out_held = *out_held || held;
}
if ((hovered || held) && show_curs) if ((hovered || held) && show_curs)
ImGui::SetMouseCursor(cur[i]); ImGui::SetMouseCursor(cur[i]);
if (held && ImGui::IsMouseDragging(0)) { if (held && ImGui::IsMouseDragging(0)) {
*x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; *x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
*y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; *y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
dragging = true; modified = true;
} }
// edges // edges
@ -4031,8 +4142,12 @@ bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_m
: ImRect(e_min.x - DRAG_GRAB_HALF_SIZE, e_min.y + DRAG_GRAB_HALF_SIZE, e_max.x + DRAG_GRAB_HALF_SIZE, e_max.y - DRAG_GRAB_HALF_SIZE); : ImRect(e_min.x - DRAG_GRAB_HALF_SIZE, e_min.y + DRAG_GRAB_HALF_SIZE, e_max.x + DRAG_GRAB_HALF_SIZE, e_max.y - DRAG_GRAB_HALF_SIZE);
ImGuiID e_id = id + i + 5; ImGuiID e_id = id + i + 5;
ImGui::KeepAliveID(e_id); ImGui::KeepAliveID(e_id);
if (input) if (input) {
ImGui::ButtonBehavior(b_rect,e_id,&hovered,&held); clicked = ImGui::ButtonBehavior(b_rect,e_id,&hovered,&held);
if (out_clicked) *out_clicked = *out_clicked || clicked;
if (out_hovered) *out_hovered = *out_hovered || hovered;
if (out_held) *out_held = *out_held || held;
}
if ((hovered || held) && show_curs) if ((hovered || held) && show_curs)
h[i] ? ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); h[i] ? ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
if (held && ImGui::IsMouseDragging(0)) { if (held && ImGui::IsMouseDragging(0)) {
@ -4040,7 +4155,7 @@ bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_m
*y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; *y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
else else
*x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; *x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
dragging = true; modified = true;
} }
if (hovered && ImGui::IsMouseDoubleClicked(0)) if (hovered && ImGui::IsMouseDoubleClicked(0))
{ {
@ -4049,14 +4164,22 @@ bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_m
*y[i] = ((y[i] == y_min && *y_min < *y_max) || (y[i] == y_max && *y_max < *y_min)) ? b.Y.Min : b.Y.Max; *y[i] = ((y[i] == y_min && *y_min < *y_max) || (y[i] == y_max && *y_max < *y_min)) ? b.Y.Min : b.Y.Max;
else else
*x[i] = ((x[i] == x_min && *x_min < *x_max) || (x[i] == x_max && *x_max < *x_min)) ? b.X.Min : b.X.Max; *x[i] = ((x[i] == x_min && *x_min < *x_max) || (x[i] == x_max && *x_max < *x_min)) ? b.X.Min : b.X.Max;
dragging = true; modified = true;
} }
} }
const bool mouse_inside = rect_grab.Contains(ImGui::GetMousePos());
const bool mouse_clicked = ImGui::IsMouseClicked(0);
const bool mouse_down = ImGui::IsMouseDown(0);
if (input && mouse_inside) {
if (out_clicked) *out_clicked = *out_clicked || mouse_clicked;
if (out_hovered) *out_hovered = true;
if (out_held) *out_held = *out_held || mouse_down;
}
PushPlotClipRect(); PushPlotClipRect();
ImDrawList& DrawList = *GetPlotDrawList(); ImDrawList& DrawList = *GetPlotDrawList();
if (dragging && no_delay) { if (modified && no_delay) {
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO); p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO);
pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO); pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO);
@ -4064,18 +4187,18 @@ bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_m
} }
DrawList.AddRectFilled(rect.Min, rect.Max, col32_a); DrawList.AddRectFilled(rect.Min, rect.Max, col32_a);
DrawList.AddRect(rect.Min, rect.Max, col32); DrawList.AddRect(rect.Min, rect.Max, col32);
if (input && (dragging || rect_grab.Contains(ImGui::GetMousePos()))) { if (input && (modified || mouse_inside)) {
DrawList.AddCircleFilled(pc,DRAG_GRAB_HALF_SIZE,col32); DrawList.AddCircleFilled(pc,DRAG_GRAB_HALF_SIZE,col32);
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
DrawList.AddCircleFilled(p[i],DRAG_GRAB_HALF_SIZE,col32); DrawList.AddCircleFilled(p[i],DRAG_GRAB_HALF_SIZE,col32);
} }
PopPlotClipRect(); PopPlotClipRect();
ImGui::PopID(); ImGui::PopID();
return dragging; return modified;
} }
bool DragRect(int id, ImPlotRect* bounds, const ImVec4& col, ImPlotDragToolFlags flags) { bool DragRect(int id, ImPlotRect* bounds, const ImVec4& col, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {
return DragRect(id, &bounds->X.Min, &bounds->Y.Min,&bounds->X.Max, &bounds->Y.Max, col, flags); return DragRect(id, &bounds->X.Min, &bounds->Y.Min,&bounds->X.Max, &bounds->Y.Max, col, flags, out_clicked, out_hovered, out_held);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -4171,7 +4294,7 @@ bool BeginDragDropTargetAxis(ImAxis axis) {
bool BeginDragDropTargetLegend() { bool BeginDragDropTargetLegend() {
SetupLock(); SetupLock();
ImPlotItemGroup& items = *GImPlot->CurrentItems; ImPlotItemGroup& items = *GImPlot->CurrentItems;
ImRect rect = items.Legend.Rect; ImRect rect = items.Legend.RectClamped;
return ImGui::BeginDragDropTargetCustom(rect, items.ID); return ImGui::BeginDragDropTargetCustom(rect, items.ID);
} }
@ -5110,6 +5233,7 @@ void ShowMetricsWindow(bool* p_popen) {
static bool show_frame_rects = false; static bool show_frame_rects = false;
static bool show_subplot_frame_rects = false; static bool show_subplot_frame_rects = false;
static bool show_subplot_grid_rects = false; static bool show_subplot_grid_rects = false;
static bool show_legend_rects = false;
ImDrawList& fg = *ImGui::GetForegroundDrawList(); ImDrawList& fg = *ImGui::GetForegroundDrawList();
@ -5134,6 +5258,7 @@ void ShowMetricsWindow(bool* p_popen) {
ImGui::Checkbox("Show Axis Rects", &show_axis_rects); ImGui::Checkbox("Show Axis Rects", &show_axis_rects);
ImGui::Checkbox("Show Subplot Frame Rects", &show_subplot_frame_rects); ImGui::Checkbox("Show Subplot Frame Rects", &show_subplot_frame_rects);
ImGui::Checkbox("Show Subplot Grid Rects", &show_subplot_grid_rects); ImGui::Checkbox("Show Subplot Grid Rects", &show_subplot_grid_rects);
ImGui::Checkbox("Show Legend Rects", &show_legend_rects);
ImGui::TreePop(); ImGui::TreePop();
} }
const int n_plots = gp.Plots.GetBufSize(); const int n_plots = gp.Plots.GetBufSize();
@ -5155,6 +5280,10 @@ void ShowMetricsWindow(bool* p_popen) {
fg.AddRect(plot->Axes[i].HoverRect.Min, plot->Axes[i].HoverRect.Max, IM_COL32(0,255,0,255)); fg.AddRect(plot->Axes[i].HoverRect.Min, plot->Axes[i].HoverRect.Max, IM_COL32(0,255,0,255));
} }
} }
if (show_legend_rects && plot->Items.GetLegendCount() > 0) {
fg.AddRect(plot->Items.Legend.Rect.Min, plot->Items.Legend.Rect.Max, IM_COL32(255,192,0,255));
fg.AddRect(plot->Items.Legend.RectClamped.Min, plot->Items.Legend.RectClamped.Max, IM_COL32(255,128,0,255));
}
} }
for (int p = 0; p < n_subplots; ++p) { for (int p = 0; p < n_subplots; ++p) {
ImPlotSubplot* subplot = gp.Subplots.GetByIndex(p); ImPlotSubplot* subplot = gp.Subplots.GetByIndex(p);
@ -5162,6 +5291,10 @@ void ShowMetricsWindow(bool* p_popen) {
fg.AddRect(subplot->FrameRect.Min, subplot->FrameRect.Max, IM_COL32(255,0,0,255)); fg.AddRect(subplot->FrameRect.Min, subplot->FrameRect.Max, IM_COL32(255,0,0,255));
if (show_subplot_grid_rects) if (show_subplot_grid_rects)
fg.AddRect(subplot->GridRect.Min, subplot->GridRect.Max, IM_COL32(0,0,255,255)); fg.AddRect(subplot->GridRect.Min, subplot->GridRect.Max, IM_COL32(0,0,255,255));
if (show_legend_rects && subplot->Items.GetLegendCount() > 0) {
fg.AddRect(subplot->Items.Legend.Rect.Min, subplot->Items.Legend.Rect.Max, IM_COL32(255,192,0,255));
fg.AddRect(subplot->Items.Legend.RectClamped.Min, subplot->Items.Legend.RectClamped.Max, IM_COL32(255,128,0,255));
}
} }
if (ImGui::TreeNode("Plots","Plots (%d)", n_plots)) { if (ImGui::TreeNode("Plots","Plots (%d)", n_plots)) {
for (int p = 0; p < n_plots; ++p) { for (int p = 0; p < n_plots; ++p) {

View File

@ -1,6 +1,6 @@
// MIT License // MIT License
// Copyright (c) 2022 Evan Pezent // Copyright (c) 2023 Evan Pezent
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@ -20,7 +20,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// ImPlot v0.14 // ImPlot v0.17
// Table of Contents: // Table of Contents:
// //
@ -60,7 +60,7 @@
#endif #endif
// ImPlot version string. // ImPlot version string.
#define IMPLOT_VERSION "0.14" #define IMPLOT_VERSION "0.17"
// Indicates variable should deduced automatically. // Indicates variable should deduced automatically.
#define IMPLOT_AUTO -1 #define IMPLOT_AUTO -1
// Special color used to indicate that a color should be deduced automatically. // Special color used to indicate that a color should be deduced automatically.
@ -135,10 +135,9 @@ enum ImPlotFlags_ {
ImPlotFlags_NoInputs = 1 << 3, // the user will not be able to interact with the plot ImPlotFlags_NoInputs = 1 << 3, // the user will not be able to interact with the plot
ImPlotFlags_NoMenus = 1 << 4, // the user will not be able to open context menus ImPlotFlags_NoMenus = 1 << 4, // the user will not be able to open context menus
ImPlotFlags_NoBoxSelect = 1 << 5, // the user will not be able to box-select ImPlotFlags_NoBoxSelect = 1 << 5, // the user will not be able to box-select
ImPlotFlags_NoChild = 1 << 6, // a child window region will not be used to capture mouse scroll (can boost performance for single ImGui window applications) ImPlotFlags_NoFrame = 1 << 6, // the ImGui frame will not be rendered
ImPlotFlags_NoFrame = 1 << 7, // the ImGui frame will not be rendered ImPlotFlags_Equal = 1 << 7, // x and y axes pairs will be constrained to have the same units/pixel
ImPlotFlags_Equal = 1 << 8, // x and y axes pairs will be constrained to have the same units/pixel ImPlotFlags_Crosshairs = 1 << 8, // the default mouse cursor will be replaced with a crosshair when hovered
ImPlotFlags_Crosshairs = 1 << 9, // the default mouse cursor will be replaced with a crosshair when hovered
ImPlotFlags_CanvasOnly = ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoMouseText ImPlotFlags_CanvasOnly = ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoMouseText
}; };
@ -287,8 +286,9 @@ enum ImPlotInfLinesFlags_ {
// Flags for PlotPieChart // Flags for PlotPieChart
enum ImPlotPieChartFlags_ { enum ImPlotPieChartFlags_ {
ImPlotPieChartFlags_None = 0, // default ImPlotPieChartFlags_None = 0, // default
ImPlotPieChartFlags_Normalize = 1 << 10 // force normalization of pie chart values (i.e. always make a full circle if sum < 0) ImPlotPieChartFlags_Normalize = 1 << 10, // force normalization of pie chart values (i.e. always make a full circle if sum < 0)
ImPlotPieChartFlags_IgnoreHidden = 1 << 11 // ignore hidden slices when drawing the pie chart (as if they were not there)
}; };
// Flags for PlotHeatmap // Flags for PlotHeatmap
@ -464,41 +464,43 @@ enum ImPlotBin_ {
}; };
// Double precision version of ImVec2 used by ImPlot. Extensible by end users. // Double precision version of ImVec2 used by ImPlot. Extensible by end users.
IM_MSVC_RUNTIME_CHECKS_OFF
struct ImPlotPoint { struct ImPlotPoint {
double x, y; double x, y;
ImPlotPoint() { x = y = 0.0; } constexpr ImPlotPoint() : x(0.0), y(0.0) { }
ImPlotPoint(double _x, double _y) { x = _x; y = _y; } constexpr ImPlotPoint(double _x, double _y) : x(_x), y(_y) { }
ImPlotPoint(const ImVec2& p) { x = p.x; y = p.y; } constexpr ImPlotPoint(const ImVec2& p) : x((double)p.x), y((double)p.y) { }
double operator[] (size_t idx) const { return (&x)[idx]; } double& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1); return ((double*)(void*)(char*)this)[idx]; }
double& operator[] (size_t idx) { return (&x)[idx]; } double operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1); return ((const double*)(const void*)(const char*)this)[idx]; }
#ifdef IMPLOT_POINT_CLASS_EXTRA #ifdef IMPLOT_POINT_CLASS_EXTRA
IMPLOT_POINT_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h IMPLOT_POINT_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h
// to convert back and forth between your math types and ImPlotPoint. // to convert back and forth between your math types and ImPlotPoint.
#endif #endif
}; };
IM_MSVC_RUNTIME_CHECKS_RESTORE
// Range defined by a min/max value. // Range defined by a min/max value.
struct ImPlotRange { struct ImPlotRange {
double Min, Max; double Min, Max;
ImPlotRange() { Min = 0; Max = 0; } constexpr ImPlotRange() : Min(0.0), Max(0.0) { }
ImPlotRange(double _min, double _max) { Min = _min; Max = _max; } constexpr ImPlotRange(double _min, double _max) : Min(_min), Max(_max) { }
bool Contains(double value) const { return value >= Min && value <= Max; } bool Contains(double value) const { return value >= Min && value <= Max; }
double Size() const { return Max - Min; } double Size() const { return Max - Min; }
double Clamp(double value) const { return (value < Min) ? Min : (value > Max) ? Max : value; } double Clamp(double value) const { return (value < Min) ? Min : (value > Max) ? Max : value; }
}; };
// Combination of two range limits for X and Y axes. Also an AABB defined by Min()/Max(). // Combination of two range limits for X and Y axes. Also an AABB defined by Min()/Max().
struct ImPlotRect { struct ImPlotRect {
ImPlotRange X, Y; ImPlotRange X, Y;
ImPlotRect() { } constexpr ImPlotRect() : X(0.0,0.0), Y(0.0,0.0) { }
ImPlotRect(double x_min, double x_max, double y_min, double y_max) { X.Min = x_min; X.Max = x_max; Y.Min = y_min; Y.Max = y_max; } constexpr ImPlotRect(double x_min, double x_max, double y_min, double y_max) : X(x_min, x_max), Y(y_min, y_max) { }
bool Contains(const ImPlotPoint& p) const { return Contains(p.x, p.y); } bool Contains(const ImPlotPoint& p) const { return Contains(p.x, p.y); }
bool Contains(double x, double y) const { return X.Contains(x) && Y.Contains(y); } bool Contains(double x, double y) const { return X.Contains(x) && Y.Contains(y); }
ImPlotPoint Size() const { return ImPlotPoint(X.Size(), Y.Size()); } ImPlotPoint Size() const { return ImPlotPoint(X.Size(), Y.Size()); }
ImPlotPoint Clamp(const ImPlotPoint& p) { return Clamp(p.x, p.y); } ImPlotPoint Clamp(const ImPlotPoint& p) { return Clamp(p.x, p.y); }
ImPlotPoint Clamp(double x, double y) { return ImPlotPoint(X.Clamp(x),Y.Clamp(y)); } ImPlotPoint Clamp(double x, double y) { return ImPlotPoint(X.Clamp(x),Y.Clamp(y)); }
ImPlotPoint Min() const { return ImPlotPoint(X.Min, Y.Min); } ImPlotPoint Min() const { return ImPlotPoint(X.Min, Y.Min); }
ImPlotPoint Max() const { return ImPlotPoint(X.Max, Y.Max); } ImPlotPoint Max() const { return ImPlotPoint(X.Max, Y.Max); }
}; };
// Plot style structure // Plot style structure
@ -728,7 +730,7 @@ IMPLOT_API void EndSubplots();
// Enables an axis or sets the label and/or flags for an existing axis. Leave #label = nullptr for no label. // Enables an axis or sets the label and/or flags for an existing axis. Leave #label = nullptr for no label.
IMPLOT_API void SetupAxis(ImAxis axis, const char* label=nullptr, ImPlotAxisFlags flags=0); IMPLOT_API void SetupAxis(ImAxis axis, const char* label=nullptr, ImPlotAxisFlags flags=0);
// Sets an axis range limits. If ImPlotCond_Always is used, the axes limits will be locked. // Sets an axis range limits. If ImPlotCond_Always is used, the axes limits will be locked. Inversion with v_min > v_max is not supported; use SetupAxisLimits instead.
IMPLOT_API void SetupAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond = ImPlotCond_Once); IMPLOT_API void SetupAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond = ImPlotCond_Once);
// Links an axis range limits to external values. Set to nullptr for no linkage. The pointer data must remain valid until EndPlot. // Links an axis range limits to external values. Set to nullptr for no linkage. The pointer data must remain valid until EndPlot.
IMPLOT_API void SetupAxisLinks(ImAxis axis, double* link_min, double* link_max); IMPLOT_API void SetupAxisLinks(ImAxis axis, double* link_min, double* link_max);
@ -754,7 +756,7 @@ IMPLOT_API void SetupAxes(const char* x_label, const char* y_label, ImPlotAxisFl
// Sets the primary X and Y axes range limits. If ImPlotCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits). // Sets the primary X and Y axes range limits. If ImPlotCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits).
IMPLOT_API void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond = ImPlotCond_Once); IMPLOT_API void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond = ImPlotCond_Once);
// Sets up the plot legend. // Sets up the plot legend. This can also be called immediately after BeginSubplots when using ImPlotSubplotFlags_ShareItems.
IMPLOT_API void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags=0); IMPLOT_API void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags=0);
// Set the location of the current plot's mouse position text (default = South|East). // Set the location of the current plot's mouse position text (default = South|East).
IMPLOT_API void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags=0); IMPLOT_API void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags=0);
@ -891,6 +893,7 @@ IMPLOT_TMP void PlotStems(const char* label_id, const T* xs, const T* ys, int co
IMPLOT_TMP void PlotInfLines(const char* label_id, const T* values, int count, ImPlotInfLinesFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotInfLines(const char* label_id, const T* values, int count, ImPlotInfLinesFlags flags=0, int offset=0, int stride=sizeof(T));
// Plots a pie chart. Center and radius are in plot units. #label_fmt can be set to nullptr for no labels. // Plots a pie chart. Center and radius are in plot units. #label_fmt can be set to nullptr for no labels.
IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, ImPlotFormatter fmt, void* fmt_data=nullptr, double angle0=90, ImPlotPieChartFlags flags=0);
IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* label_fmt="%.1f", double angle0=90, ImPlotPieChartFlags flags=0); IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* label_fmt="%.1f", double angle0=90, ImPlotPieChartFlags flags=0);
// Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels. // Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels.
@ -923,16 +926,18 @@ IMPLOT_API void PlotDummy(const char* label_id, ImPlotDummyFlags flags=0);
// The following can be used to render interactive elements and/or annotations. // The following can be used to render interactive elements and/or annotations.
// Like the item plotting functions above, they apply to the current x and y // Like the item plotting functions above, they apply to the current x and y
// axes, which can be changed with `SetAxis/SetAxes`. // axes, which can be changed with `SetAxis/SetAxes`. These functions return true
// when user interaction causes the provided coordinates to change. Additional
// user interactions can be retrieved through the optional output parameters.
// Shows a draggable point at x,y. #col defaults to ImGuiCol_Text. // Shows a draggable point at x,y. #col defaults to ImGuiCol_Text.
IMPLOT_API bool DragPoint(int id, double* x, double* y, const ImVec4& col, float size = 4, ImPlotDragToolFlags flags=0); IMPLOT_API bool DragPoint(int id, double* x, double* y, const ImVec4& col, float size = 4, ImPlotDragToolFlags flags = 0, bool* out_clicked = nullptr, bool* out_hovered = nullptr, bool* held = nullptr);
// Shows a draggable vertical guide line at an x-value. #col defaults to ImGuiCol_Text. // Shows a draggable vertical guide line at an x-value. #col defaults to ImGuiCol_Text.
IMPLOT_API bool DragLineX(int id, double* x, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags=0); IMPLOT_API bool DragLineX(int id, double* x, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags = 0, bool* out_clicked = nullptr, bool* out_hovered = nullptr, bool* held = nullptr);
// Shows a draggable horizontal guide line at a y-value. #col defaults to ImGuiCol_Text. // Shows a draggable horizontal guide line at a y-value. #col defaults to ImGuiCol_Text.
IMPLOT_API bool DragLineY(int id, double* y, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags=0); IMPLOT_API bool DragLineY(int id, double* y, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags = 0, bool* out_clicked = nullptr, bool* out_hovered = nullptr, bool* held = nullptr);
// Shows a draggable and resizeable rectangle. // Shows a draggable and resizeable rectangle.
IMPLOT_API bool DragRect(int id, double* x1, double* y1, double* x2, double* y2, const ImVec4& col, ImPlotDragToolFlags flags=0); IMPLOT_API bool DragRect(int id, double* x1, double* y1, double* x2, double* y2, const ImVec4& col, ImPlotDragToolFlags flags = 0, bool* out_clicked = nullptr, bool* out_hovered = nullptr, bool* held = nullptr);
// Shows an annotation callout at a chosen point. Clamping keeps annotations in the plot area. Annotations are always rendered on top. // Shows an annotation callout at a chosen point. Clamping keeps annotations in the plot area. Annotations are always rendered on top.
IMPLOT_API void Annotation(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, bool round = false); IMPLOT_API void Annotation(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, bool round = false);

View File

@ -1,6 +1,6 @@
// MIT License // MIT License
// Copyright (c) 2022 Evan Pezent // Copyright (c) 2023 Evan Pezent
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@ -20,7 +20,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// ImPlot v0.14 // ImPlot v0.17
// We define this so that the demo does not accidentally use deprecated API // We define this so that the demo does not accidentally use deprecated API
#ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS #ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS
@ -610,10 +610,8 @@ void Demo_PieCharts() {
static ImPlotPieChartFlags flags = 0; static ImPlotPieChartFlags flags = 0;
ImGui::SetNextItemWidth(250); ImGui::SetNextItemWidth(250);
ImGui::DragFloat4("Values", data1, 0.01f, 0, 1); ImGui::DragFloat4("Values", data1, 0.01f, 0, 1);
if ((data1[0] + data1[1] + data1[2] + data1[3]) < 1) { CHECKBOX_FLAG(flags, ImPlotPieChartFlags_Normalize);
ImGui::SameLine(); CHECKBOX_FLAG(flags, ImPlotPieChartFlags_IgnoreHidden);
CHECKBOX_FLAG(flags,ImPlotPieChartFlags_Normalize);
}
if (ImPlot::BeginPlot("##Pie1", ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) { if (ImPlot::BeginPlot("##Pie1", ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) {
ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations);
@ -1147,7 +1145,7 @@ void Demo_MultipleAxes() {
ImPlot::SetAxes(ImAxis_X1, ImAxis_Y2); ImPlot::SetAxes(ImAxis_X1, ImAxis_Y2);
ImPlot::PlotLine("f(x) = cos(x)*.2+.5", xs, ys2, 1001); ImPlot::PlotLine("f(x) = cos(x)*.2+.5", xs, ys2, 1001);
} }
if (y3_axis) { if (x2_axis && y3_axis) {
ImPlot::SetAxes(ImAxis_X2, ImAxis_Y3); ImPlot::SetAxes(ImAxis_X2, ImAxis_Y3);
ImPlot::PlotLine("f(x) = sin(x+.5)*100+200 ", xs2, ys3, 1001); ImPlot::PlotLine("f(x) = sin(x+.5)*100+200 ", xs2, ys3, 1001);
} }
@ -1264,7 +1262,7 @@ ImPlotPoint SinewaveGetter(int i, void* data) {
void Demo_SubplotsSizing() { void Demo_SubplotsSizing() {
static ImPlotSubplotFlags flags = ImPlotSubplotFlags_None; static ImPlotSubplotFlags flags = ImPlotSubplotFlags_ShareItems|ImPlotSubplotFlags_NoLegend;
ImGui::CheckboxFlags("ImPlotSubplotFlags_NoResize", (unsigned int*)&flags, ImPlotSubplotFlags_NoResize); ImGui::CheckboxFlags("ImPlotSubplotFlags_NoResize", (unsigned int*)&flags, ImPlotSubplotFlags_NoResize);
ImGui::CheckboxFlags("ImPlotSubplotFlags_NoTitle", (unsigned int*)&flags, ImPlotSubplotFlags_NoTitle); ImGui::CheckboxFlags("ImPlotSubplotFlags_NoTitle", (unsigned int*)&flags, ImPlotSubplotFlags_NoTitle);
@ -1272,17 +1270,26 @@ void Demo_SubplotsSizing() {
static int cols = 3; static int cols = 3;
ImGui::SliderInt("Rows",&rows,1,5); ImGui::SliderInt("Rows",&rows,1,5);
ImGui::SliderInt("Cols",&cols,1,5); ImGui::SliderInt("Cols",&cols,1,5);
if (rows < 1 || cols < 1) {
ImGui::TextColored(ImVec4(1,0,0,1), "Nice try, but the number of rows and columns must be greater than 0!");
return;
}
static float rratios[] = {5,1,1,1,1,1}; static float rratios[] = {5,1,1,1,1,1};
static float cratios[] = {5,1,1,1,1,1}; static float cratios[] = {5,1,1,1,1,1};
ImGui::DragScalarN("Row Ratios",ImGuiDataType_Float,rratios,rows,0.01f,nullptr); ImGui::DragScalarN("Row Ratios",ImGuiDataType_Float,rratios,rows,0.01f,nullptr);
ImGui::DragScalarN("Col Ratios",ImGuiDataType_Float,cratios,cols,0.01f,nullptr); ImGui::DragScalarN("Col Ratios",ImGuiDataType_Float,cratios,cols,0.01f,nullptr);
if (ImPlot::BeginSubplots("My Subplots", rows, cols, ImVec2(-1,400), flags, rratios, cratios)) { if (ImPlot::BeginSubplots("My Subplots", rows, cols, ImVec2(-1,400), flags, rratios, cratios)) {
int id = 0;
for (int i = 0; i < rows*cols; ++i) { for (int i = 0; i < rows*cols; ++i) {
if (ImPlot::BeginPlot("",ImVec2(),ImPlotFlags_NoLegend)) { if (ImPlot::BeginPlot("",ImVec2(),ImPlotFlags_NoLegend)) {
ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations);
float fi = 0.01f * (i+1); float fi = 0.01f * (i+1);
ImPlot::SetNextLineStyle(SampleColormap((float)i/(float)(rows*cols-1),ImPlotColormap_Jet)); if (rows*cols > 1) {
ImPlot::PlotLineG("data",SinewaveGetter,&fi,1000); ImPlot::SetNextLineStyle(SampleColormap((float)i/(float)(rows*cols-1),ImPlotColormap_Jet));
}
char label[16];
snprintf(label, sizeof(label), "data%d", id++);
ImPlot::PlotLineG(label,SinewaveGetter,&fi,1000);
ImPlot::EndPlot(); ImPlot::EndPlot();
} }
} }
@ -1302,6 +1309,7 @@ void Demo_SubplotItemSharing() {
static int id[] = {0,1,2,3,4,5}; static int id[] = {0,1,2,3,4,5};
static int curj = -1; static int curj = -1;
if (ImPlot::BeginSubplots("##ItemSharing", rows, cols, ImVec2(-1,400), flags)) { if (ImPlot::BeginSubplots("##ItemSharing", rows, cols, ImVec2(-1,400), flags)) {
ImPlot::SetupLegend(ImPlotLocation_South, ImPlotLegendFlags_Sort|ImPlotLegendFlags_Horizontal);
for (int i = 0; i < rows*cols; ++i) { for (int i = 0; i < rows*cols; ++i) {
if (ImPlot::BeginPlot("")) { if (ImPlot::BeginPlot("")) {
float fc = 0.01f; float fc = 0.01f;
@ -1376,6 +1384,9 @@ void Demo_LegendOptions() {
ImGui::SliderFloat2("LegendInnerPadding", (float*)&GetStyle().LegendInnerPadding, 0.0f, 10.0f, "%.0f"); ImGui::SliderFloat2("LegendInnerPadding", (float*)&GetStyle().LegendInnerPadding, 0.0f, 10.0f, "%.0f");
ImGui::SliderFloat2("LegendSpacing", (float*)&GetStyle().LegendSpacing, 0.0f, 5.0f, "%.0f"); ImGui::SliderFloat2("LegendSpacing", (float*)&GetStyle().LegendSpacing, 0.0f, 5.0f, "%.0f");
static int num_dummy_items = 25;
ImGui::SliderInt("Num Dummy Items (Demo Scrolling)", &num_dummy_items, 0, 100);
if (ImPlot::BeginPlot("##Legend",ImVec2(-1,0))) { if (ImPlot::BeginPlot("##Legend",ImVec2(-1,0))) {
ImPlot::SetupLegend(loc, flags); ImPlot::SetupLegend(loc, flags);
static MyImPlot::WaveData data1(0.001, 0.2, 4, 0.2); static MyImPlot::WaveData data1(0.001, 0.2, 4, 0.2);
@ -1384,12 +1395,17 @@ void Demo_LegendOptions() {
static MyImPlot::WaveData data4(0.001, 0.2, 4, 0.8); static MyImPlot::WaveData data4(0.001, 0.2, 4, 0.8);
static MyImPlot::WaveData data5(0.001, 0.2, 4, 1.0); static MyImPlot::WaveData data5(0.001, 0.2, 4, 1.0);
ImPlot::PlotLineG("Item B", MyImPlot::SawWave, &data1, 1000); // "Item B" added to legend ImPlot::PlotLineG("Item 002", MyImPlot::SawWave, &data1, 1000); // "Item B" added to legend
ImPlot::PlotLineG("Item A##IDText", MyImPlot::SawWave, &data2, 1000); // "Item A" added to legend, text after ## used for ID only ImPlot::PlotLineG("Item 001##IDText", MyImPlot::SawWave, &data2, 1000); // "Item A" added to legend, text after ## used for ID only
ImPlot::PlotLineG("##NotListed", MyImPlot::SawWave, &data3, 1000); // plotted, but not added to legend ImPlot::PlotLineG("##NotListed", MyImPlot::SawWave, &data3, 1000); // plotted, but not added to legend
ImPlot::PlotLineG("Item C", MyImPlot::SawWave, &data4, 1000); // "Item C" added to legend ImPlot::PlotLineG("Item 003", MyImPlot::SawWave, &data4, 1000); // "Item C" added to legend
ImPlot::PlotLineG("Item C", MyImPlot::SawWave, &data5, 1000); // combined with previous "Item C" ImPlot::PlotLineG("Item 003", MyImPlot::SawWave, &data5, 1000); // combined with previous "Item C"
for (int i = 0; i < num_dummy_items; ++i) {
char label[16];
snprintf(label, sizeof(label), "Item %03d", i+4);
ImPlot::PlotDummy(label);
}
ImPlot::EndPlot(); ImPlot::EndPlot();
} }
} }
@ -1403,15 +1419,18 @@ void Demo_DragPoints() {
ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine(); ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine();
ImGui::CheckboxFlags("NoInput", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs); ImGui::CheckboxFlags("NoInput", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs);
ImPlotAxisFlags ax_flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoTickMarks; ImPlotAxisFlags ax_flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoTickMarks;
bool clicked[4] = {false, false, false, false};
bool hovered[4] = {false, false, false, false};
bool held[4] = {false, false, false, false};
if (ImPlot::BeginPlot("##Bezier",ImVec2(-1,0),ImPlotFlags_CanvasOnly)) { if (ImPlot::BeginPlot("##Bezier",ImVec2(-1,0),ImPlotFlags_CanvasOnly)) {
ImPlot::SetupAxes(nullptr,nullptr,ax_flags,ax_flags); ImPlot::SetupAxes(nullptr,nullptr,ax_flags,ax_flags);
ImPlot::SetupAxesLimits(0,1,0,1); ImPlot::SetupAxesLimits(0,1,0,1);
static ImPlotPoint P[] = {ImPlotPoint(.05f,.05f), ImPlotPoint(0.2,0.4), ImPlotPoint(0.8,0.6), ImPlotPoint(.95f,.95f)}; static ImPlotPoint P[] = {ImPlotPoint(.05f,.05f), ImPlotPoint(0.2,0.4), ImPlotPoint(0.8,0.6), ImPlotPoint(.95f,.95f)};
ImPlot::DragPoint(0,&P[0].x,&P[0].y, ImVec4(0,0.9f,0,1),4,flags); ImPlot::DragPoint(0,&P[0].x,&P[0].y, ImVec4(0,0.9f,0,1),4,flags, &clicked[0], &hovered[0], &held[0]);
ImPlot::DragPoint(1,&P[1].x,&P[1].y, ImVec4(1,0.5f,1,1),4,flags); ImPlot::DragPoint(1,&P[1].x,&P[1].y, ImVec4(1,0.5f,1,1),4,flags, &clicked[1], &hovered[1], &held[1]);
ImPlot::DragPoint(2,&P[2].x,&P[2].y, ImVec4(0,0.5f,1,1),4,flags); ImPlot::DragPoint(2,&P[2].x,&P[2].y, ImVec4(0,0.5f,1,1),4,flags, &clicked[2], &hovered[2], &held[2]);
ImPlot::DragPoint(3,&P[3].x,&P[3].y, ImVec4(0,0.9f,0,1),4,flags); ImPlot::DragPoint(3,&P[3].x,&P[3].y, ImVec4(0,0.9f,0,1),4,flags, &clicked[3], &hovered[3], &held[3]);
static ImPlotPoint B[100]; static ImPlotPoint B[100];
for (int i = 0; i < 100; ++i) { for (int i = 0; i < 100; ++i) {
@ -1424,14 +1443,12 @@ void Demo_DragPoints() {
B[i] = ImPlotPoint(w1*P[0].x + w2*P[1].x + w3*P[2].x + w4*P[3].x, w1*P[0].y + w2*P[1].y + w3*P[2].y + w4*P[3].y); B[i] = ImPlotPoint(w1*P[0].x + w2*P[1].x + w3*P[2].x + w4*P[3].x, w1*P[0].y + w2*P[1].y + w3*P[2].y + w4*P[3].y);
} }
ImPlot::SetNextLineStyle(ImVec4(1,0.5f,1,1),hovered[1]||held[1] ? 2.0f : 1.0f);
ImPlot::SetNextLineStyle(ImVec4(1,0.5f,1,1));
ImPlot::PlotLine("##h1",&P[0].x, &P[0].y, 2, 0, 0, sizeof(ImPlotPoint)); ImPlot::PlotLine("##h1",&P[0].x, &P[0].y, 2, 0, 0, sizeof(ImPlotPoint));
ImPlot::SetNextLineStyle(ImVec4(0,0.5f,1,1)); ImPlot::SetNextLineStyle(ImVec4(0,0.5f,1,1), hovered[2]||held[2] ? 2.0f : 1.0f);
ImPlot::PlotLine("##h2",&P[2].x, &P[2].y, 2, 0, 0, sizeof(ImPlotPoint)); ImPlot::PlotLine("##h2",&P[2].x, &P[2].y, 2, 0, 0, sizeof(ImPlotPoint));
ImPlot::SetNextLineStyle(ImVec4(0,0.9f,0,1), 2); ImPlot::SetNextLineStyle(ImVec4(0,0.9f,0,1), hovered[0]||held[0]||hovered[3]||held[3] ? 3.0f : 2.0f);
ImPlot::PlotLine("##bez",&B[0].x, &B[0].y, 100, 0, 0, sizeof(ImPlotPoint)); ImPlot::PlotLine("##bez",&B[0].x, &B[0].y, 100, 0, 0, sizeof(ImPlotPoint));
ImPlot::EndPlot(); ImPlot::EndPlot();
} }
} }
@ -1445,6 +1462,9 @@ void Demo_DragLines() {
static double y1 = 0.25; static double y1 = 0.25;
static double y2 = 0.75; static double y2 = 0.75;
static double f = 0.1; static double f = 0.1;
bool clicked = false;
bool hovered = false;
bool held = false;
static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None; static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None;
ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine(); ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine();
ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine(); ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine();
@ -1460,8 +1480,9 @@ void Demo_DragLines() {
xs[i] = (x2+x1)/2+fabs(x2-x1)*(i/1000.0f - 0.5f); xs[i] = (x2+x1)/2+fabs(x2-x1)*(i/1000.0f - 0.5f);
ys[i] = (y1+y2)/2+fabs(y2-y1)/2*sin(f*i/10); ys[i] = (y1+y2)/2+fabs(y2-y1)/2*sin(f*i/10);
} }
ImPlot::DragLineY(120482,&f,ImVec4(1,0.5f,1,1),1,flags, &clicked, &hovered, &held);
ImPlot::SetNextLineStyle(IMPLOT_AUTO_COL, hovered||held ? 2.0f : 1.0f);
ImPlot::PlotLine("Interactive Data", xs, ys, 1000); ImPlot::PlotLine("Interactive Data", xs, ys, 1000);
ImPlot::DragLineY(120482,&f,ImVec4(1,0.5f,1,1),1,flags);
ImPlot::EndPlot(); ImPlot::EndPlot();
} }
} }
@ -1476,6 +1497,9 @@ void Demo_DragRects() {
static float y_data3[512]; static float y_data3[512];
static float sampling_freq = 44100; static float sampling_freq = 44100;
static float freq = 500; static float freq = 500;
bool clicked = false;
bool hovered = false;
bool held = false;
for (size_t i = 0; i < 512; ++i) { for (size_t i = 0; i < 512; ++i) {
const float t = i / sampling_freq; const float t = i / sampling_freq;
x_data[i] = t; x_data[i] = t;
@ -1485,6 +1509,7 @@ void Demo_DragRects() {
y_data3[i] = y_data2[i] * -0.6f + sinf(3 * arg) * 0.4f; y_data3[i] = y_data2[i] * -0.6f + sinf(3 * arg) * 0.4f;
} }
ImGui::BulletText("Click and drag the edges, corners, and center of the rect."); ImGui::BulletText("Click and drag the edges, corners, and center of the rect.");
ImGui::BulletText("Double click edges to expand rect to plot extents.");
static ImPlotRect rect(0.0025,0.0045,0,0.5); static ImPlotRect rect(0.0025,0.0045,0,0.5);
static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None; static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None;
ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine(); ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine();
@ -1497,9 +1522,11 @@ void Demo_DragRects() {
ImPlot::PlotLine("Signal 1", x_data, y_data1, 512); ImPlot::PlotLine("Signal 1", x_data, y_data1, 512);
ImPlot::PlotLine("Signal 2", x_data, y_data2, 512); ImPlot::PlotLine("Signal 2", x_data, y_data2, 512);
ImPlot::PlotLine("Signal 3", x_data, y_data3, 512); ImPlot::PlotLine("Signal 3", x_data, y_data3, 512);
ImPlot::DragRect(0,&rect.X.Min,&rect.Y.Min,&rect.X.Max,&rect.Y.Max,ImVec4(1,0,1,1),flags); ImPlot::DragRect(0,&rect.X.Min,&rect.Y.Min,&rect.X.Max,&rect.Y.Max,ImVec4(1,0,1,1),flags, &clicked, &hovered, &held);
ImPlot::EndPlot(); ImPlot::EndPlot();
} }
ImVec4 bg_col = held ? ImVec4(0.5f,0,0.5f,1) : (hovered ? ImVec4(0.25f,0,0.25f,1) : ImPlot::GetStyle().Colors[ImPlotCol_PlotBg]);
ImPlot::PushStyleColor(ImPlotCol_PlotBg, bg_col);
if (ImPlot::BeginPlot("##rect",ImVec2(-1,150), ImPlotFlags_CanvasOnly)) { if (ImPlot::BeginPlot("##rect",ImVec2(-1,150), ImPlotFlags_CanvasOnly)) {
ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations);
ImPlot::SetupAxesLimits(rect.X.Min, rect.X.Max, rect.Y.Min, rect.Y.Max, ImGuiCond_Always); ImPlot::SetupAxesLimits(rect.X.Min, rect.X.Max, rect.Y.Min, rect.Y.Max, ImGuiCond_Always);
@ -1508,6 +1535,8 @@ void Demo_DragRects() {
ImPlot::PlotLine("Signal 3", x_data, y_data3, 512); ImPlot::PlotLine("Signal 3", x_data, y_data3, 512);
ImPlot::EndPlot(); ImPlot::EndPlot();
} }
ImPlot::PopStyleColor();
ImGui::Text("Rect is %sclicked, %shovered, %sheld", clicked ? "" : "not ", hovered ? "" : "not ", held ? "" : "not ");
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -2299,7 +2328,7 @@ ImPlotPoint Spiral(int idx, void*) {
void Sparkline(const char* id, const float* values, int count, float min_v, float max_v, int offset, const ImVec4& col, const ImVec2& size) { void Sparkline(const char* id, const float* values, int count, float min_v, float max_v, int offset, const ImVec4& col, const ImVec2& size) {
ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, ImVec2(0,0)); ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, ImVec2(0,0));
if (ImPlot::BeginPlot(id,size,ImPlotFlags_CanvasOnly|ImPlotFlags_NoChild)) { if (ImPlot::BeginPlot(id,size,ImPlotFlags_CanvasOnly)) {
ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations);
ImPlot::SetupAxesLimits(0, count - 1, min_v, max_v, ImGuiCond_Always); ImPlot::SetupAxesLimits(0, count - 1, min_v, max_v, ImGuiCond_Always);
ImPlot::SetNextLineStyle(col); ImPlot::SetNextLineStyle(col);

View File

@ -1,6 +1,6 @@
// MIT License // MIT License
// Copyright (c) 2022 Evan Pezent // Copyright (c) 2023 Evan Pezent
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@ -20,7 +20,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// ImPlot v0.14 // ImPlot v0.17
// You may use this file to debug, understand or extend ImPlot features but we // You may use this file to debug, understand or extend ImPlot features but we
// don't provide any guarantee of forward compatibility! // don't provide any guarantee of forward compatibility!
@ -907,8 +907,9 @@ struct ImPlotAxis
} }
void PullLinks() { void PullLinks() {
if (LinkedMin) { SetMin(*LinkedMin,true); } if (LinkedMin && LinkedMax) { SetRange(*LinkedMin, *LinkedMax); }
if (LinkedMax) { SetMax(*LinkedMax,true); } else if (LinkedMin) { SetMin(*LinkedMin,true); }
else if (LinkedMax) { SetMax(*LinkedMax,true); }
} }
}; };
@ -965,9 +966,11 @@ struct ImPlotLegend
ImPlotLegendFlags PreviousFlags; ImPlotLegendFlags PreviousFlags;
ImPlotLocation Location; ImPlotLocation Location;
ImPlotLocation PreviousLocation; ImPlotLocation PreviousLocation;
ImVec2 Scroll;
ImVector<int> Indices; ImVector<int> Indices;
ImGuiTextBuffer Labels; ImGuiTextBuffer Labels;
ImRect Rect; ImRect Rect;
ImRect RectClamped;
bool Hovered; bool Hovered;
bool Held; bool Held;
bool CanGoInside; bool CanGoInside;
@ -977,6 +980,7 @@ struct ImPlotLegend
CanGoInside = true; CanGoInside = true;
Hovered = Held = false; Hovered = Held = false;
Location = PreviousLocation = ImPlotLocation_NorthWest; Location = PreviousLocation = ImPlotLocation_NorthWest;
Scroll = ImVec2(0,0);
} }
void Reset() { Indices.shrink(0); Labels.Buf.shrink(0); } void Reset() { Indices.shrink(0); Labels.Buf.shrink(0); }
@ -1136,7 +1140,6 @@ struct ImPlotSubplot {
ID = 0; ID = 0;
Flags = PreviousFlags = ImPlotSubplotFlags_None; Flags = PreviousFlags = ImPlotSubplotFlags_None;
Rows = Cols = CurrentIdx = 0; Rows = Cols = CurrentIdx = 0;
FrameHovered = false;
Items.Legend.Location = ImPlotLocation_North; Items.Legend.Location = ImPlotLocation_North;
Items.Legend.Flags = ImPlotLegendFlags_Horizontal|ImPlotLegendFlags_Outside; Items.Legend.Flags = ImPlotLegendFlags_Horizontal|ImPlotLegendFlags_Outside;
Items.Legend.CanGoInside = false; Items.Legend.CanGoInside = false;
@ -1215,9 +1218,6 @@ struct ImPlotContext {
ImPlotAnnotationCollection Annotations; ImPlotAnnotationCollection Annotations;
ImPlotTagCollection Tags; ImPlotTagCollection Tags;
// Flags
bool ChildWindowMade;
// Style and Colormaps // Style and Colormaps
ImPlotStyle Style; ImPlotStyle Style;
ImVector<ImGuiColorMod> ColorModifiers; ImVector<ImGuiColorMod> ColorModifiers;
@ -1414,13 +1414,15 @@ IMPLOT_API void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bo
// Gets the position of an inner rect that is located inside of an outer rect according to an ImPlotLocation and padding amount. // Gets the position of an inner rect that is located inside of an outer rect according to an ImPlotLocation and padding amount.
IMPLOT_API ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation location, const ImVec2& pad = ImVec2(0,0)); IMPLOT_API ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation location, const ImVec2& pad = ImVec2(0,0));
// Calculates the bounding box size of a legend // Calculates the bounding box size of a legend _before_ clipping.
IMPLOT_API ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical); IMPLOT_API ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical);
// Clips calculated legend size
IMPLOT_API bool ClampLegendRect(ImRect& legend_rect, const ImRect& outer_rect, const ImVec2& pad);
// Renders legend entries into a bounding box // Renders legend entries into a bounding box
IMPLOT_API bool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool interactable, const ImVec2& pad, const ImVec2& spacing, bool vertical, ImDrawList& DrawList); IMPLOT_API bool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool interactable, const ImVec2& pad, const ImVec2& spacing, bool vertical, ImDrawList& DrawList);
// Shows an alternate legend for the plot identified by #title_id, outside of the plot frame (can be called before or after of Begin/EndPlot but must occur in the same ImGui window!). // Shows an alternate legend for the plot identified by #title_id, outside of the plot frame (can be called before or after of Begin/EndPlot but must occur in the same ImGui window! This is not thoroughly tested nor scrollable!).
IMPLOT_API void ShowAltLegend(const char* title_id, bool vertical = true, const ImVec2 size = ImVec2(0,0), bool interactable = true); IMPLOT_API void ShowAltLegend(const char* title_id, bool vertical = true, const ImVec2 size = ImVec2(0,0), bool interactable = true);
// Shows an legends's context menu. // Shows a legend's context menu.
IMPLOT_API bool ShowLegendContextMenu(ImPlotLegend& legend, bool visible); IMPLOT_API bool ShowLegendContextMenu(ImPlotLegend& legend, bool visible);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -1,6 +1,6 @@
// MIT License // MIT License
// Copyright (c) 2020 Evan Pezent // Copyright (c) 2023 Evan Pezent
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@ -20,7 +20,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// ImPlot v0.14 // ImPlot v0.17
#define IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS
#include "implot.h" #include "implot.h"
@ -98,11 +98,11 @@ static IMPLOT_INLINE float ImInvSqrt(float x) { return 1.0f / sqrtf(x); }
#define _CAT(x, y) _CAT_(x, y) #define _CAT(x, y) _CAT_(x, y)
#define _CAT_(x,y) x ## y #define _CAT_(x,y) x ## y
#define _INSTANTIATE_FOR_NUMERIC_TYPES(chain) _CAT(_INSTANTIATE_FOR_NUMERIC_TYPES_1 chain, _END) #define _INSTANTIATE_FOR_NUMERIC_TYPES(chain) _CAT(_INSTANTIATE_FOR_NUMERIC_TYPES_1 chain, _END)
#define _INSTANTIATE_FOR_NUMERIC_TYPES_1(T) INSTANTIATE_MACRO(T); _INSTANTIATE_FOR_NUMERIC_TYPES_2 #define _INSTANTIATE_FOR_NUMERIC_TYPES_1(T) INSTANTIATE_MACRO(T) _INSTANTIATE_FOR_NUMERIC_TYPES_2
#define _INSTANTIATE_FOR_NUMERIC_TYPES_2(T) INSTANTIATE_MACRO(T); _INSTANTIATE_FOR_NUMERIC_TYPES_1 #define _INSTANTIATE_FOR_NUMERIC_TYPES_2(T) INSTANTIATE_MACRO(T) _INSTANTIATE_FOR_NUMERIC_TYPES_1
#define _INSTANTIATE_FOR_NUMERIC_TYPES_1_END #define _INSTANTIATE_FOR_NUMERIC_TYPES_1_END
#define _INSTANTIATE_FOR_NUMERIC_TYPES_2_END #define _INSTANTIATE_FOR_NUMERIC_TYPES_2_END
#define CALL_INSTANTIATE_FOR_NUMERIC_TYPES() _INSTANTIATE_FOR_NUMERIC_TYPES(IMPLOT_NUMERIC_TYPES); #define CALL_INSTANTIATE_FOR_NUMERIC_TYPES() _INSTANTIATE_FOR_NUMERIC_TYPES(IMPLOT_NUMERIC_TYPES)
namespace ImPlot { namespace ImPlot {
@ -1287,7 +1287,7 @@ struct RendererShaded : RendererBase {
return false; return false;
} }
const int intersect = (P11.y > P12.y && P22.y > P21.y) || (P12.y > P11.y && P21.y > P22.y); const int intersect = (P11.y > P12.y && P22.y > P21.y) || (P12.y > P11.y && P21.y > P22.y);
ImVec2 intersection = Intersection(P11,P21,P12,P22); const ImVec2 intersection = intersect == 0 ? ImVec2(0,0) : Intersection(P11,P21,P12,P22);
draw_list._VtxWritePtr[0].pos = P11; draw_list._VtxWritePtr[0].pos = P11;
draw_list._VtxWritePtr[0].uv = UV; draw_list._VtxWritePtr[0].uv = UV;
draw_list._VtxWritePtr[0].col = Col; draw_list._VtxWritePtr[0].col = Col;
@ -1569,6 +1569,10 @@ void RenderMarkers(const _Getter& getter, ImPlotMarker marker, float size, bool
template <typename _Getter> template <typename _Getter>
void PlotLineEx(const char* label_id, const _Getter& getter, ImPlotLineFlags flags) { void PlotLineEx(const char* label_id, const _Getter& getter, ImPlotLineFlags flags) {
if (BeginItemEx(label_id, Fitter1<_Getter>(getter), flags, ImPlotCol_Line)) { if (BeginItemEx(label_id, Fitter1<_Getter>(getter), flags, ImPlotCol_Line)) {
if (getter.Count <= 0) {
EndItem();
return;
}
const ImPlotNextItemData& s = GetItemData(); const ImPlotNextItemData& s = GetItemData();
if (getter.Count > 1) { if (getter.Count > 1) {
if (ImHasFlag(flags, ImPlotLineFlags_Shaded) && s.RenderFill) { if (ImHasFlag(flags, ImPlotLineFlags_Shaded) && s.RenderFill) {
@ -1640,6 +1644,10 @@ void PlotLineG(const char* label_id, ImPlotGetter getter_func, void* data, int c
template <typename Getter> template <typename Getter>
void PlotScatterEx(const char* label_id, const Getter& getter, ImPlotScatterFlags flags) { void PlotScatterEx(const char* label_id, const Getter& getter, ImPlotScatterFlags flags) {
if (BeginItemEx(label_id, Fitter1<Getter>(getter), flags, ImPlotCol_MarkerOutline)) { if (BeginItemEx(label_id, Fitter1<Getter>(getter), flags, ImPlotCol_MarkerOutline)) {
if (getter.Count <= 0) {
EndItem();
return;
}
const ImPlotNextItemData& s = GetItemData(); const ImPlotNextItemData& s = GetItemData();
ImPlotMarker marker = s.Marker == ImPlotMarker_None ? ImPlotMarker_Circle: s.Marker; ImPlotMarker marker = s.Marker == ImPlotMarker_None ? ImPlotMarker_Circle: s.Marker;
if (marker != ImPlotMarker_None) { if (marker != ImPlotMarker_None) {
@ -1686,8 +1694,12 @@ void PlotScatterG(const char* label_id, ImPlotGetter getter_func, void* data, in
template <typename Getter> template <typename Getter>
void PlotStairsEx(const char* label_id, const Getter& getter, ImPlotStairsFlags flags) { void PlotStairsEx(const char* label_id, const Getter& getter, ImPlotStairsFlags flags) {
if (BeginItemEx(label_id, Fitter1<Getter>(getter), flags, ImPlotCol_Line)) { if (BeginItemEx(label_id, Fitter1<Getter>(getter), flags, ImPlotCol_Line)) {
if (getter.Count <= 0) {
EndItem();
return;
}
const ImPlotNextItemData& s = GetItemData(); const ImPlotNextItemData& s = GetItemData();
if (getter.Count > 1 ) { if (getter.Count > 1) {
if (s.RenderFill && ImHasFlag(flags,ImPlotStairsFlags_Shaded)) { if (s.RenderFill && ImHasFlag(flags,ImPlotStairsFlags_Shaded)) {
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]); const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);
if (ImHasFlag(flags, ImPlotStairsFlags_PreStep)) if (ImHasFlag(flags, ImPlotStairsFlags_PreStep))
@ -1746,6 +1758,10 @@ void PlotStairsG(const char* label_id, ImPlotGetter getter_func, void* data, int
template <typename Getter1, typename Getter2> template <typename Getter1, typename Getter2>
void PlotShadedEx(const char* label_id, const Getter1& getter1, const Getter2& getter2, ImPlotShadedFlags flags) { void PlotShadedEx(const char* label_id, const Getter1& getter1, const Getter2& getter2, ImPlotShadedFlags flags) {
if (BeginItemEx(label_id, Fitter2<Getter1,Getter2>(getter1,getter2), flags, ImPlotCol_Fill)) { if (BeginItemEx(label_id, Fitter2<Getter1,Getter2>(getter1,getter2), flags, ImPlotCol_Fill)) {
if (getter1.Count <= 0 || getter2.Count <= 0) {
EndItem();
return;
}
const ImPlotNextItemData& s = GetItemData(); const ImPlotNextItemData& s = GetItemData();
if (s.RenderFill) { if (s.RenderFill) {
const ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]); const ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);
@ -1806,6 +1822,10 @@ void PlotShadedG(const char* label_id, ImPlotGetter getter_func1, void* data1, I
template <typename Getter1, typename Getter2> template <typename Getter1, typename Getter2>
void PlotBarsVEx(const char* label_id, const Getter1& getter1, const Getter2 getter2, double width, ImPlotBarsFlags flags) { void PlotBarsVEx(const char* label_id, const Getter1& getter1, const Getter2 getter2, double width, ImPlotBarsFlags flags) {
if (BeginItemEx(label_id, FitterBarV<Getter1,Getter2>(getter1,getter2,width), flags, ImPlotCol_Fill)) { if (BeginItemEx(label_id, FitterBarV<Getter1,Getter2>(getter1,getter2,width), flags, ImPlotCol_Fill)) {
if (getter1.Count <= 0 || getter2.Count <= 0) {
EndItem();
return;
}
const ImPlotNextItemData& s = GetItemData(); const ImPlotNextItemData& s = GetItemData();
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]); const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]); const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
@ -1826,6 +1846,10 @@ void PlotBarsVEx(const char* label_id, const Getter1& getter1, const Getter2 get
template <typename Getter1, typename Getter2> template <typename Getter1, typename Getter2>
void PlotBarsHEx(const char* label_id, const Getter1& getter1, const Getter2& getter2, double height, ImPlotBarsFlags flags) { void PlotBarsHEx(const char* label_id, const Getter1& getter1, const Getter2& getter2, double height, ImPlotBarsFlags flags) {
if (BeginItemEx(label_id, FitterBarH<Getter1,Getter2>(getter1,getter2,height), flags, ImPlotCol_Fill)) { if (BeginItemEx(label_id, FitterBarH<Getter1,Getter2>(getter1,getter2,height), flags, ImPlotCol_Fill)) {
if (getter1.Count <= 0 || getter2.Count <= 0) {
EndItem();
return;
}
const ImPlotNextItemData& s = GetItemData(); const ImPlotNextItemData& s = GetItemData();
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]); const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]); const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
@ -1982,6 +2006,10 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
template <typename _GetterPos, typename _GetterNeg> template <typename _GetterPos, typename _GetterNeg>
void PlotErrorBarsVEx(const char* label_id, const _GetterPos& getter_pos, const _GetterNeg& getter_neg, ImPlotErrorBarsFlags flags) { void PlotErrorBarsVEx(const char* label_id, const _GetterPos& getter_pos, const _GetterNeg& getter_neg, ImPlotErrorBarsFlags flags) {
if (BeginItemEx(label_id, Fitter2<_GetterPos,_GetterNeg>(getter_pos, getter_neg), flags, IMPLOT_AUTO)) { if (BeginItemEx(label_id, Fitter2<_GetterPos,_GetterNeg>(getter_pos, getter_neg), flags, IMPLOT_AUTO)) {
if (getter_pos.Count <= 0 || getter_neg.Count <= 0) {
EndItem();
return;
}
const ImPlotNextItemData& s = GetItemData(); const ImPlotNextItemData& s = GetItemData();
ImDrawList& draw_list = *GetPlotDrawList(); ImDrawList& draw_list = *GetPlotDrawList();
const ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_ErrorBar]); const ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_ErrorBar]);
@ -2003,6 +2031,10 @@ void PlotErrorBarsVEx(const char* label_id, const _GetterPos& getter_pos, const
template <typename _GetterPos, typename _GetterNeg> template <typename _GetterPos, typename _GetterNeg>
void PlotErrorBarsHEx(const char* label_id, const _GetterPos& getter_pos, const _GetterNeg& getter_neg, ImPlotErrorBarsFlags flags) { void PlotErrorBarsHEx(const char* label_id, const _GetterPos& getter_pos, const _GetterNeg& getter_neg, ImPlotErrorBarsFlags flags) {
if (BeginItemEx(label_id, Fitter2<_GetterPos,_GetterNeg>(getter_pos, getter_neg), flags, IMPLOT_AUTO)) { if (BeginItemEx(label_id, Fitter2<_GetterPos,_GetterNeg>(getter_pos, getter_neg), flags, IMPLOT_AUTO)) {
if (getter_pos.Count <= 0 || getter_neg.Count <= 0) {
EndItem();
return;
}
const ImPlotNextItemData& s = GetItemData(); const ImPlotNextItemData& s = GetItemData();
ImDrawList& draw_list = *GetPlotDrawList(); ImDrawList& draw_list = *GetPlotDrawList();
const ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_ErrorBar]); const ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_ErrorBar]);
@ -2060,13 +2092,17 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <typename _GetterM, typename _GetterB> template <typename _GetterM, typename _GetterB>
void PlotStemsEx(const char* label_id, const _GetterM& get_mark, const _GetterB& get_base, ImPlotStemsFlags flags) { void PlotStemsEx(const char* label_id, const _GetterM& getter_mark, const _GetterB& getter_base, ImPlotStemsFlags flags) {
if (BeginItemEx(label_id, Fitter2<_GetterM,_GetterB>(get_mark,get_base), flags, ImPlotCol_Line)) { if (BeginItemEx(label_id, Fitter2<_GetterM,_GetterB>(getter_mark,getter_base), flags, ImPlotCol_Line)) {
if (getter_mark.Count <= 0 || getter_base.Count <= 0) {
EndItem();
return;
}
const ImPlotNextItemData& s = GetItemData(); const ImPlotNextItemData& s = GetItemData();
// render stems // render stems
if (s.RenderLine) { if (s.RenderLine) {
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]); const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
RenderPrimitives2<RendererLineSegments2>(get_mark, get_base, col_line, s.LineWeight); RenderPrimitives2<RendererLineSegments2>(getter_mark, getter_base, col_line, s.LineWeight);
} }
// render markers // render markers
if (s.Marker != ImPlotMarker_None) { if (s.Marker != ImPlotMarker_None) {
@ -2074,7 +2110,7 @@ void PlotStemsEx(const char* label_id, const _GetterM& get_mark, const _GetterB&
PushPlotClipRect(s.MarkerSize); PushPlotClipRect(s.MarkerSize);
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]); const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]);
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerFill]); const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerFill]);
RenderMarkers<_GetterM>(get_mark, s.Marker, s.MarkerSize, s.RenderMarkerFill, col_fill, s.RenderMarkerLine, col_line, s.MarkerWeight); RenderMarkers<_GetterM>(getter_mark, s.Marker, s.MarkerSize, s.RenderMarkerFill, col_fill, s.RenderMarkerLine, col_line, s.MarkerWeight);
} }
EndItem(); EndItem();
} }
@ -2123,13 +2159,17 @@ template <typename T>
void PlotInfLines(const char* label_id, const T* values, int count, ImPlotInfLinesFlags flags, int offset, int stride) { void PlotInfLines(const char* label_id, const T* values, int count, ImPlotInfLinesFlags flags, int offset, int stride) {
const ImPlotRect lims = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO); const ImPlotRect lims = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO);
if (ImHasFlag(flags, ImPlotInfLinesFlags_Horizontal)) { if (ImHasFlag(flags, ImPlotInfLinesFlags_Horizontal)) {
GetterXY<IndexerConst,IndexerIdx<T>> get_min(IndexerConst(lims.X.Min),IndexerIdx<T>(values,count,offset,stride),count); GetterXY<IndexerConst,IndexerIdx<T>> getter_min(IndexerConst(lims.X.Min),IndexerIdx<T>(values,count,offset,stride),count);
GetterXY<IndexerConst,IndexerIdx<T>> get_max(IndexerConst(lims.X.Max),IndexerIdx<T>(values,count,offset,stride),count); GetterXY<IndexerConst,IndexerIdx<T>> getter_max(IndexerConst(lims.X.Max),IndexerIdx<T>(values,count,offset,stride),count);
if (BeginItemEx(label_id, FitterY<GetterXY<IndexerConst,IndexerIdx<T>>>(get_min), flags, ImPlotCol_Line)) { if (BeginItemEx(label_id, FitterY<GetterXY<IndexerConst,IndexerIdx<T>>>(getter_min), flags, ImPlotCol_Line)) {
if (count <= 0) {
EndItem();
return;
}
const ImPlotNextItemData& s = GetItemData(); const ImPlotNextItemData& s = GetItemData();
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]); const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
if (s.RenderLine) if (s.RenderLine)
RenderPrimitives2<RendererLineSegments2>(get_min, get_max, col_line, s.LineWeight); RenderPrimitives2<RendererLineSegments2>(getter_min, getter_max, col_line, s.LineWeight);
EndItem(); EndItem();
} }
} }
@ -2137,6 +2177,10 @@ void PlotInfLines(const char* label_id, const T* values, int count, ImPlotInfLin
GetterXY<IndexerIdx<T>,IndexerConst> get_min(IndexerIdx<T>(values,count,offset,stride),IndexerConst(lims.Y.Min),count); GetterXY<IndexerIdx<T>,IndexerConst> get_min(IndexerIdx<T>(values,count,offset,stride),IndexerConst(lims.Y.Min),count);
GetterXY<IndexerIdx<T>,IndexerConst> get_max(IndexerIdx<T>(values,count,offset,stride),IndexerConst(lims.Y.Max),count); GetterXY<IndexerIdx<T>,IndexerConst> get_max(IndexerIdx<T>(values,count,offset,stride),IndexerConst(lims.Y.Max),count);
if (BeginItemEx(label_id, FitterX<GetterXY<IndexerIdx<T>,IndexerConst>>(get_min), flags, ImPlotCol_Line)) { if (BeginItemEx(label_id, FitterX<GetterXY<IndexerIdx<T>,IndexerConst>>(get_min), flags, ImPlotCol_Line)) {
if (count <= 0) {
EndItem();
return;
}
const ImPlotNextItemData& s = GetItemData(); const ImPlotNextItemData& s = GetItemData();
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]); const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
if (s.RenderLine) if (s.RenderLine)
@ -2172,57 +2216,121 @@ IMPLOT_INLINE void RenderPieSlice(ImDrawList& draw_list, const ImPlotPoint& cent
} }
template <typename T> template <typename T>
void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* fmt, double angle0, ImPlotPieChartFlags flags) { double PieChartSum(const T* values, int count, bool ignore_hidden) {
IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "PlotPieChart() needs to be called between BeginPlot() and EndPlot()!");
ImDrawList & draw_list = *GetPlotDrawList();
double sum = 0; double sum = 0;
for (int i = 0; i < count; ++i) if (ignore_hidden) {
sum += (double)values[i]; ImPlotContext& gp = *GImPlot;
const bool normalize = ImHasFlag(flags,ImPlotPieChartFlags_Normalize) || sum > 1.0; ImPlotItemGroup& Items = *gp.CurrentItems;
ImPlotPoint center(x,y); for (int i = 0; i < count; ++i) {
PushPlotClipRect(); if (i >= Items.GetItemCount())
break;
ImPlotItem* item = Items.GetItemByIndex(i);
IM_ASSERT(item != nullptr);
if (item->Show) {
sum += (double)values[i];
}
}
}
else {
for (int i = 0; i < count; ++i) {
sum += (double)values[i];
}
}
return sum;
}
template <typename T>
void PlotPieChartEx(const char* const label_ids[], const T* values, int count, ImPlotPoint center, double radius, double angle0, ImPlotPieChartFlags flags) {
ImDrawList& draw_list = *GetPlotDrawList();
const bool ignore_hidden = ImHasFlag(flags, ImPlotPieChartFlags_IgnoreHidden);
const double sum = PieChartSum(values, count, ignore_hidden);
const bool normalize = ImHasFlag(flags, ImPlotPieChartFlags_Normalize) || sum > 1.0;
double a0 = angle0 * 2 * IM_PI / 360.0; double a0 = angle0 * 2 * IM_PI / 360.0;
double a1 = angle0 * 2 * IM_PI / 360.0; double a1 = angle0 * 2 * IM_PI / 360.0;
ImPlotPoint Pmin = ImPlotPoint(x-radius,y-radius); ImPlotPoint Pmin = ImPlotPoint(center.x - radius, center.y - radius);
ImPlotPoint Pmax = ImPlotPoint(x+radius,y+radius); ImPlotPoint Pmax = ImPlotPoint(center.x + radius, center.y + radius);
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
double percent = normalize ? (double)values[i] / sum : (double)values[i]; ImPlotItem* item = GetItem(label_ids[i]);
a1 = a0 + 2 * IM_PI * percent;
if (BeginItemEx(label_ids[i], FitterRect(Pmin,Pmax))) { const double percent = normalize ? (double)values[i] / sum : (double)values[i];
ImU32 col = GetCurrentItem()->Color; const bool skip = sum <= 0.0 || (ignore_hidden && item != nullptr && !item->Show);
if (percent < 0.5) { if (!skip)
RenderPieSlice(draw_list, center, radius, a0, a1, col); a1 = a0 + 2 * IM_PI * percent;
}
else { if (BeginItemEx(label_ids[i], FitterRect(Pmin, Pmax))) {
RenderPieSlice(draw_list, center, radius, a0, a0 + (a1 - a0) * 0.5, col); if (sum > 0.0) {
RenderPieSlice(draw_list, center, radius, a0 + (a1 - a0) * 0.5, a1, col); ImU32 col = GetCurrentItem()->Color;
if (percent < 0.5) {
RenderPieSlice(draw_list, center, radius, a0, a1, col);
}
else {
RenderPieSlice(draw_list, center, radius, a0, a0 + (a1 - a0) * 0.5, col);
RenderPieSlice(draw_list, center, radius, a0 + (a1 - a0) * 0.5, a1, col);
}
} }
EndItem(); EndItem();
} }
a0 = a1; if (!skip)
a0 = a1;
} }
}
int PieChartFormatter(double value, char* buff, int size, void* data) {
const char* fmt = (const char*)data;
return snprintf(buff, size, fmt, value);
};
template <typename T>
void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* fmt, double angle0, ImPlotPieChartFlags flags) {
PlotPieChart<T>(label_ids, values, count, x, y, radius, PieChartFormatter, (void*)fmt, angle0, flags);
}
#define INSTANTIATE_MACRO(T) template IMPLOT_API void PlotPieChart<T>(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* fmt, double angle0, ImPlotPieChartFlags flags);
CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
#undef INSTANTIATE_MACRO
template <typename T>
void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, ImPlotFormatter fmt, void* fmt_data, double angle0, ImPlotPieChartFlags flags) {
IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "PlotPieChart() needs to be called between BeginPlot() and EndPlot()!");
ImDrawList& draw_list = *GetPlotDrawList();
const bool ignore_hidden = ImHasFlag(flags, ImPlotPieChartFlags_IgnoreHidden);
const double sum = PieChartSum(values, count, ignore_hidden);
const bool normalize = ImHasFlag(flags, ImPlotPieChartFlags_Normalize) || sum > 1.0;
ImPlotPoint center(x, y);
PushPlotClipRect();
PlotPieChartEx(label_ids, values, count, center, radius, angle0, flags);
if (fmt != nullptr) { if (fmt != nullptr) {
a0 = angle0 * 2 * IM_PI / 360.0; double a0 = angle0 * 2 * IM_PI / 360.0;
a1 = angle0 * 2 * IM_PI / 360.0; double a1 = angle0 * 2 * IM_PI / 360.0;
char buffer[32]; char buffer[32];
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
ImPlotItem* item = GetItem(label_ids[i]); ImPlotItem* item = GetItem(label_ids[i]);
double percent = normalize ? (double)values[i] / sum : (double)values[i]; IM_ASSERT(item != nullptr);
a1 = a0 + 2 * IM_PI * percent;
if (item->Show) { const double percent = normalize ? (double)values[i] / sum : (double)values[i];
ImFormatString(buffer, 32, fmt, (double)values[i]); const bool skip = ignore_hidden && item != nullptr && !item->Show;
ImVec2 size = ImGui::CalcTextSize(buffer);
double angle = a0 + (a1 - a0) * 0.5; if (!skip) {
ImVec2 pos = PlotToPixels(center.x + 0.5 * radius * cos(angle), center.y + 0.5 * radius * sin(angle),IMPLOT_AUTO,IMPLOT_AUTO); a1 = a0 + 2 * IM_PI * percent;
ImU32 col = CalcTextColor(ImGui::ColorConvertU32ToFloat4(item->Color)); if (item->Show) {
draw_list.AddText(pos - size * 0.5f, col, buffer); fmt((double)values[i], buffer, 32, fmt_data);
ImVec2 size = ImGui::CalcTextSize(buffer);
double angle = a0 + (a1 - a0) * 0.5;
ImVec2 pos = PlotToPixels(center.x + 0.5 * radius * cos(angle), center.y + 0.5 * radius * sin(angle), IMPLOT_AUTO, IMPLOT_AUTO);
ImU32 col = CalcTextColor(ImGui::ColorConvertU32ToFloat4(item->Color));
draw_list.AddText(pos - size * 0.5f, col, buffer);
}
a0 = a1;
} }
a0 = a1;
} }
} }
PopPlotClipRect(); PopPlotClipRect();
} }
#define INSTANTIATE_MACRO(T) template IMPLOT_API void PlotPieChart<T>(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* fmt, double angle0, ImPlotPieChartFlags flags); #define INSTANTIATE_MACRO(T) template IMPLOT_API void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, ImPlotFormatter fmt, void* fmt_data, double angle0, ImPlotPieChartFlags flags);
CALL_INSTANTIATE_FOR_NUMERIC_TYPES() CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
#undef INSTANTIATE_MACRO #undef INSTANTIATE_MACRO
@ -2283,8 +2391,8 @@ struct GetterHeatmapColMaj {
{ } { }
template <typename I> IMPLOT_INLINE RectC operator()(I idx) const { template <typename I> IMPLOT_INLINE RectC operator()(I idx) const {
double val = (double)Values[idx]; double val = (double)Values[idx];
const int r = idx % Cols; const int r = idx % Rows;
const int c = idx / Cols; const int c = idx / Rows;
const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height)); const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height));
RectC rect; RectC rect;
rect.Pos = p; rect.Pos = p;
@ -2375,6 +2483,10 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d
template <typename T> template <typename T>
void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, ImPlotHeatmapFlags flags) { void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, ImPlotHeatmapFlags flags) {
if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max))) { if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max))) {
if (rows <= 0 || cols <= 0) {
EndItem();
return;
}
ImDrawList& draw_list = *GetPlotDrawList(); ImDrawList& draw_list = *GetPlotDrawList();
const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor);
RenderHeatmap(draw_list, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true, col_maj); RenderHeatmap(draw_list, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true, col_maj);
@ -2539,6 +2651,10 @@ double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count
} }
if (BeginItemEx(label_id, FitterRect(range))) { if (BeginItemEx(label_id, FitterRect(range))) {
if (y_bins <= 0 || x_bins <= 0) {
EndItem();
return max_count;
}
ImDrawList& draw_list = *GetPlotDrawList(); ImDrawList& draw_list = *GetPlotDrawList();
RenderHeatmap(draw_list, &bin_counts.Data[0], y_bins, x_bins, 0, max_count, nullptr, range.Min(), range.Max(), false, col_maj); RenderHeatmap(draw_list, &bin_counts.Data[0], y_bins, x_bins, 0, max_count, nullptr, range.Min(), range.Max(), false, col_maj);
EndItem(); EndItem();
@ -2597,8 +2713,8 @@ void PlotDigitalEx(const char* label_id, Getter getter, ImPlotDigitalFlags flags
//do not extend plot outside plot range //do not extend plot outside plot range
if (pMin.x < x_axis.PixelMin) pMin.x = x_axis.PixelMin; if (pMin.x < x_axis.PixelMin) pMin.x = x_axis.PixelMin;
if (pMax.x < x_axis.PixelMin) pMax.x = x_axis.PixelMin; if (pMax.x < x_axis.PixelMin) pMax.x = x_axis.PixelMin;
if (pMin.x > x_axis.PixelMax) pMin.x = x_axis.PixelMax; if (pMin.x > x_axis.PixelMax) pMin.x = x_axis.PixelMax - 1; //fix issue related to https://github.com/ocornut/imgui/issues/3976
if (pMax.x > x_axis.PixelMax) pMax.x = x_axis.PixelMax; if (pMax.x > x_axis.PixelMax) pMax.x = x_axis.PixelMax - 1; //fix issue related to https://github.com/ocornut/imgui/issues/3976
//plot a rectangle that extends up to x2 with y1 height //plot a rectangle that extends up to x2 with y1 height
if ((pMax.x > pMin.x) && (gp.CurrentPlot->PlotRect.Contains(pMin) || gp.CurrentPlot->PlotRect.Contains(pMax))) { if ((pMax.x > pMin.x) && (gp.CurrentPlot->PlotRect.Contains(pMin) || gp.CurrentPlot->PlotRect.Contains(pMax))) {
// ImVec4 colAlpha = item->Color; // ImVec4 colAlpha = item->Color;

View File

@ -2,8 +2,9 @@
// This is a slightly modified version of stb_textedit.h 1.14. // This is a slightly modified version of stb_textedit.h 1.14.
// Those changes would need to be pushed into nothings/stb: // Those changes would need to be pushed into nothings/stb:
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) // - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000) // - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
// Grep for [DEAR IMGUI] to find the changes. // Grep for [DEAR IMGUI] to find the changes.
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
// stb_textedit.h - v1.14 - public domain - Sean Barrett // stb_textedit.h - v1.14 - public domain - Sean Barrett
// Development of this library was sponsored by RAD Game Tools // Development of this library was sponsored by RAD Game Tools
@ -30,7 +31,7 @@
// DEPENDENCIES // DEPENDENCIES
// //
// Uses the C runtime function 'memmove', which you can override // Uses the C runtime function 'memmove', which you can override
// by defining STB_TEXTEDIT_memmove before the implementation. // by defining IMSTB_TEXTEDIT_memmove before the implementation.
// Uses no other functions. Performs no runtime allocations. // Uses no other functions. Performs no runtime allocations.
// //
// //
@ -274,8 +275,8 @@
//// ////
//// ////
#ifndef INCLUDE_STB_TEXTEDIT_H #ifndef INCLUDE_IMSTB_TEXTEDIT_H
#define INCLUDE_STB_TEXTEDIT_H #define INCLUDE_IMSTB_TEXTEDIT_H
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// //
@ -286,33 +287,33 @@
// and undo state. // and undo state.
// //
#ifndef STB_TEXTEDIT_UNDOSTATECOUNT #ifndef IMSTB_TEXTEDIT_UNDOSTATECOUNT
#define STB_TEXTEDIT_UNDOSTATECOUNT 99 #define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99
#endif #endif
#ifndef STB_TEXTEDIT_UNDOCHARCOUNT #ifndef IMSTB_TEXTEDIT_UNDOCHARCOUNT
#define STB_TEXTEDIT_UNDOCHARCOUNT 999 #define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999
#endif #endif
#ifndef STB_TEXTEDIT_CHARTYPE #ifndef IMSTB_TEXTEDIT_CHARTYPE
#define STB_TEXTEDIT_CHARTYPE int #define IMSTB_TEXTEDIT_CHARTYPE int
#endif #endif
#ifndef STB_TEXTEDIT_POSITIONTYPE #ifndef IMSTB_TEXTEDIT_POSITIONTYPE
#define STB_TEXTEDIT_POSITIONTYPE int #define IMSTB_TEXTEDIT_POSITIONTYPE int
#endif #endif
typedef struct typedef struct
{ {
// private data // private data
STB_TEXTEDIT_POSITIONTYPE where; IMSTB_TEXTEDIT_POSITIONTYPE where;
STB_TEXTEDIT_POSITIONTYPE insert_length; IMSTB_TEXTEDIT_POSITIONTYPE insert_length;
STB_TEXTEDIT_POSITIONTYPE delete_length; IMSTB_TEXTEDIT_POSITIONTYPE delete_length;
int char_storage; int char_storage;
} StbUndoRecord; } StbUndoRecord;
typedef struct typedef struct
{ {
// private data // private data
StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT]; StbUndoRecord undo_rec [IMSTB_TEXTEDIT_UNDOSTATECOUNT];
STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT]; IMSTB_TEXTEDIT_CHARTYPE undo_char[IMSTB_TEXTEDIT_UNDOCHARCOUNT];
short undo_point, redo_point; short undo_point, redo_point;
int undo_char_point, redo_char_point; int undo_char_point, redo_char_point;
} StbUndoState; } StbUndoState;
@ -371,7 +372,7 @@ typedef struct
float ymin,ymax; // height of row above and below baseline float ymin,ymax; // height of row above and below baseline
int num_chars; int num_chars;
} StbTexteditRow; } StbTexteditRow;
#endif //INCLUDE_STB_TEXTEDIT_H #endif //INCLUDE_IMSTB_TEXTEDIT_H
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
@ -384,11 +385,11 @@ typedef struct
// implementation isn't include-guarded, since it might have indirectly // implementation isn't include-guarded, since it might have indirectly
// included just the "header" portion // included just the "header" portion
#ifdef STB_TEXTEDIT_IMPLEMENTATION #ifdef IMSTB_TEXTEDIT_IMPLEMENTATION
#ifndef STB_TEXTEDIT_memmove #ifndef IMSTB_TEXTEDIT_memmove
#include <string.h> #include <string.h>
#define STB_TEXTEDIT_memmove memmove #define IMSTB_TEXTEDIT_memmove memmove
#endif #endif
@ -398,7 +399,7 @@ typedef struct
// //
// traverse the layout to locate the nearest character to a display position // traverse the layout to locate the nearest character to a display position
static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y) static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
{ {
StbTexteditRow r; StbTexteditRow r;
int n = STB_TEXTEDIT_STRINGLEN(str); int n = STB_TEXTEDIT_STRINGLEN(str);
@ -458,7 +459,7 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
} }
// API click: on mouse down, move the cursor to the clicked location, and reset the selection // API click: on mouse down, move the cursor to the clicked location, and reset the selection
static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
{ {
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
// goes off the top or bottom of the text // goes off the top or bottom of the text
@ -476,7 +477,7 @@ static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
} }
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location // API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
{ {
int p = 0; int p = 0;
@ -502,11 +503,11 @@ static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state
// //
// forward declarations // forward declarations
static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length); static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length); static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length);
static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length); static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
typedef struct typedef struct
{ {
@ -518,7 +519,7 @@ typedef struct
// find the x/y location of a character, and remember info about the previous row in // find the x/y location of a character, and remember info about the previous row in
// case we get a move-up event (for page up, we'll have to rescan) // case we get a move-up event (for page up, we'll have to rescan)
static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line) static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING *str, int n, int single_line)
{ {
StbTexteditRow r; StbTexteditRow r;
int prev_start = 0; int prev_start = 0;
@ -549,7 +550,10 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
i += r.num_chars; i += r.num_chars;
find->y += r.baseline_y_delta; find->y += r.baseline_y_delta;
if (i == z) // [DEAR IMGUI] if (i == z) // [DEAR IMGUI]
{
r.num_chars = 0; // [DEAR IMGUI]
break; // [DEAR IMGUI] break; // [DEAR IMGUI]
}
} }
find->first_char = first = i; find->first_char = first = i;
@ -566,7 +570,7 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
#define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) #define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end)
// make the selection/cursor state valid if client altered the string // make the selection/cursor state valid if client altered the string
static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) static void stb_textedit_clamp(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{ {
int n = STB_TEXTEDIT_STRINGLEN(str); int n = STB_TEXTEDIT_STRINGLEN(str);
if (STB_TEXT_HAS_SELECTION(state)) { if (STB_TEXT_HAS_SELECTION(state)) {
@ -580,7 +584,7 @@ static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
} }
// delete characters while updating undo // delete characters while updating undo
static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len) static void stb_textedit_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
{ {
stb_text_makeundo_delete(str, state, where, len); stb_text_makeundo_delete(str, state, where, len);
STB_TEXTEDIT_DELETECHARS(str, where, len); STB_TEXTEDIT_DELETECHARS(str, where, len);
@ -588,7 +592,7 @@ static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *sta
} }
// delete the section // delete the section
static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) static void stb_textedit_delete_selection(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{ {
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
if (STB_TEXT_HAS_SELECTION(state)) { if (STB_TEXT_HAS_SELECTION(state)) {
@ -625,7 +629,7 @@ static void stb_textedit_move_to_first(STB_TexteditState *state)
} }
// move cursor to last character of selection // move cursor to last character of selection
static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{ {
if (STB_TEXT_HAS_SELECTION(state)) { if (STB_TEXT_HAS_SELECTION(state)) {
stb_textedit_sortselection(state); stb_textedit_sortselection(state);
@ -637,13 +641,13 @@ static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditStat
} }
#ifdef STB_TEXTEDIT_IS_SPACE #ifdef STB_TEXTEDIT_IS_SPACE
static int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx ) static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
{ {
return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1; return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1;
} }
#ifndef STB_TEXTEDIT_MOVEWORDLEFT #ifndef STB_TEXTEDIT_MOVEWORDLEFT
static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c ) static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
{ {
--c; // always move at least one character --c; // always move at least one character
while( c >= 0 && !is_word_boundary( str, c ) ) while( c >= 0 && !is_word_boundary( str, c ) )
@ -658,7 +662,7 @@ static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
#endif #endif
#ifndef STB_TEXTEDIT_MOVEWORDRIGHT #ifndef STB_TEXTEDIT_MOVEWORDRIGHT
static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c ) static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
{ {
const int len = STB_TEXTEDIT_STRINGLEN(str); const int len = STB_TEXTEDIT_STRINGLEN(str);
++c; // always move at least one character ++c; // always move at least one character
@ -685,7 +689,7 @@ static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state)
} }
// API cut: delete selection // API cut: delete selection
static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) static int stb_textedit_cut(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{ {
if (STB_TEXT_HAS_SELECTION(state)) { if (STB_TEXT_HAS_SELECTION(state)) {
stb_textedit_delete_selection(str,state); // implicitly clamps stb_textedit_delete_selection(str,state); // implicitly clamps
@ -696,7 +700,7 @@ static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
} }
// API paste: replace existing selection with passed-in text // API paste: replace existing selection with passed-in text
static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE *text, int len)
{ {
// if there's a selection, the paste should delete it // if there's a selection, the paste should delete it
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
@ -717,14 +721,14 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta
#endif #endif
// API key: process a keyboard input // API key: process a keyboard input
static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key) static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
{ {
retry: retry:
switch (key) { switch (key) {
default: { default: {
int c = STB_TEXTEDIT_KEYTOTEXT(key); int c = STB_TEXTEDIT_KEYTOTEXT(key);
if (c > 0) { if (c > 0) {
STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c; IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE) c;
// can't add newline in single-line mode // can't add newline in single-line mode
if (c == '\n' && state->single_line) if (c == '\n' && state->single_line)
@ -889,8 +893,8 @@ retry:
x = row.x0; x = row.x0;
for (i=0; i < row.num_chars; ++i) { for (i=0; i < row.num_chars; ++i) {
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i); float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break; break;
#endif #endif
x += dx; x += dx;
@ -951,8 +955,8 @@ retry:
x = row.x0; x = row.x0;
for (i=0; i < row.num_chars; ++i) { for (i=0; i < row.num_chars; ++i) {
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break; break;
#endif #endif
x += dx; x += dx;
@ -1109,8 +1113,8 @@ retry:
static void stb_textedit_flush_redo(StbUndoState *state) static void stb_textedit_flush_redo(StbUndoState *state)
{ {
state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; state->redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; state->redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
} }
// discard the oldest entry in the undo list // discard the oldest entry in the undo list
@ -1122,13 +1126,13 @@ static void stb_textedit_discard_undo(StbUndoState *state)
int n = state->undo_rec[0].insert_length, i; int n = state->undo_rec[0].insert_length, i;
// delete n characters from all other records // delete n characters from all other records
state->undo_char_point -= n; state->undo_char_point -= n;
STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE))); IMSTB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
for (i=0; i < state->undo_point; ++i) for (i=0; i < state->undo_point; ++i)
if (state->undo_rec[i].char_storage >= 0) if (state->undo_rec[i].char_storage >= 0)
state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it
} }
--state->undo_point; --state->undo_point;
STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0]))); IMSTB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
} }
} }
@ -1138,7 +1142,7 @@ static void stb_textedit_discard_undo(StbUndoState *state)
// fill up even though the undo buffer didn't // fill up even though the undo buffer didn't
static void stb_textedit_discard_redo(StbUndoState *state) static void stb_textedit_discard_redo(StbUndoState *state)
{ {
int k = STB_TEXTEDIT_UNDOSTATECOUNT-1; int k = IMSTB_TEXTEDIT_UNDOSTATECOUNT-1;
if (state->redo_point <= k) { if (state->redo_point <= k) {
// if the k'th undo state has characters, clean those up // if the k'th undo state has characters, clean those up
@ -1146,7 +1150,7 @@ static void stb_textedit_discard_redo(StbUndoState *state)
int n = state->undo_rec[k].insert_length, i; int n = state->undo_rec[k].insert_length, i;
// move the remaining redo character data to the end of the buffer // move the remaining redo character data to the end of the buffer
state->redo_char_point += n; state->redo_char_point += n;
STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE))); IMSTB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((IMSTB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
// adjust the position of all the other records to account for above memmove // adjust the position of all the other records to account for above memmove
for (i=state->redo_point; i < k; ++i) for (i=state->redo_point; i < k; ++i)
if (state->undo_rec[i].char_storage >= 0) if (state->undo_rec[i].char_storage >= 0)
@ -1154,12 +1158,12 @@ static void stb_textedit_discard_redo(StbUndoState *state)
} }
// now move all the redo records towards the end of the buffer; the first one is at 'redo_point' // now move all the redo records towards the end of the buffer; the first one is at 'redo_point'
// [DEAR IMGUI] // [DEAR IMGUI]
size_t move_size = (size_t)((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0])); size_t move_size = (size_t)((IMSTB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
const char* buf_begin = (char*)state->undo_rec; (void)buf_begin; const char* buf_begin = (char*)state->undo_rec; (void)buf_begin;
const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end; const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end;
IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin); IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin);
IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end); IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end);
STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size); IMSTB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
// now move redo_point to point to the new one // now move redo_point to point to the new one
++state->redo_point; ++state->redo_point;
@ -1173,32 +1177,32 @@ static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numch
// if we have no free records, we have to make room, by sliding the // if we have no free records, we have to make room, by sliding the
// existing records down // existing records down
if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT) if (state->undo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
stb_textedit_discard_undo(state); stb_textedit_discard_undo(state);
// if the characters to store won't possibly fit in the buffer, we can't undo // if the characters to store won't possibly fit in the buffer, we can't undo
if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) { if (numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
state->undo_point = 0; state->undo_point = 0;
state->undo_char_point = 0; state->undo_char_point = 0;
return NULL; return NULL;
} }
// if we don't have enough free characters in the buffer, we have to make room // if we don't have enough free characters in the buffer, we have to make room
while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT) while (state->undo_char_point + numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT)
stb_textedit_discard_undo(state); stb_textedit_discard_undo(state);
return &state->undo_rec[state->undo_point++]; return &state->undo_rec[state->undo_point++];
} }
static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len) static IMSTB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
{ {
StbUndoRecord *r = stb_text_create_undo_record(state, insert_len); StbUndoRecord *r = stb_text_create_undo_record(state, insert_len);
if (r == NULL) if (r == NULL)
return NULL; return NULL;
r->where = pos; r->where = pos;
r->insert_length = (STB_TEXTEDIT_POSITIONTYPE) insert_len; r->insert_length = (IMSTB_TEXTEDIT_POSITIONTYPE) insert_len;
r->delete_length = (STB_TEXTEDIT_POSITIONTYPE) delete_len; r->delete_length = (IMSTB_TEXTEDIT_POSITIONTYPE) delete_len;
if (insert_len == 0) { if (insert_len == 0) {
r->char_storage = -1; r->char_storage = -1;
@ -1210,7 +1214,7 @@ static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos,
} }
} }
static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{ {
StbUndoState *s = &state->undostate; StbUndoState *s = &state->undostate;
StbUndoRecord u, *r; StbUndoRecord u, *r;
@ -1237,7 +1241,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
// characters stored for *undoing* don't leave room for redo // characters stored for *undoing* don't leave room for redo
// if the last is true, we have to bail // if the last is true, we have to bail
if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) { if (s->undo_char_point + u.delete_length >= IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
// the undo records take up too much character space; there's no space to store the redo characters // the undo records take up too much character space; there's no space to store the redo characters
r->insert_length = 0; r->insert_length = 0;
} else { } else {
@ -1246,7 +1250,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
// there's definitely room to store the characters eventually // there's definitely room to store the characters eventually
while (s->undo_char_point + u.delete_length > s->redo_char_point) { while (s->undo_char_point + u.delete_length > s->redo_char_point) {
// should never happen: // should never happen:
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
return; return;
// there's currently not enough room, so discard a redo record // there's currently not enough room, so discard a redo record
stb_textedit_discard_redo(s); stb_textedit_discard_redo(s);
@ -1278,11 +1282,11 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
s->redo_point--; s->redo_point--;
} }
static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{ {
StbUndoState *s = &state->undostate; StbUndoState *s = &state->undostate;
StbUndoRecord *u, r; StbUndoRecord *u, r;
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
return; return;
// we need to do two things: apply the redo record, and create an undo record // we need to do two things: apply the redo record, and create an undo record
@ -1334,20 +1338,20 @@ static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int le
stb_text_createundo(&state->undostate, where, 0, length); stb_text_createundo(&state->undostate, where, 0, length);
} }
static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length) static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
{ {
int i; int i;
STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0); IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
if (p) { if (p) {
for (i=0; i < length; ++i) for (i=0; i < length; ++i)
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
} }
} }
static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length) static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
{ {
int i; int i;
STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length); IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
if (p) { if (p) {
for (i=0; i < old_length; ++i) for (i=0; i < old_length; ++i)
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
@ -1359,8 +1363,8 @@ static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_lin
{ {
state->undostate.undo_point = 0; state->undostate.undo_point = 0;
state->undostate.undo_char_point = 0; state->undostate.undo_char_point = 0;
state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; state->undostate.redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; state->undostate.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
state->select_end = state->select_start = 0; state->select_end = state->select_start = 0;
state->cursor = 0; state->cursor = 0;
state->has_preferred_x = 0; state->has_preferred_x = 0;
@ -1383,16 +1387,16 @@ static void stb_textedit_initialize_state(STB_TexteditState *state, int is_singl
#pragma GCC diagnostic ignored "-Wcast-qual" #pragma GCC diagnostic ignored "-Wcast-qual"
#endif #endif
static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len) static int stb_textedit_paste(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE const *ctext, int len)
{ {
return stb_textedit_paste_internal(str, state, (STB_TEXTEDIT_CHARTYPE *) ctext, len); return stb_textedit_paste_internal(str, state, (IMSTB_TEXTEDIT_CHARTYPE *) ctext, len);
} }
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
#endif//STB_TEXTEDIT_IMPLEMENTATION #endif//IMSTB_TEXTEDIT_IMPLEMENTATION
/* /*
------------------------------------------------------------------------------ ------------------------------------------------------------------------------

View File

@ -0,0 +1,57 @@
Dear ImGui Test Engine License (v1.03)
Copyright (c) 2018-2023 Omar Cornut
This document is a legal agreement ("License") between you ("Licensee") and
DISCO HELLO ("Licensor") that governs your use of Dear ImGui Test Engine ("Software").
1. LICENSE MODELS
1.1. Free license
The Licensor grants you a free license ("Free License") if you meet ANY of the following
criterion:
- You are a natural person;
- You are not a legal entity, or you are a not-for-profit legal entity;
- You are using this Software for educational purposes;
- You are using this Software to create Derivative Software released publicly and under
an Open Source license, as defined by the Open Source Initiative;
- You are a legal entity with a turnover inferior to 2 million USD (or equivalent) during
your last fiscal year.
1.2. Paid license
If you do not meet any criterion of Article 1.1, Licensor grants you a trial period of a
maximum of 45 days, at no charge. Following this trial period, you must subscribe to a paid
license ("Paid License") with the Licensor to continue using the Software.
Paid Licenses are exclusively sold by DISCO HELLO. Paid Licenses and the associated
information are available at the following URL: http://www.dearimgui.com/licenses
2. GRANT OF LICENSE
2.1. License scope
A limited and non-exclusive license is hereby granted, to the Licensee, to reproduce,
execute, publicly perform, and display, use, copy, modify, merge, distribute, or create
derivative works based on and/or derived from the Software ("Derivative Software").
2.2. Right of distribution
License holders may also publish and/or distribute the Software or any Derivative
Software. The above copyright notice and this license shall be included in all copies
or substantial portions of the Software and/or Derivative Software.
License holders may add their own attribution notices within the Derivative Software
that they distribute. Such attribution notices must not directly or indirectly imply a
modification of the License. License holders may provide to their modifications their
own copyright and/or additional or different terms and conditions, providing such
conditions complies with this License.
3. DISCLAIMER
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE 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.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,180 @@
// dear imgui test engine
// (screen/video capture tool)
// This is usable as a standalone applet or controlled by the test engine.
#pragma once
#include "imgui_te_utils.h" // ImFuncPtr
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
// Our types
struct ImGuiCaptureArgs; // Parameters for Capture
struct ImGuiCaptureContext; // State of an active capture tool
struct ImGuiCaptureImageBuf; // Simple helper to store an RGBA image in memory
struct ImGuiCaptureToolUI; // Capture tool instance + UI window
typedef unsigned int ImGuiCaptureFlags; // See enum: ImGuiCaptureFlags_
// Capture function which needs to be provided by user application
typedef bool (ImGuiScreenCaptureFunc)(ImGuiID viewport_id, int x, int y, int w, int h, unsigned int* pixels, void* user_data);
// External types
struct ImGuiWindow; // imgui.h
//-----------------------------------------------------------------------------
// [Internal]
// Helper class for simple bitmap manipulation (not particularly efficient!)
struct IMGUI_API ImGuiCaptureImageBuf
{
int Width;
int Height;
unsigned int* Data; // RGBA8
ImGuiCaptureImageBuf() { Width = Height = 0; Data = NULL; }
~ImGuiCaptureImageBuf() { Clear(); }
void Clear(); // Free allocated memory buffer if such exists.
void CreateEmpty(int w, int h); // Reallocate buffer for pixel data and zero it.
bool SaveFile(const char* filename); // Save pixel data to specified image file.
void RemoveAlpha(); // Clear alpha channel from all pixels.
};
enum ImGuiCaptureFlags_ : unsigned int
{
ImGuiCaptureFlags_None = 0,
ImGuiCaptureFlags_StitchAll = 1 << 0, // Capture entire window scroll area (by scrolling and taking multiple screenshot). Only works for a single window.
ImGuiCaptureFlags_IncludeOtherWindows = 1 << 1, // Disable hiding other windows (when CaptureAddWindow has been called by default other windows are hidden)
ImGuiCaptureFlags_IncludeTooltipsAndPopups = 1 << 2, // Expand capture area to automatically include visible popups and tooltips (use with ImGuiCaptureflags_HideOtherWindows)
ImGuiCaptureFlags_HideMouseCursor = 1 << 3, // Hide render software mouse cursor during capture.
ImGuiCaptureFlags_Instant = 1 << 4, // Perform capture on very same frame. Only works when capturing a rectangular region. Unsupported features: content stitching, window hiding, window relocation.
ImGuiCaptureFlags_NoSave = 1 << 5 // Do not save output image.
};
// Defines input and output arguments for capture process.
// When capturing from tests you can usually use the ImGuiTestContext::CaptureXXX() helpers functions.
struct ImGuiCaptureArgs
{
// [Input]
ImGuiCaptureFlags InFlags = 0; // Flags for customizing behavior of screenshot tool.
ImVector<ImGuiWindow*> InCaptureWindows; // Windows to capture. All other windows will be hidden. May be used with InCaptureRect to capture only some windows in specified rect.
ImRect InCaptureRect; // Screen rect to capture. Does not include padding.
float InPadding = 16.0f; // Extra padding at the edges of the screenshot. Ensure that there is available space around capture rect horizontally, also vertically if ImGuiCaptureFlags_StitchFullContents is not used.
char InOutputFile[256] = ""; // Output will be saved to a file if InOutputImageBuf is NULL.
ImGuiCaptureImageBuf* InOutputImageBuf = NULL; // _OR_ Output will be saved to image buffer if specified.
int InRecordFPSTarget = 30; // FPS target for recording videos.
int InSizeAlign = 0; // Resolution alignment (0 = auto, 1 = no alignment, >= 2 = align width/height to be multiple of given value)
// [Output]
ImVec2 OutImageSize; // Produced image size.
};
enum ImGuiCaptureStatus
{
ImGuiCaptureStatus_InProgress,
ImGuiCaptureStatus_Done,
ImGuiCaptureStatus_Error
};
struct ImGuiCaptureWindowData
{
ImGuiWindow* Window;
ImRect BackupRect;
ImVec2 PosDuringCapture;
};
// Implements functionality for capturing images
struct IMGUI_API ImGuiCaptureContext
{
// IO
ImFuncPtr(ImGuiScreenCaptureFunc) ScreenCaptureFunc = NULL; // Graphics backend specific function that captures specified portion of framebuffer and writes RGBA data to `pixels` buffer.
void* ScreenCaptureUserData = NULL; // Custom user pointer which is passed to ScreenCaptureFunc. (Optional)
char* VideoCaptureEncoderPath = NULL; // Video encoder path (not owned, stored externally).
int VideoCaptureEncoderPathSize = 0; // Optional. Set in order to edit this parameter from UI.
char* VideoCaptureEncoderParams = NULL; // Video encoder params (not owned, stored externally).
int VideoCaptureEncoderParamsSize = 0; // Optional. Set in order to edit this parameter from UI.
char* GifCaptureEncoderParams = NULL; // Video encoder params for GIF output (not owned, stored externally).
int GifCaptureEncoderParamsSize = 0; // Optional. Set in order to edit this parameter from UI.
// [Internal]
ImRect _CaptureRect; // Viewport rect that is being captured.
ImRect _CapturedWindowRect; // Top-left corner of region that covers all windows included in capture. This is not same as _CaptureRect.Min when capturing explicitly specified rect.
int _ChunkNo = 0; // Number of chunk that is being captured when capture spans multiple frames.
int _FrameNo = 0; // Frame number during capture process that spans multiple frames.
ImVec2 _MouseRelativeToWindowPos; // Mouse cursor position relative to captured window (when _StitchAll is in use).
ImGuiWindow* _HoveredWindow = NULL; // Window which was hovered at capture start.
ImGuiCaptureImageBuf _CaptureBuf; // Output image buffer.
const ImGuiCaptureArgs* _CaptureArgs = NULL; // Current capture args. Set only if capture is in progress.
ImVector<ImGuiCaptureWindowData> _WindowsData; // Backup windows that will have their rect modified and restored. args->InCaptureWindows can not be used because popups may get closed during capture and no longer appear in that list.
// [Internal] Video recording
bool _VideoRecording = false; // Flag indicating that video recording is in progress.
double _VideoLastFrameTime = 0; // Time when last video frame was recorded.
FILE* _VideoEncoderPipe = NULL; // File writing to stdin of video encoder process.
// [Internal] Backups
bool _BackupMouseDrawCursor = false; // Initial value of g.IO.MouseDrawCursor
ImVec2 _BackupDisplayWindowPadding; // Backup padding. We set it to {0, 0} during capture.
ImVec2 _BackupDisplaySafeAreaPadding; // Backup padding. We set it to {0, 0} during capture.
//-------------------------------------------------------------------------
// Functions
//-------------------------------------------------------------------------
ImGuiCaptureContext(ImGuiScreenCaptureFunc capture_func = NULL) { ScreenCaptureFunc = capture_func; _MouseRelativeToWindowPos = ImVec2(-FLT_MAX, -FLT_MAX); }
// These functions should be called from appropriate context hooks. See ImGui::AddContextHook() for more info.
// (ImGuiTestEngine automatically calls that for you, so this only apply to independently created instance)
void PreNewFrame();
void PreRender();
void PostRender();
// Update capturing. If this function returns true then it should be called again with same arguments on the next frame.
ImGuiCaptureStatus CaptureUpdate(ImGuiCaptureArgs* args);
void RestoreBackedUpData();
void ClearState();
// Begin video capture. Call CaptureUpdate() every frame afterwards until it returns false.
void BeginVideoCapture(ImGuiCaptureArgs* args);
void EndVideoCapture();
bool IsCapturingVideo();
bool IsCapturing();
};
//-----------------------------------------------------------------------------
// ImGuiCaptureToolUI
//-----------------------------------------------------------------------------
// Implements UI for capturing images
// (when using ImGuiTestEngine scripting API you may not need to use this at all)
struct IMGUI_API ImGuiCaptureToolUI
{
float SnapGridSize = 32.0f; // Size of the grid cell for "snap to grid" functionality.
char OutputLastFilename[256] = ""; // File name of last captured file.
char* VideoCaptureExtension = NULL; // Video file extension (e.g. ".gif" or ".mp4")
int VideoCaptureExtensionSize = 0; // Optional. Set in order to edit this parameter from UI.
ImGuiCaptureArgs _CaptureArgs; // Capture args
bool _StateIsPickingWindow = false;
bool _StateIsCapturing = false;
ImVector<ImGuiID> _SelectedWindows;
char _OutputFileTemplate[256] = ""; //
int _FileCounter = 0; // Counter which may be appended to file name when saving. By default, counting starts from 1. When done this field holds number of saved files.
// Public
ImGuiCaptureToolUI();
void ShowCaptureToolWindow(ImGuiCaptureContext* context, bool* p_open = NULL); // Render a capture tool window with various options and utilities.
// [Internal]
void _CaptureWindowPicker(ImGuiCaptureArgs* args); // Render a window picker that captures picked window to file specified in file_name.
void _CaptureWindowsSelector(ImGuiCaptureContext* context, ImGuiCaptureArgs* args); // Render a selector for selecting multiple windows for capture.
void _SnapWindowsToGrid(float cell_size); // Snap edges of all visible windows to a virtual grid.
bool _InitializeOutputFile(); // Format output file template into capture args struct and ensure target directory exists.
bool _ShowEncoderConfigFields(ImGuiCaptureContext* context);
};
#define IMGUI_CAPTURE_DEFAULT_VIDEO_PARAMS_FOR_FFMPEG "-hide_banner -loglevel error -r $FPS -f rawvideo -pix_fmt rgba -s $WIDTHx$HEIGHT -i - -threads 0 -y -preset ultrafast -pix_fmt yuv420p -crf 20 $OUTPUT"
#define IMGUI_CAPTURE_DEFAULT_GIF_PARAMS_FOR_FFMPEG "-hide_banner -loglevel error -r $FPS -f rawvideo -pix_fmt rgba -s $WIDTHx$HEIGHT -i - -threads 0 -y -filter_complex \"split=2 [a] [b]; [a] palettegen [pal]; [b] [pal] paletteuse\" $OUTPUT"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,616 @@
// dear imgui test engine
// (context when a running test + end user automation API)
// This is the main (if not only) interface that your Tests will be using.
#pragma once
#include "imgui.h"
#include "imgui_internal.h" // ImGuiAxis, ImGuiItemStatusFlags, ImGuiInputSource, ImGuiWindow
#include "imgui_te_engine.h" // ImGuiTestStatus, ImGuiTestRunFlags, ImGuiTestActiveFunc, ImGuiTestItemInfo, ImGuiTestLogFlags
/*
Index of this file:
// [SECTION] Header mess, warnings
// [SECTION] Forward declarations
// [SECTION] ImGuiTestRef
// [SECTION] Helper keys
// [SECTION] ImGuiTestContext related Flags/Enumerations
// [SECTION] ImGuiTestGenericVars, ImGuiTestGenericItemStatus
// [SECTION] ImGuiTestContext
// [SECTION] Debugging macros: IM_SUSPEND_TESTFUNC()
// [SECTION] Testing/Checking macros: IM_CHECK(), IM_ERRORF() etc.
*/
//-------------------------------------------------------------------------
// [SECTION] Header mess, warnings
//-------------------------------------------------------------------------
// Undo some of the damage done by <windows.h>
#ifdef Yield
#undef Yield
#endif
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
#endif
//-------------------------------------------------------------------------
// [SECTION] Forward declarations
//-------------------------------------------------------------------------
// This file
typedef int ImGuiTestOpFlags; // Flags: See ImGuiTestOpFlags_
// External: imgui
struct ImGuiDockNode;
struct ImGuiTabBar;
struct ImGuiWindow;
// External: test engine
struct ImGuiTest; // A test registered with IM_REGISTER_TEST()
struct ImGuiTestEngine; // Test Engine Instance (opaque)
struct ImGuiTestEngineIO; // Test Engine IO structure (configuration flags, state)
struct ImGuiTestItemInfo; // Information gathered about an item: label, status, bounding box etc.
struct ImGuiTestItemList; // Result of an GatherItems() query
struct ImGuiTestInputs; // Test Engine Simulated Inputs structure (opaque)
struct ImGuiTestGatherTask; // Test Engine task for scanning/finding items
struct ImGuiCaptureArgs; // Parameters for ctx->CaptureXXX functions
enum ImGuiTestVerboseLevel : int;
//-------------------------------------------------------------------------
// [SECTION] ImGuiTestRef
//-------------------------------------------------------------------------
// Weak reference to an Item/Window given an hashed ID _or_ a string path ID.
// This is most often passed as argument to function and generally has a very short lifetime.
// Documentation: https://github.com/ocornut/imgui_test_engine/wiki/Named-References
// (SUGGESTION: add those constructors to "VA Step Filter" (Visual Assist) or a .natstepfilter file (Visual Studio) so they are skipped by F11 (StepInto)
struct IMGUI_API ImGuiTestRef
{
ImGuiID ID; // Pre-hashed ID
const char* Path; // Relative or absolute path (string pointed to, not owned, as our lifetime is very short)
ImGuiTestRef() { ID = 0; Path = NULL; }
ImGuiTestRef(ImGuiID id) { ID = id; Path = NULL; }
ImGuiTestRef(const char* path) { ID = 0; Path = path; }
bool IsEmpty() const { return ID == 0 && (Path == NULL || Path[0] == 0); }
};
// Debug helper to output a string showing the Path, ID or Debug Label based on what is available (some items only have ID as we couldn't find/store a Path)
// (The size is arbitrary, this is only used for logging info the user/debugger)
struct IMGUI_API ImGuiTestRefDesc
{
char Buf[80];
const char* c_str() { return Buf; }
ImGuiTestRefDesc(const ImGuiTestRef& ref, const ImGuiTestItemInfo* item);
};
//-------------------------------------------------------------------------
// [SECTION] ImGuiTestContext related Flags/Enumerations
//-------------------------------------------------------------------------
// Named actions. Generally you will call the named helpers e.g. ItemClick(). This is used by shared/low-level functions such as ItemAction().
enum ImGuiTestAction
{
ImGuiTestAction_Unknown = 0,
ImGuiTestAction_Hover, // Move mouse
ImGuiTestAction_Click, // Move mouse and click
ImGuiTestAction_DoubleClick, // Move mouse and double-click
ImGuiTestAction_Check, // Check item if unchecked (Checkbox, MenuItem or any widget reporting ImGuiItemStatusFlags_Checkable)
ImGuiTestAction_Uncheck, // Uncheck item if checked
ImGuiTestAction_Open, // Open item if closed (TreeNode, BeginMenu or any widget reporting ImGuiItemStatusFlags_Openable)
ImGuiTestAction_Close, // Close item if opened
ImGuiTestAction_Input, // Start text inputing into a field (e.g. CTRL+Click on Drags/Slider, click on InputText etc.)
ImGuiTestAction_NavActivate, // Activate item with navigation
ImGuiTestAction_COUNT
};
// Generic flags for many ImGuiTestContext functions
enum ImGuiTestOpFlags_
{
ImGuiTestOpFlags_None = 0,
ImGuiTestOpFlags_NoCheckHoveredId = 1 << 1, // Don't check for HoveredId after aiming for a widget. A few situations may want this: while e.g. dragging or another items prevents hovering, or for items that don't use ItemHoverable()
ImGuiTestOpFlags_NoError = 1 << 2, // Don't abort/error e.g. if the item cannot be found or the operation doesn't succeed.
ImGuiTestOpFlags_NoFocusWindow = 1 << 3, // Don't focus window when aiming at an item
ImGuiTestOpFlags_NoAutoUncollapse = 1 << 4, // Disable automatically uncollapsing windows (useful when specifically testing Collapsing behaviors)
ImGuiTestOpFlags_NoAutoOpenFullPath = 1 << 5, // Disable automatically opening intermediaries (e.g. ItemClick("Hello/OK") will automatically first open "Hello" if "OK" isn't found. Only works if ref is a string path.
ImGuiTestOpFlags_IsSecondAttempt = 1 << 6, // Used by recursing functions to indicate a second attempt
ImGuiTestOpFlags_MoveToEdgeL = 1 << 7, // Simple Dumb aiming helpers to test widget that care about clicking position. May need to replace will better functionalities.
ImGuiTestOpFlags_MoveToEdgeR = 1 << 8,
ImGuiTestOpFlags_MoveToEdgeU = 1 << 9,
ImGuiTestOpFlags_MoveToEdgeD = 1 << 10,
};
// Advanced filtering for ItemActionAll()
struct IMGUI_API ImGuiTestActionFilter
{
int MaxDepth;
int MaxPasses;
const int* MaxItemCountPerDepth;
ImGuiItemStatusFlags RequireAllStatusFlags;
ImGuiItemStatusFlags RequireAnyStatusFlags;
ImGuiTestActionFilter() { MaxDepth = -1; MaxPasses = -1; MaxItemCountPerDepth = NULL; RequireAllStatusFlags = RequireAnyStatusFlags = 0; }
};
//-------------------------------------------------------------------------
// [SECTION] ImGuiTestGenericVars, ImGuiTestGenericItemStatus
//-------------------------------------------------------------------------
// Helper struct to store various query-able state of an item.
// This facilitate interactions between GuiFunc and TestFunc, since those state are frequently used.
struct IMGUI_API ImGuiTestGenericItemStatus
{
int RetValue; // return value
int Hovered; // result of IsItemHovered()
int Active; // result of IsItemActive()
int Focused; // result of IsItemFocused()
int Clicked; // result of IsItemClicked()
int Visible; // result of IsItemVisible()
int Edited; // result of IsItemEdited()
int Activated; // result of IsItemActivated()
int Deactivated; // result of IsItemDeactivated()
int DeactivatedAfterEdit; // result of IsItemDeactivatedAfterEdit()
ImGuiTestGenericItemStatus() { Clear(); }
void Clear() { memset(this, 0, sizeof(*this)); }
void QuerySet(bool ret_val = false) { Clear(); QueryInc(ret_val); }
void QueryInc(bool ret_val = false) { RetValue += ret_val; Hovered += ImGui::IsItemHovered(); Active += ImGui::IsItemActive(); Focused += ImGui::IsItemFocused(); Clicked += ImGui::IsItemClicked(); Visible += ImGui::IsItemVisible(); Edited += ImGui::IsItemEdited(); Activated += ImGui::IsItemActivated(); Deactivated += ImGui::IsItemDeactivated(); DeactivatedAfterEdit += ImGui::IsItemDeactivatedAfterEdit(); }
};
// Generic structure with various storage fields.
// This is useful for tests to quickly share data between GuiFunc and TestFunc without creating custom data structure.
// If those fields are not enough: using test->SetVarsDataType<>() + ctx->GetVars<>() it is possible to store custom data.
struct IMGUI_API ImGuiTestGenericVars
{
// Generic storage with a bit of semantic to make user/test code look neater
int Step;
int Count;
ImGuiID DockId;
ImGuiID OwnerId;
ImGuiWindowFlags WindowFlags;
ImGuiTableFlags TableFlags;
ImGuiPopupFlags PopupFlags;
ImGuiTestGenericItemStatus Status;
bool ShowWindow1, ShowWindow2;
bool UseClipper;
bool UseViewports;
float Width;
ImVec2 Pos;
ImVec2 Size;
ImVec2 Pivot;
ImVec4 Color1, Color2;
// Generic unnamed storage
int Int1, Int2, IntArray[10];
float Float1, Float2, FloatArray[10];
bool Bool1, Bool2, BoolArray[10];
ImGuiID Id, IdArray[10];
char Str1[256], Str2[256];
ImGuiTestGenericVars() { Clear(); }
void Clear() { memset(this, 0, sizeof(*this)); }
};
//-------------------------------------------------------------------------
// [SECTION] ImGuiTestContext
// Context for a running ImGuiTest
// This is the interface that most tests will interact with.
//-------------------------------------------------------------------------
struct IMGUI_API ImGuiTestContext
{
// User variables
ImGuiTestGenericVars GenericVars; // Generic variables holder for convenience.
void* UserVars = NULL; // Access using ctx->GetVars<Type>(). Setup with test->SetVarsDataType<>().
// Public fields
ImGuiContext* UiContext = NULL; // UI context
ImGuiTestEngineIO* EngineIO = NULL; // Test Engine IO/settings
ImGuiTest* Test = NULL; // Test currently running
ImGuiTestOutput* TestOutput = NULL; // Test output (generally == &Test->Output)
ImGuiTestOpFlags OpFlags = ImGuiTestOpFlags_None; // Flags affecting all operation (supported: ImGuiTestOpFlags_NoAutoUncollapse)
int PerfStressAmount = 0; // Convenience copy of engine->IO.PerfStressAmount
int FrameCount = 0; // Test frame count (restarts from zero every time)
int FirstTestFrameCount = 0; // First frame where TestFunc is running (after warm-up frame). This is generally -1 or 0 depending on whether we have warm up enabled
bool FirstGuiFrame = false;
bool HasDock = false; // #ifdef IMGUI_HAS_DOCK expressed in an easier to test value
ImGuiCaptureArgs* CaptureArgs = NULL; // Capture settings used by ctx->Capture*() functions
//-------------------------------------------------------------------------
// [Internal Fields]
//-------------------------------------------------------------------------
ImGuiTestEngine* Engine = NULL;
ImGuiTestInputs* Inputs = NULL;
ImGuiTestRunFlags RunFlags = ImGuiTestRunFlags_None;
ImGuiTestActiveFunc ActiveFunc = ImGuiTestActiveFunc_None; // None/GuiFunc/TestFunc
double RunningTime = 0.0; // Amount of wall clock time the Test has been running. Used by safety watchdog.
int ActionDepth = 0; // Nested depth of ctx-> function calls (used to decorate log)
int CaptureCounter = 0; // Number of captures
int ErrorCounter = 0; // Number of errors (generally this maxxes at 1 as most functions will early out)
bool Abort = false;
double PerfRefDt = -1.0;
int PerfIterations = 400; // Number of frames for PerfCapture() measurements
char RefStr[256] = { 0 }; // Reference window/path over which all named references are based
ImGuiID RefID = 0; // Reference ID over which all named references are based
ImGuiID RefWindowID = 0; // ID of a window that contains RefID item
ImGuiInputSource InputMode = ImGuiInputSource_Mouse; // Prefer interacting with mouse/keyboard/gamepad
ImVector<char> Clipboard; // Private clipboard for the test instance
ImVector<ImGuiWindow*> ForeignWindowsToHide;
ImGuiTestItemInfo DummyItemInfoNull; // Storage for ItemInfoNull()
bool CachedLinesPrintedToTTY = false;
//-------------------------------------------------------------------------
// Public API
//-------------------------------------------------------------------------
// Main control
void RecoverFromUiContextErrors();
void Finish(ImGuiTestStatus status = ImGuiTestStatus_Success); // Set test status and stop running. Usually called when running test logic from GuiFunc() only.
ImGuiTestStatus RunChildTest(const char* test_name, ImGuiTestRunFlags flags = 0); // [Experimental] Run another test from the current test.
template <typename T> T& GetVars() { IM_ASSERT(UserVars != NULL); return *(T*)(UserVars); }// Campanion to using t->SetVarsDataType<>(). FIXME: Assert to compare sizes
// Main status queries
bool IsError() const { return TestOutput->Status == ImGuiTestStatus_Error || Abort; }
bool IsWarmUpGuiFrame() const { return FrameCount < FirstTestFrameCount; } // Unless test->Flags has ImGuiTestFlags_NoGuiWarmUp, we run GuiFunc() twice before running TestFunc(). Those frames are called "WarmUp" frames.
bool IsFirstGuiFrame() const { return FirstGuiFrame; }
bool IsFirstTestFrame() const { return FrameCount == FirstTestFrameCount; } // First frame where TestFunc is running (after warm-up frame).
bool IsGuiFuncOnly() const { return (RunFlags & ImGuiTestRunFlags_GuiFuncOnly) != 0; }
// Debugging
bool SuspendTestFunc(const char* file = NULL, int line = 0); // [DEBUG] Generally called via IM_SUSPEND_TESTFUNC
// Logging
void LogEx(ImGuiTestVerboseLevel level, ImGuiTestLogFlags flags, const char* fmt, ...) IM_FMTARGS(4);
void LogExV(ImGuiTestVerboseLevel level, ImGuiTestLogFlags flags, const char* fmt, va_list args) IM_FMTLIST(4);
void LogToTTY(ImGuiTestVerboseLevel level, const char* message, const char* message_end = NULL);
void LogToDebugger(ImGuiTestVerboseLevel level, const char* message);
void LogDebug(const char* fmt, ...) IM_FMTARGS(2); // ImGuiTestVerboseLevel_Debug or ImGuiTestVerboseLevel_Trace depending on context depth
void LogInfo(const char* fmt, ...) IM_FMTARGS(2); // ImGuiTestVerboseLevel_Info
void LogWarning(const char* fmt, ...) IM_FMTARGS(2); // ImGuiTestVerboseLevel_Warning
void LogError(const char* fmt, ...) IM_FMTARGS(2); // ImGuiTestVerboseLevel_Error
void LogBasicUiState();
void LogItemList(ImGuiTestItemList* list);
// Yield, Timing
void Yield(int count = 1);
void YieldUntil(int frame_count);
void Sleep(float time_in_second); // Sleep for a given simulation time, unless in Fast mode
void SleepShort(); // Standard short delay of io.ActionDelayShort (~0.15f), unless in Fast mode.
void SleepStandard(); // Standard regular delay of io.ActionDelayStandard (~0.40f), unless in Fast mode.
void SleepNoSkip(float time_in_second, float framestep_in_second);
// Base Reference
// - ItemClick("Window/Button") --> click "Window/Button"
// - SetRef("Window"), ItemClick("Button") --> click "Window/Button"
// - SetRef("Window"), ItemClick("/Button") --> click "Window/Button"
// - SetRef("Window"), ItemClick("//Button") --> click "/Button"
// - SetRef("//$FOCUSED"), ItemClick("Button") --> click "Button" in focused window.
// See https://github.com/ocornut/imgui_test_engine/wiki/Named-References about using ImGuiTestRef in all ImGuiTestContext functions.
// Note: SetRef() may take multiple frames to complete if specified ref is an item id.
void SetRef(ImGuiTestRef ref);
void SetRef(ImGuiWindow* window); // Shortcut to SetRef(window->Name) which works for ChildWindow (see code)
ImGuiTestRef GetRef();
// Windows
ImGuiTestItemInfo* WindowInfo(ImGuiTestRef window_ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
void WindowClose(ImGuiTestRef window_ref);
void WindowCollapse(ImGuiTestRef window_ref, bool collapsed);
void WindowFocus(ImGuiTestRef window_ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
void WindowBringToFront(ImGuiTestRef window_ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
void WindowMove(ImGuiTestRef window_ref, ImVec2 pos, ImVec2 pivot = ImVec2(0.0f, 0.0f), ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
void WindowResize(ImGuiTestRef window_ref, ImVec2 sz);
bool WindowTeleportToMakePosVisible(ImGuiTestRef window_ref, ImVec2 pos_in_window);
ImGuiWindow*GetWindowByRef(ImGuiTestRef window_ref);
// Popups
void PopupCloseOne();
void PopupCloseAll();
ImGuiID PopupGetWindowID(ImGuiTestRef ref);
// Get hash for a decorated ID Path.
// Note: for windows you may use WindowInfo()
ImGuiID GetID(ImGuiTestRef ref);
ImGuiID GetID(ImGuiTestRef ref, ImGuiTestRef seed_ref);
// Miscellaneous helpers
ImVec2 GetPosOnVoid(ImGuiViewport* viewport); // Find a point that has no windows // FIXME: This needs error return and flag to enable/disable forcefully finding void.
ImVec2 GetWindowTitlebarPoint(ImGuiTestRef window_ref); // Return a clickable point on window title-bar (window tab for docked windows).
ImVec2 GetMainMonitorWorkPos(); // Work pos and size of main viewport when viewports are disabled, or work pos and size of monitor containing main viewport when viewports are enabled.
ImVec2 GetMainMonitorWorkSize();
// Screenshot/Video Captures
void CaptureReset(); // Reset state (use when doing multiple captures)
void CaptureSetExtension(const char* ext); // Set capture file format (otherwise for video this default to EngineIO->VideoCaptureExtension)
bool CaptureAddWindow(ImGuiTestRef ref); // Add window to be captured (default to capture everything)
void CaptureScreenshotWindow(ImGuiTestRef ref, int capture_flags = 0); // Trigger a screen capture of a single window (== CaptureAddWindow() + CaptureScreenshot())
bool CaptureScreenshot(int capture_flags = 0); // Trigger a screen capture
bool CaptureBeginVideo(); // Start a video capture
bool CaptureEndVideo();
// Mouse inputs
void MouseMove(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
void MouseMoveToPos(ImVec2 pos);
void MouseTeleportToPos(ImVec2 pos);
void MouseClick(ImGuiMouseButton button = 0);
void MouseClickMulti(ImGuiMouseButton button, int count);
void MouseDoubleClick(ImGuiMouseButton button = 0);
void MouseDown(ImGuiMouseButton button = 0);
void MouseUp(ImGuiMouseButton button = 0);
void MouseLiftDragThreshold(ImGuiMouseButton button = 0);
void MouseDragWithDelta(ImVec2 delta, ImGuiMouseButton button = 0);
void MouseWheel(ImVec2 delta);
void MouseWheelX(float dx) { MouseWheel(ImVec2(dx, 0.0f)); }
void MouseWheelY(float dy) { MouseWheel(ImVec2(0.0f, dy)); }
void MouseMoveToVoid(ImGuiViewport* viewport = NULL);
void MouseClickOnVoid(ImGuiMouseButton button = 0, ImGuiViewport* viewport = NULL);
ImGuiWindow*FindHoveredWindowAtPos(const ImVec2& pos);
bool FindExistingVoidPosOnViewport(ImGuiViewport* viewport, ImVec2* out);
// Mouse inputs: Viewports
// - This is automatically called by SetRef() and any mouse action taking an item reference (e.g. ItemClick("button"), MouseClick("button"))
// - But when using raw position directy e.g. MouseMoveToPos() / MouseTeleportToPos() without referring to the parent window before, this needs to be set.
void MouseSetViewport(ImGuiWindow* window);
void MouseSetViewportID(ImGuiID viewport_id);
// Keyboard inputs
void KeyDown(ImGuiKeyChord key_chord);
void KeyUp(ImGuiKeyChord key_chord);
void KeyPress(ImGuiKeyChord key_chord, int count = 1);
void KeyHold(ImGuiKeyChord key_chord, float time);
void KeySetEx(ImGuiKeyChord key_chord, bool is_down, float time);
void KeyChars(const char* chars); // Input characters
void KeyCharsAppend(const char* chars); // Input characters at end of field
void KeyCharsAppendEnter(const char* chars); // Input characters at end of field, press Enter
void KeyCharsReplace(const char* chars); // Delete existing field then input characters
void KeyCharsReplaceEnter(const char* chars); // Delete existing field then input characters, press Enter
// Navigation inputs
// FIXME: Need some redesign/refactoring:
// - This was initially intended to: replace mouse action with keyboard/gamepad
// - Abstract keyboard vs gamepad actions
// However this is widely inconsistent and unfinished at this point.
void SetInputMode(ImGuiInputSource input_mode); // Mouse or Keyboard or Gamepad. In Keyboard or Gamepad mode, actions such as ItemClick or ItemInput are using nav facilities instead of Mouse.
void NavMoveTo(ImGuiTestRef ref);
void NavActivate(); // Activate current selected item: activate button, tweak sliders/drags. Equivalent of pressing Space on keyboard, ImGuiKey_GamepadFaceUp on a gamepad.
void NavInput(); // Input into select item: input sliders/drags. Equivalent of pressing Enter on keyboard, ImGuiKey_GamepadFaceDown on a gamepad.
// Scrolling
void ScrollTo(ImGuiTestRef ref, ImGuiAxis axis, float scroll_v, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
void ScrollToX(ImGuiTestRef ref, float scroll_x) { ScrollTo(ref, ImGuiAxis_X, scroll_x); }
void ScrollToY(ImGuiTestRef ref, float scroll_y) { ScrollTo(ref, ImGuiAxis_Y, scroll_y); }
void ScrollToTop(ImGuiTestRef ref);
void ScrollToBottom(ImGuiTestRef ref);
void ScrollToItem(ImGuiTestRef ref, ImGuiAxis axis, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
void ScrollToItemX(ImGuiTestRef ref);
void ScrollToItemY(ImGuiTestRef ref);
void ScrollToTabItem(ImGuiTabBar* tab_bar, ImGuiID tab_id);
bool ScrollErrorCheck(ImGuiAxis axis, float expected, float actual, int* remaining_attempts);
void ScrollVerifyScrollMax(ImGuiTestRef ref);
// Low-level queries
// - ItemInfo queries never returns a NULL pointer, instead they return an empty instance (info->IsEmpty(), info->ID == 0) and set contexted as errored.
// - You can use ImGuiTestOpFlags_NoError to do a query without marking context as errored. This is what ItemExists() does.
ImGuiTestItemInfo* ItemInfo(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
ImGuiTestItemInfo* ItemInfoOpenFullPath(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);
ImGuiID ItemInfoHandleWildcardSearch(const char* wildcard_prefix_start, const char* wildcard_prefix_end, const char* wildcard_suffix_start);
ImGuiTestItemInfo* ItemInfoNull();
void GatherItems(ImGuiTestItemList* out_list, ImGuiTestRef parent, int depth = -1);
// Item/Widgets manipulation
void ItemAction(ImGuiTestAction action, ImGuiTestRef ref, ImGuiTestOpFlags flags = 0, void* action_arg = NULL);
void ItemClick(ImGuiTestRef ref, ImGuiMouseButton button = 0, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Click, ref, flags, (void*)(size_t)button); }
void ItemDoubleClick(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_DoubleClick, ref, flags); }
void ItemCheck(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Check, ref, flags); }
void ItemUncheck(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Uncheck, ref, flags); }
void ItemOpen(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Open, ref, flags); }
void ItemClose(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Close, ref, flags); }
void ItemInput(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Input, ref, flags); }
void ItemNavActivate(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_NavActivate, ref, flags); }
bool ItemOpenFullPath(ImGuiTestRef);
// Item/Widgets: Batch actions over an entire scope
void ItemActionAll(ImGuiTestAction action, ImGuiTestRef ref_parent, const ImGuiTestActionFilter* filter = NULL);
void ItemOpenAll(ImGuiTestRef ref_parent, int depth = -1, int passes = -1);
void ItemCloseAll(ImGuiTestRef ref_parent, int depth = -1, int passes = -1);
// Item/Widgets: Helpers to easily set a value
void ItemInputValue(ImGuiTestRef ref, int v);
void ItemInputValue(ImGuiTestRef ref, float f);
void ItemInputValue(ImGuiTestRef ref, const char* str);
// Item/Widgets: Drag and Mouse operations
void ItemHold(ImGuiTestRef ref, float time);
void ItemHoldForFrames(ImGuiTestRef ref, int frames);
void ItemDragOverAndHold(ImGuiTestRef ref_src, ImGuiTestRef ref_dst);
void ItemDragAndDrop(ImGuiTestRef ref_src, ImGuiTestRef ref_dst, ImGuiMouseButton button = 0);
void ItemDragWithDelta(ImGuiTestRef ref_src, ImVec2 pos_delta);
// Item/Widgets: Status query
bool ItemExists(ImGuiTestRef ref);
bool ItemIsChecked(ImGuiTestRef ref);
bool ItemIsOpened(ImGuiTestRef ref);
void ItemVerifyCheckedIfAlive(ImGuiTestRef ref, bool checked);
// Helpers for Tab Bars widgets
void TabClose(ImGuiTestRef ref);
bool TabBarCompareOrder(ImGuiTabBar* tab_bar, const char** tab_order);
// Helpers for MenuBar and Menus widgets
// - e.g. MenuCheck("File/Options/Enable grid");
void MenuAction(ImGuiTestAction action, ImGuiTestRef ref);
void MenuActionAll(ImGuiTestAction action, ImGuiTestRef ref_parent);
void MenuClick(ImGuiTestRef ref) { MenuAction(ImGuiTestAction_Click, ref); }
void MenuCheck(ImGuiTestRef ref) { MenuAction(ImGuiTestAction_Check, ref); }
void MenuUncheck(ImGuiTestRef ref) { MenuAction(ImGuiTestAction_Uncheck, ref); }
void MenuCheckAll(ImGuiTestRef ref_parent) { MenuActionAll(ImGuiTestAction_Check, ref_parent); }
void MenuUncheckAll(ImGuiTestRef ref_parent) { MenuActionAll(ImGuiTestAction_Uncheck, ref_parent); }
// Helpers for Combo Boxes
void ComboClick(ImGuiTestRef ref);
void ComboClickAll(ImGuiTestRef ref);
// Helpers for Tables
void TableOpenContextMenu(ImGuiTestRef ref, int column_n = -1);
ImGuiSortDirection TableClickHeader(ImGuiTestRef ref, const char* label, ImGuiKeyChord key_mods = 0);
void TableSetColumnEnabled(ImGuiTestRef ref, const char* label, bool enabled);
void TableResizeColumn(ImGuiTestRef ref, int column_n, float width);
const ImGuiTableSortSpecs* TableGetSortSpecs(ImGuiTestRef ref);
// Viewports
// IMPORTANT: Those function may alter Platform state (unless using the "Mock Viewport" backend). Use carefully.
// Those are mostly useful to simulate OS actions and testing of viewport-specific features, may not be useful to most users.
#ifdef IMGUI_HAS_VIEWPORT
//void ViewportPlatform_SetWindowPos(ImGuiViewport* viewport, const ImVec2& pos);
//void ViewportPlatform_SetWindowSize(ImGuiViewport* viewport, const ImVec2& size);
void ViewportPlatform_SetWindowFocus(ImGuiViewport* viewport);
void ViewportPlatform_CloseWindow(ImGuiViewport* viewport);
#endif
// Docking
#ifdef IMGUI_HAS_DOCK
void DockClear(const char* window_name, ...);
void DockInto(ImGuiTestRef src_id, ImGuiTestRef dst_id, ImGuiDir split_dir = ImGuiDir_None, bool is_outer_docking = false, ImGuiTestOpFlags flags = 0);
void UndockNode(ImGuiID dock_id);
void UndockWindow(const char* window_name);
bool WindowIsUndockedOrStandalone(ImGuiWindow* window);
bool DockIdIsUndockedOrStandalone(ImGuiID dock_id);
void DockNodeHideTabBar(ImGuiDockNode* node, bool hidden);
#endif
// Performances Measurement (use along with Dear ImGui Perf Tool)
void PerfCalcRef();
void PerfCapture(const char* category = NULL, const char* test_name = NULL, const char* csv_file = NULL);
// Obsolete functions
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
// Obsoleted 2022/10/11
ImGuiID GetIDByInt(int n); // Prefer using "$$123"
ImGuiID GetIDByInt(int n, ImGuiTestRef seed_ref);
ImGuiID GetIDByPtr(void* p); // Prefer using "$$(ptr)0xFFFFFFFF"
ImGuiID GetIDByPtr(void* p, ImGuiTestRef seed_ref);
// Obsoleted 2022/09/26
void KeyModDown(ImGuiModFlags mods) { KeyDown(mods); }
void KeyModUp(ImGuiModFlags mods) { KeyUp(mods); }
void KeyModPress(ImGuiModFlags mods) { KeyPress(mods); }
#endif
// [Internal]
// FIXME: Aim to remove this system...
void ForeignWindowsHideOverPos(ImVec2 pos, ImGuiWindow** ignore_list);
void ForeignWindowsUnhideAll();
};
//-------------------------------------------------------------------------
// [SECTION] Debugging macros (IM_SUSPEND_TESTFUNC)
//-------------------------------------------------------------------------
// Debug: Temporarily suspend TestFunc to let user interactively inspect the GUI state (user will need to press the "Continue" button to resume TestFunc execution)
#define IM_SUSPEND_TESTFUNC() do { if (ctx->SuspendTestFunc(__FILE__, __LINE__)) return; } while (0)
//-------------------------------------------------------------------------
// [SECTION] Testing/Checking macros: IM_CHECK(), IM_ERRORF() etc.
//-------------------------------------------------------------------------
// Helpers used by IM_CHECK_OP() macros.
// ImGuiTestEngine_GetTempStringBuilder() returns a shared instance of ImGuiTextBuffer to recycle memory allocations
template<typename T> void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, T v) { buf->append("???"); IM_UNUSED(v); } // FIXME-TESTS: Could improve with some template magic
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, const char* v) { buf->appendf("\"%s\"", v); }
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, bool v) { buf->append(v ? "true" : "false"); }
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImS8 v) { buf->appendf("%d", v); }
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImU8 v) { buf->appendf("%u", v); }
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImS16 v) { buf->appendf("%hd", v); }
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImU16 v) { buf->appendf("%hu", v); }
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImS32 v) { buf->appendf("%d", v); }
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImU32 v) { buf->appendf("0x%08X", v); } // Assuming ImGuiID
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImS64 v) { buf->appendf("%lld", v); }
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImU64 v) { buf->appendf("%llu", v); }
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, float v) { buf->appendf("%.3f", v); }
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, double v) { buf->appendf("%f", v); }
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImVec2 v) { buf->appendf("(%.3f, %.3f)", v.x, v.y); }
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, const void* v) { buf->appendf("%p", v); }
template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImGuiWindow* v){ if (v) buf->appendf("\"%s\"", v->Name); else buf->append("NULL"); }
// We embed every macro in a do {} while(0) statement as a trick to allow using them as regular single statement, e.g. if (XXX) IM_CHECK(A); else IM_CHECK(B)
// We leave the IM_DEBUG_BREAK() outside of the check function to step out faster when using a debugger. It also has the benefit of being lighter than an IM_ASSERT().
#define IM_CHECK(_EXPR) do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, res, #_EXPR)) { IM_DEBUG_BREAK(); } if (!res) return; } while (0)
#define IM_CHECK_NO_RET(_EXPR) do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, res, #_EXPR)) { IM_DEBUG_BREAK(); } } while (0)
#define IM_CHECK_SILENT(_EXPR) do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_SilentSuccess, res, #_EXPR)) { IM_DEBUG_BREAK(); } if (!res) return; } while (0)
#define IM_CHECK_RETV(_EXPR,_RETV) do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, res, #_EXPR)) { IM_DEBUG_BREAK(); } if (!res) return _RETV; } while (0)
#define IM_CHECK_SILENT_RETV(_EXPR,_RETV) do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_SilentSuccess, res, #_EXPR)) { IM_DEBUG_BREAK(); } if (!res) return _RETV; } while (0)
#define IM_ERRORF(_FMT,...) do { if (ImGuiTestEngine_Error(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, _FMT, __VA_ARGS__)) { IM_DEBUG_BREAK(); } } while (0)
#define IM_ERRORF_NOHDR(_FMT,...) do { if (ImGuiTestEngine_Error(NULL, NULL, 0, ImGuiTestCheckFlags_None, _FMT, __VA_ARGS__)) { IM_DEBUG_BREAK(); } } while (0)
// Those macros allow us to print out the values of both LHS and RHS expressions involved in a check.
#define IM_CHECK_OP(_LHS, _RHS, _OP, _RETURN) \
do \
{ \
auto __lhs = _LHS; /* Cache to avoid side effects */ \
auto __rhs = _RHS; \
bool __res = __lhs _OP __rhs; \
ImGuiTextBuffer* expr_buf = ImGuiTestEngine_GetTempStringBuilder(); \
expr_buf->append(#_LHS " ["); \
ImGuiTestEngineUtil_appendf_auto(expr_buf, __lhs); \
expr_buf->append("] " #_OP " " #_RHS " ["); \
ImGuiTestEngineUtil_appendf_auto(expr_buf, __rhs); \
expr_buf->append("]"); \
if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, __res, expr_buf->c_str())) \
IM_ASSERT(__res); \
if (_RETURN && !__res) \
return; \
} while (0)
#define IM_CHECK_STR_OP(_LHS, _RHS, _OP, _RETURN, _FLAGS) \
do \
{ \
bool __res; \
if (ImGuiTestEngine_CheckStrOp(__FILE__, __func__, __LINE__, _FLAGS, #_OP, #_LHS, _LHS, #_RHS, _RHS, &__res)) \
IM_ASSERT(__res); \
if (_RETURN && !__res) \
return; \
} while (0)
// Scalar compares
#define IM_CHECK_EQ(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, ==, true) // Equal
#define IM_CHECK_NE(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, !=, true) // Not Equal
#define IM_CHECK_LT(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, < , true) // Less Than
#define IM_CHECK_LE(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, <=, true) // Less or Equal
#define IM_CHECK_GT(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, > , true) // Greater Than
#define IM_CHECK_GE(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, >=, true) // Greater or Equal
// Scalar compares, without return on failure
#define IM_CHECK_EQ_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, ==, false) // Equal
#define IM_CHECK_NE_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, !=, false) // Not Equal
#define IM_CHECK_LT_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, < , false) // Less Than
#define IM_CHECK_LE_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, <=, false) // Less or Equal
#define IM_CHECK_GT_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, > , false) // Greater Than
#define IM_CHECK_GE_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, >=, false) // Greater or Equal
// String compares
#define IM_CHECK_STR_EQ(_LHS, _RHS) IM_CHECK_STR_OP(_LHS, _RHS, ==, true, ImGuiTestCheckFlags_None)
#define IM_CHECK_STR_NE(_LHS, _RHS) IM_CHECK_STR_OP(_LHS, _RHS, !=, true, ImGuiTestCheckFlags_None)
#define IM_CHECK_STR_EQ_NO_RET(_LHS, _RHS) IM_CHECK_STR_OP(_LHS, _RHS, ==, false, ImGuiTestCheckFlags_None)
#define IM_CHECK_STR_NE_NO_RET(_LHS, _RHS) IM_CHECK_STR_OP(_LHS, _RHS, !=, false, ImGuiTestCheckFlags_None)
#define IM_CHECK_STR_EQ_SILENT(_LHS, _RHS) IM_CHECK_STR_OP(_LHS, _RHS, ==, true, ImGuiTestCheckFlags_SilentSuccess)
// Floating point compares
#define IM_CHECK_FLOAT_EQ_EPS(_LHS, _RHS) IM_CHECK_LE(ImFabs(_LHS - (_RHS)), FLT_EPSILON) // Float Equal
#define IM_CHECK_FLOAT_NEAR(_LHS, _RHS, _EPS) IM_CHECK_LE(ImFabs(_LHS - (_RHS)), _EPS)
#define IM_CHECK_FLOAT_NEAR_NO_RET(_LHS, _RHS, _E) IM_CHECK_LE_NO_RET(ImFabs(_LHS - (_RHS)), _E)
//-------------------------------------------------------------------------
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif

View File

@ -0,0 +1,167 @@
// dear imgui test engine
// (coroutine interface + optional implementation)
// Read https://github.com/ocornut/imgui_test_engine/wiki/Setting-Up
#include "imgui_te_coroutine.h"
#include "imgui.h"
#ifdef _MSC_VER
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
#endif
//------------------------------------------------------------------------
// Coroutine implementation using std::thread
// This implements a coroutine using std::thread, with a helper thread for each coroutine (with serialised execution, so threads never actually run concurrently)
//------------------------------------------------------------------------
#if IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL
#include "imgui_te_utils.h"
#include "thirdparty/Str/Str.h"
#include <thread>
#include <mutex>
#include <condition_variable>
struct Coroutine_ImplStdThreadData
{
std::thread* Thread; // The thread this coroutine is using
std::condition_variable StateChange; // Condition variable notified when the coroutine state changes
std::mutex StateMutex; // Mutex to protect coroutine state
bool CoroutineRunning; // Is the coroutine currently running? Lock StateMutex before access and notify StateChange on change
bool CoroutineTerminated; // Has the coroutine terminated? Lock StateMutex before access and notify StateChange on change
Str64 Name; // The name of this coroutine
};
// The coroutine executing on the current thread (if it is a coroutine thread)
static thread_local Coroutine_ImplStdThreadData* GThreadCoroutine = NULL;
// The main function for a coroutine thread
static void CoroutineThreadMain(Coroutine_ImplStdThreadData* data, ImGuiTestCoroutineMainFunc func, void* ctx)
{
// Set our thread name
ImThreadSetCurrentThreadDescription(data->Name.c_str());
// Set the thread coroutine
GThreadCoroutine = data;
// Wait for initial Run()
while (1)
{
std::unique_lock<std::mutex> lock(data->StateMutex);
if (data->CoroutineRunning)
break;
data->StateChange.wait(lock);
}
// Run user code, which will then call Yield() when it wants to yield control
func(ctx);
// Mark as terminated
{
std::lock_guard<std::mutex> lock(data->StateMutex);
data->CoroutineTerminated = true;
data->CoroutineRunning = false;
data->StateChange.notify_all();
}
}
static ImGuiTestCoroutineHandle Coroutine_ImplStdThread_Create(ImGuiTestCoroutineMainFunc* func, const char* name, void* ctx)
{
Coroutine_ImplStdThreadData* data = new Coroutine_ImplStdThreadData();
data->Name = name;
data->CoroutineRunning = false;
data->CoroutineTerminated = false;
data->Thread = new std::thread(CoroutineThreadMain, data, func, ctx);
return (ImGuiTestCoroutineHandle)data;
}
static void Coroutine_ImplStdThread_Destroy(ImGuiTestCoroutineHandle handle)
{
Coroutine_ImplStdThreadData* data = (Coroutine_ImplStdThreadData*)handle;
IM_ASSERT(data->CoroutineTerminated); // The coroutine needs to run to termination otherwise it may leak all sorts of things and this will deadlock
if (data->Thread)
{
data->Thread->join();
delete data->Thread;
data->Thread = NULL;
}
delete data;
data = NULL;
}
// Run the coroutine until the next call to Yield(). Returns TRUE if the coroutine yielded, FALSE if it terminated (or had previously terminated)
static bool Coroutine_ImplStdThread_Run(ImGuiTestCoroutineHandle handle)
{
Coroutine_ImplStdThreadData* data = (Coroutine_ImplStdThreadData*)handle;
// Wake up coroutine thread
{
std::lock_guard<std::mutex> lock(data->StateMutex);
if (data->CoroutineTerminated)
return false; // Coroutine has already finished
data->CoroutineRunning = true;
data->StateChange.notify_all();
}
// Wait for coroutine to stop
while (1)
{
std::unique_lock<std::mutex> lock(data->StateMutex);
if (!data->CoroutineRunning)
{
// Breakpoint here to catch the point where we return from the coroutine
if (data->CoroutineTerminated)
return false; // Coroutine finished
break;
}
data->StateChange.wait(lock);
}
return true;
}
// Yield the current coroutine (can only be called from a coroutine)
static void Coroutine_ImplStdThread_Yield()
{
IM_ASSERT(GThreadCoroutine); // This can only be called from a coroutine thread
Coroutine_ImplStdThreadData* data = GThreadCoroutine;
// Flag that we are not running any more
{
std::lock_guard<std::mutex> lock(data->StateMutex);
data->CoroutineRunning = false;
data->StateChange.notify_all();
}
// At this point the thread that called RunCoroutine() will leave the "Wait for coroutine to stop" loop
// Wait until we get started up again
while (1)
{
std::unique_lock<std::mutex> lock(data->StateMutex);
if (data->CoroutineRunning)
break; // Breakpoint here if you want to catch the point where execution of this coroutine resumes
data->StateChange.wait(lock);
}
}
ImGuiTestCoroutineInterface* Coroutine_ImplStdThread_GetInterface()
{
static ImGuiTestCoroutineInterface intf;
intf.CreateFunc = Coroutine_ImplStdThread_Create;
intf.DestroyFunc = Coroutine_ImplStdThread_Destroy;
intf.RunFunc = Coroutine_ImplStdThread_Run;
intf.YieldFunc = Coroutine_ImplStdThread_Yield;
return &intf;
}
#endif // #if IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL

View File

@ -0,0 +1,56 @@
// dear imgui test engine
// (coroutine interface + optional implementation)
// Read https://github.com/ocornut/imgui_test_engine/wiki/Setting-Up
#pragma once
#ifndef IMGUI_VERSION
#include "imgui.h"
#endif
//------------------------------------------------------------------------
// Coroutine abstraction
//------------------------------------------------------------------------
// Coroutines should be used like this:
// ImGuiTestCoroutineHandle handle = CoroutineCreate(<func>, <name>, <ctx>); // name being for debugging, and ctx being an arbitrary user context pointer
// while (CoroutineRun(handle)) { <do other stuff };
// CoroutineDestroy(handle);
// The coroutine code itself should call CoroutineYieldFunc() whenever it wants to yield control back to the main thread.
//------------------------------------------------------------------------
// An arbitrary handle used internally to represent coroutines (NULL indicates no handle)
typedef void* ImGuiTestCoroutineHandle;
// A coroutine main function
typedef void (ImGuiTestCoroutineMainFunc)(void* data);
// Coroutine support interface
// Your app needs to return and implement this.
// You can '#define IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL 1' in your imconfig file to use a default implementation using std::thread
// Documentation: https://github.com/ocornut/imgui_test_engine/wiki/Setting-Up
struct IMGUI_API ImGuiTestCoroutineInterface
{
// Create a new coroutine
ImGuiTestCoroutineHandle (*CreateFunc)(ImGuiTestCoroutineMainFunc* func, const char* name, void* data);
// Destroy a coroutine (which must have completed first)
void (*DestroyFunc)(ImGuiTestCoroutineHandle handle);
// Run a coroutine until it yields or finishes, returning false if finished
bool (*RunFunc)(ImGuiTestCoroutineHandle handle);
// Yield from a coroutine back to the caller, preserving coroutine state
void (*YieldFunc)();
};
//------------------------------------------------------------------------
// Coroutine implementation using std::thread
// The "coroutine" thread and user's main thread will always block on each other (both threads will NEVER run in parallel)
// It is just an implementation convenience that we provide an implementation using std::thread as it is widely available/standard.
//------------------------------------------------------------------------
#if IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL
IMGUI_API ImGuiTestCoroutineInterface* Coroutine_ImplStdThread_GetInterface();
#endif // #if IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,429 @@
// dear imgui test engine
// (core)
// This is the interface that your initial setup (app init, main loop) will mostly be using.
// Actual tests will mostly use the interface of imgui_te_context.h
#pragma once
#include "imgui.h"
#include "imgui_internal.h" // ImPool<>, ImRect, ImGuiItemStatusFlags, ImFormatString
#include "imgui_te_utils.h" // ImFuncPtr
#include "imgui_capture_tool.h" // ImGuiScreenCaptureFunc
//-------------------------------------------------------------------------
// Forward Declarations
//-------------------------------------------------------------------------
struct ImGuiTest; // Data for a test registered with IM_REGISTER_TEST()
struct ImGuiTestContext; // Context while a test is running
struct ImGuiTestCoroutineInterface; // Interface to expose coroutine functions (imgui_te_coroutine provides a default implementation for C++11 using std::thread, but you may use your own)
struct ImGuiTestEngine; // Test engine instance
struct ImGuiTestEngineIO; // Test engine public I/O
struct ImGuiTestItemInfo; // Info queried from item (id, geometry, status flags, debug label)
struct ImGuiTestItemList; // A list of items
struct ImGuiTestInputs; // Simulated user inputs (will be fed into ImGuiIO by the test engine)
struct ImGuiTestRunTask; // A queued test (test + runflags)
typedef int ImGuiTestFlags; // Flags: See ImGuiTestFlags_
typedef int ImGuiTestCheckFlags; // Flags: See ImGuiTestCheckFlags_
typedef int ImGuiTestLogFlags; // Flags: See ImGuiTestLogFlags_
typedef int ImGuiTestRunFlags; // Flags: See ImGuiTestRunFlags_
enum ImGuiTestActiveFunc : int;
enum ImGuiTestGroup : int;
enum ImGuiTestRunSpeed : int;
enum ImGuiTestStatus : int;
enum ImGuiTestVerboseLevel : int;
enum ImGuiTestEngineExportFormat : int;
//-------------------------------------------------------------------------
// Types
//-------------------------------------------------------------------------
// Stored in ImGuiTestContext: where we are currently running GuiFunc or TestFunc
enum ImGuiTestActiveFunc : int
{
ImGuiTestActiveFunc_None,
ImGuiTestActiveFunc_GuiFunc,
ImGuiTestActiveFunc_TestFunc
};
enum ImGuiTestRunSpeed : int
{
ImGuiTestRunSpeed_Fast = 0, // Run tests as fast as possible (teleport mouse, skip delays, etc.)
ImGuiTestRunSpeed_Normal = 1, // Run tests at human watchable speed (for debugging)
ImGuiTestRunSpeed_Cinematic = 2, // Run tests with pauses between actions (for e.g. tutorials)
ImGuiTestRunSpeed_COUNT
};
enum ImGuiTestVerboseLevel : int
{
ImGuiTestVerboseLevel_Silent = 0, // -v0
ImGuiTestVerboseLevel_Error = 1, // -v1
ImGuiTestVerboseLevel_Warning = 2, // -v2
ImGuiTestVerboseLevel_Info = 3, // -v3
ImGuiTestVerboseLevel_Debug = 4, // -v4
ImGuiTestVerboseLevel_Trace = 5,
ImGuiTestVerboseLevel_COUNT
};
// Test status (stored in ImGuiTest)
enum ImGuiTestStatus : int
{
ImGuiTestStatus_Unknown = -1,
ImGuiTestStatus_Success = 0,
ImGuiTestStatus_Queued = 1,
ImGuiTestStatus_Running = 2,
ImGuiTestStatus_Error = 3,
ImGuiTestStatus_Suspended = 4,
ImGuiTestStatus_COUNT
};
// Test group: this is mostly used to categorize tests in our testing UI. (Stored in ImGuiTest)
enum ImGuiTestGroup : int
{
ImGuiTestGroup_Unknown = -1,
ImGuiTestGroup_Tests = 0,
ImGuiTestGroup_Perfs = 1,
ImGuiTestGroup_COUNT
};
// Flags (stored in ImGuiTest)
enum ImGuiTestFlags_
{
ImGuiTestFlags_None = 0,
ImGuiTestFlags_NoGuiWarmUp = 1 << 0, // Disable running the GUI func for 2 frames before starting test code. For tests which absolutely need to start before GuiFunc.
ImGuiTestFlags_NoAutoFinish = 1 << 1, // By default, tests with no TestFunc (only a GuiFunc) will end after warmup. Setting this require test to call ctx->Finish().
ImGuiTestFlags_NoRecoveryWarnings = 1 << 2 // Disable state recovery warnings (missing End/Pop calls etc.) for tests which may rely on those.
//ImGuiTestFlags_RequireViewports = 1 << 10
};
// Flags for IM_CHECK* macros.
enum ImGuiTestCheckFlags_
{
ImGuiTestCheckFlags_None = 0,
ImGuiTestCheckFlags_SilentSuccess = 1 << 0
};
// Flags for ImGuiTestContext::Log* functions.
enum ImGuiTestLogFlags_
{
ImGuiTestLogFlags_None = 0,
ImGuiTestLogFlags_NoHeader = 1 << 0 // Do not display frame count and depth padding
};
enum ImGuiTestRunFlags_
{
ImGuiTestRunFlags_None = 0,
ImGuiTestRunFlags_GuiFuncDisable = 1 << 0, // Used internally to temporarily disable the GUI func (at the end of a test, etc)
ImGuiTestRunFlags_GuiFuncOnly = 1 << 1, // Set when user selects "Run GUI func"
ImGuiTestRunFlags_NoSuccessMsg = 1 << 2,
ImGuiTestRunFlags_EnableRawInputs = 1 << 3, // Disable input submission to let test submission raw input event (in order to test e.g. IO queue)
ImGuiTestRunFlags_RunFromGui = 1 << 4, // Test ran manually from GUI, will disable watchdog.
ImGuiTestRunFlags_RunFromCommandLine= 1 << 5, // Test queued from command-line.
// Flags for ImGuiTestContext::RunChildTest()
ImGuiTestRunFlags_NoError = 1 << 10,
ImGuiTestRunFlags_ShareVars = 1 << 11, // Share generic vars and custom vars between child and parent tests (custom vars need to be same type)
ImGuiTestRunFlags_ShareTestContext = 1 << 12, // Share ImGuiTestContext instead of creating a new one (unsure what purpose this may be useful for yet)
// TODO: Add GuiFunc options
};
//-------------------------------------------------------------------------
// Functions
//-------------------------------------------------------------------------
// Hooks for core imgui/ library (generally called via macros)
extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ui_ctx, ImGuiID id, const ImRect& bb, const ImGuiLastItemData* item_data);
#if IMGUI_VERSION_NUM < 18934
extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ui_ctx, const ImRect& bb, ImGuiID id);
#endif
#ifdef IMGUI_HAS_IMSTR
extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ui_ctx, ImGuiID id, ImStrv label, ImGuiItemStatusFlags flags);
#else
extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ui_ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags);
#endif
extern void ImGuiTestEngineHook_Log(ImGuiContext* ui_ctx, const char* fmt, ...);
extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ui_ctx, ImGuiID id);
// Functions (generally called via IM_CHECK() macros)
IMGUI_API bool ImGuiTestEngine_Check(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, bool result, const char* expr);
IMGUI_API bool ImGuiTestEngine_CheckStrOp(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, const char* op, const char* lhs_var, const char* lhs_value, const char* rhs_var, const char* rhs_value, bool* out_result);
IMGUI_API bool ImGuiTestEngine_Error(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, const char* fmt, ...);
IMGUI_API void ImGuiTestEngine_AssertLog(const char* expr, const char* file, const char* function, int line);
IMGUI_API ImGuiTextBuffer* ImGuiTestEngine_GetTempStringBuilder();
//-------------------------------------------------------------------------
// ImGuiTestEngine API
//-------------------------------------------------------------------------
// Functions: Initialization
IMGUI_API ImGuiTestEngine* ImGuiTestEngine_CreateContext(); // Create test engine
IMGUI_API void ImGuiTestEngine_DestroyContext(ImGuiTestEngine* engine); // Destroy test engine. Call after ImGui::DestroyContext() so test engine specific ini data gets saved.
IMGUI_API void ImGuiTestEngine_Start(ImGuiTestEngine* engine, ImGuiContext* ui_ctx); // Bind to a dear imgui context. Start coroutine.
IMGUI_API void ImGuiTestEngine_Stop(ImGuiTestEngine* engine); // Stop coroutine and export if any. (Unbind will lazily happen on context shutdown)
IMGUI_API void ImGuiTestEngine_PostSwap(ImGuiTestEngine* engine); // Call every frame after framebuffer swap, will process screen capture and call test_io.ScreenCaptureFunc()
IMGUI_API ImGuiTestEngineIO& ImGuiTestEngine_GetIO(ImGuiTestEngine* engine);
// Macros: Register Test
#define IM_REGISTER_TEST(_ENGINE, _CATEGORY, _NAME) ImGuiTestEngine_RegisterTest(_ENGINE, _CATEGORY, _NAME, __FILE__, __LINE__)
IMGUI_API ImGuiTest* ImGuiTestEngine_RegisterTest(ImGuiTestEngine* engine, const char* category, const char* name, const char* src_file = NULL, int src_line = 0); // Prefer calling IM_REGISTER_TEST()
// Functions: Main
IMGUI_API void ImGuiTestEngine_QueueTest(ImGuiTestEngine* engine, ImGuiTest* test, ImGuiTestRunFlags run_flags = 0);
IMGUI_API void ImGuiTestEngine_QueueTests(ImGuiTestEngine* engine, ImGuiTestGroup group, const char* filter = NULL, ImGuiTestRunFlags run_flags = 0);
IMGUI_API bool ImGuiTestEngine_TryAbortEngine(ImGuiTestEngine* engine);
IMGUI_API void ImGuiTestEngine_AbortCurrentTest(ImGuiTestEngine* engine);
IMGUI_API ImGuiTest* ImGuiTestEngine_FindTestByName(ImGuiTestEngine* engine, const char* category, const char* name);
// Functions: Status Queries
// FIXME: Clarify API to avoid function calls vs raw bools in ImGuiTestEngineIO
IMGUI_API bool ImGuiTestEngine_IsTestQueueEmpty(ImGuiTestEngine* engine);
IMGUI_API bool ImGuiTestEngine_IsUsingSimulatedInputs(ImGuiTestEngine* engine);
IMGUI_API void ImGuiTestEngine_GetResult(ImGuiTestEngine* engine, int& count_tested, int& success_count);
IMGUI_API void ImGuiTestEngine_GetTestList(ImGuiTestEngine* engine, ImVector<ImGuiTest*>* out_tests);
IMGUI_API void ImGuiTestEngine_GetTestQueue(ImGuiTestEngine* engine, ImVector<ImGuiTestRunTask>* out_tests);
// Functions: Crash Handling
// Ensure past test results are properly exported even if application crash during a test.
IMGUI_API void ImGuiTestEngine_InstallDefaultCrashHandler(); // Install default crash handler (if you don't have one)
IMGUI_API void ImGuiTestEngine_CrashHandler(); // Default crash handler, should be called from a custom crash handler if such exists
//-----------------------------------------------------------------------------
// IO structure to configure the test engine
//-----------------------------------------------------------------------------
// Function bound to right-clicking on a test and selecting "Open source" in the UI
// - Easy: you can make this function call OS shell to "open" the file (e.g. ImOsOpenInShell() helper).
// - Better: bind this function to a custom setup which can pass line number to a text editor (e.g. see 'imgui_test_suite/tools/win32_open_with_sublime.cmd' example)
typedef void (ImGuiTestEngineSrcFileOpenFunc)(const char* filename, int line_no, void* user_data);
struct IMGUI_API ImGuiTestEngineIO
{
//-------------------------------------------------------------------------
// Functions
//-------------------------------------------------------------------------
// Options: Functions
ImGuiTestCoroutineInterface* CoroutineFuncs = NULL; // (Required) Coroutine functions (see imgui_te_coroutines.h)
ImFuncPtr(ImGuiTestEngineSrcFileOpenFunc) SrcFileOpenFunc = NULL; // (Optional) To open source files from test engine UI
ImFuncPtr(ImGuiScreenCaptureFunc) ScreenCaptureFunc = NULL; // (Optional) To capture graphics output (application _MUST_ call ImGuiTestEngine_PostSwap() function after swapping is framebuffer)
void* SrcFileOpenUserData = NULL; // (Optional) User data for SrcFileOpenFunc
void* ScreenCaptureUserData = NULL; // (Optional) User data for ScreenCaptureFunc
// Options: Main
bool ConfigSavedSettings = true; // Load/Save settings in main context .ini file.
ImGuiTestRunSpeed ConfigRunSpeed = ImGuiTestRunSpeed_Fast; // Run tests in fast/normal/cinematic mode
bool ConfigStopOnError = false; // Stop queued tests on test error
bool ConfigBreakOnError = false; // Break debugger on test error by calling IM_DEBUG_BREAK()
bool ConfigKeepGuiFunc = false; // Keep test GUI running at the end of the test
ImGuiTestVerboseLevel ConfigVerboseLevel = ImGuiTestVerboseLevel_Warning;
ImGuiTestVerboseLevel ConfigVerboseLevelOnError = ImGuiTestVerboseLevel_Info;
bool ConfigLogToTTY = false;
bool ConfigLogToDebugger = false;
bool ConfigRestoreFocusAfterTests = true;// Restore focus back after running tests
bool ConfigCaptureEnabled = true; // Master enable flags for capturing and saving captures. Disable to avoid e.g. lengthy saving of large PNG files.
bool ConfigCaptureOnError = false;
bool ConfigNoThrottle = false; // Disable vsync for performance measurement or fast test running
bool ConfigMouseDrawCursor = true; // Enable drawing of Dear ImGui software mouse cursor when running tests
float ConfigFixedDeltaTime = 0.0f; // Use fixed delta time instead of calculating it from wall clock
int PerfStressAmount = 1; // Integer to scale the amount of items submitted in test
char GitBranchName[64] = ""; // e.g. fill in branch name (e.g. recorded in perf samples .csv)
// Options: Speed of user simulation
float MouseSpeed = 600.0f; // Mouse speed (pixel/second) when not running in fast mode
float MouseWobble = 0.25f; // (0.0f..1.0f) How much wobble to apply to the mouse (pixels per pixel of move distance) when not running in fast mode
float ScrollSpeed = 1400.0f; // Scroll speed (pixel/second) when not running in fast mode
float TypingSpeed = 20.0f; // Char input speed (characters/second) when not running in fast mode
float ActionDelayShort = 0.15f; // Time between short actions
float ActionDelayStandard = 0.40f; // Time between most actions
// Options: Screen/video capture
char VideoCaptureEncoderPath[256] = ""; // Video encoder executable path, e.g. "path/to/ffmpeg.exe".
char VideoCaptureEncoderParams[256] = "";// Video encoder parameters for .MP4 captures, e.g. see IMGUI_CAPTURE_DEFAULT_VIDEO_PARAMS_FOR_FFMPEG
char GifCaptureEncoderParams[512] = ""; // Video encoder parameters for .GIF captures, e.g. see IMGUI_CAPTURE_DEFAULT_GIF_PARAMS_FOR_FFMPEG
char VideoCaptureExtension[8] = ".mp4"; // Video file extension (default, may be overridden by test).
// Options: Watchdog. Set values to FLT_MAX to disable.
// Interactive GUI applications that may be slower tend to use higher values.
float ConfigWatchdogWarning = 30.0f; // Warn when a test exceed this time (in second)
float ConfigWatchdogKillTest = 60.0f; // Attempt to stop running a test when exceeding this time (in second)
float ConfigWatchdogKillApp = FLT_MAX; // Stop application when exceeding this time (in second)
// Options: Export
// While you can manually call ImGuiTestEngine_Export(), registering filename/format here ensure the crash handler will always export if application crash.
const char* ExportResultsFilename = NULL;
ImGuiTestEngineExportFormat ExportResultsFormat = (ImGuiTestEngineExportFormat)0;
// Options: Sanity Checks
bool CheckDrawDataIntegrity = false; // Check ImDrawData integrity (buffer count, etc.). Currently cheap but may become a slow operation.
//-------------------------------------------------------------------------
// Output
//-------------------------------------------------------------------------
// Output: State of test engine
bool IsRunningTests = false;
bool IsRequestingMaxAppSpeed = false; // When running in fast mode: request app to skip vsync or even skip rendering if it wants
bool IsCapturing = false; // Capture is in progress
};
//-------------------------------------------------------------------------
// ImGuiTestItemInfo
//-------------------------------------------------------------------------
// Information about a given item or window, result of an ItemInfo() or WindowInfo() query
struct ImGuiTestItemInfo
{
int RefCount : 8; // User can increment this if they want to hold on the result pointer across frames, otherwise the task will be GC-ed.
unsigned int NavLayer : 1; // Nav layer of the item (ImGuiNavLayer)
int Depth : 16; // Depth from requested parent id. 0 == ID is immediate child of requested parent id.
int TimestampMain = -1; // Timestamp of main result (all fields)
int TimestampStatus = -1; // Timestamp of StatusFlags
ImGuiID ID = 0; // Item ID
ImGuiID ParentID = 0; // Item Parent ID (value at top of the ID stack)
ImGuiWindow* Window = NULL; // Item Window
ImRect RectFull = ImRect(); // Item Rectangle
ImRect RectClipped = ImRect(); // Item Rectangle (clipped with window->ClipRect at time of item submission)
ImGuiItemFlags InFlags = 0; // Item flags
ImGuiItemStatusFlags StatusFlags = 0; // Item Status flags (fully updated for some items only, compare TimestampStatus to FrameCount)
char DebugLabel[32] = {}; // Shortened label for debugging purpose
ImGuiTestItemInfo() { RefCount = 0; NavLayer = 0; Depth = 0; }
bool IsEmpty() const { return ID == 0; }
};
// Result of an GatherItems() query
struct IMGUI_API ImGuiTestItemList
{
ImPool<ImGuiTestItemInfo> Pool;
void Clear() { Pool.Clear(); }
void Reserve(int capacity) { Pool.Reserve(capacity); }
int GetSize() const { return Pool.GetMapSize(); }
const ImGuiTestItemInfo* GetByIndex(int n) { return Pool.GetByIndex(n); }
const ImGuiTestItemInfo* GetByID(ImGuiID id) { return Pool.GetByKey(id); }
// For range-for
size_t size() const { return (size_t)Pool.GetMapSize(); }
const ImGuiTestItemInfo* begin() const { return Pool.Buf.begin(); }
const ImGuiTestItemInfo* end() const { return Pool.Buf.end(); }
const ImGuiTestItemInfo* operator[] (size_t n) { return &Pool.Buf[(int)n]; }
};
//-------------------------------------------------------------------------
// ImGuiTestLog: store textual output of one given Test.
//-------------------------------------------------------------------------
struct IMGUI_API ImGuiTestLogLineInfo
{
ImGuiTestVerboseLevel Level;
int LineOffset;
};
struct IMGUI_API ImGuiTestLog
{
ImGuiTextBuffer Buffer;
ImVector<ImGuiTestLogLineInfo> LineInfo;
int CountPerLevel[ImGuiTestVerboseLevel_COUNT] = {};
// Functions
ImGuiTestLog() {}
bool IsEmpty() const { return Buffer.empty(); }
void Clear();
// Extract log contents filtered per log-level.
// Output:
// - If 'buffer != NULL': all extracted lines are appended to 'buffer'. Use 'buffer->c_str()' on your side to obtain the text.
// - Return value: number of lines extracted (should be equivalent to number of '\n' inside buffer->c_str()).
// - You may call the function with buffer == NULL to only obtain a count without getting the data.
// Verbose levels are inclusive:
// - To get ONLY Error: Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Error
// - To get ONLY Error and Warnings: Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Warning
// - To get All Errors, Warnings, Debug... Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Trace
int ExtractLinesForVerboseLevels(ImGuiTestVerboseLevel level_min, ImGuiTestVerboseLevel level_max, ImGuiTextBuffer* out_buffer);
// [Internal]
void UpdateLineOffsets(ImGuiTestEngineIO* engine_io, ImGuiTestVerboseLevel level, const char* start);
};
//-------------------------------------------------------------------------
// ImGuiTest
//-------------------------------------------------------------------------
typedef void (ImGuiTestGuiFunc)(ImGuiTestContext* ctx);
typedef void (ImGuiTestTestFunc)(ImGuiTestContext* ctx);
// Wraps a placement new of a given type (where 'buffer' is the allocated memory)
typedef void (ImGuiTestVarsConstructor)(void* buffer);
typedef void (ImGuiTestVarsPostConstructor)(ImGuiTestContext* ctx, void* ptr, void* fn);
typedef void (ImGuiTestVarsDestructor)(void* ptr);
// Storage for the output of a test run
struct IMGUI_API ImGuiTestOutput
{
ImGuiTestStatus Status = ImGuiTestStatus_Unknown;
ImGuiTestLog Log;
ImU64 StartTime = 0;
ImU64 EndTime = 0;
};
// Storage for one test
struct IMGUI_API ImGuiTest
{
// Test Definition
const char* Category = NULL; // Literal, not owned
const char* Name = NULL; // Literal, generally not owned unless NameOwned=true
ImGuiTestGroup Group = ImGuiTestGroup_Unknown; // Coarse groups: 'Tests' or 'Perf'
bool NameOwned = false; //
const char* SourceFile = NULL; // __FILE__
int SourceLine = 0; // __LINE__
int SourceLineEnd = 0; // Calculated by ImGuiTestEngine_StartCalcSourceLineEnds()
int ArgVariant = 0; // User parameter. Generally we use it to run variations of a same test by sharing GuiFunc/TestFunc
ImGuiTestFlags Flags = ImGuiTestFlags_None; // See ImGuiTestFlags_
ImFuncPtr(ImGuiTestGuiFunc) GuiFunc = NULL; // GUI function (optional if your test are running over an existing GUI application)
ImFuncPtr(ImGuiTestTestFunc) TestFunc = NULL; // Test function
void* UserData = NULL; // General purpose user data (if assigning capturing lambdas on GuiFunc/TestFunc you may not need to use this)
//ImVector<ImGuiTestRunTask> Dependencies; // Registered via AddDependencyTest(), ran automatically before our test. This is a simpler wrapper to calling ctx->RunChildTest()
// Last Test Output/Status
// (this is the only part that may change after registration)
ImGuiTestOutput Output;
// User variables (which are instantiated when running the test)
// Setup after test registration with SetVarsDataType<>(), access instance during test with GetVars<>().
// This is mostly useful to communicate between GuiFunc and TestFunc. If you don't use both you may not want to use it!
size_t VarsSize = 0;
ImGuiTestVarsConstructor* VarsConstructor = NULL;
ImGuiTestVarsPostConstructor* VarsPostConstructor = NULL; // To override constructor default (in case the default are problematic on the first GuiFunc frame)
void* VarsPostConstructorUserFn = NULL;
ImGuiTestVarsDestructor* VarsDestructor = NULL;
// Functions
ImGuiTest() {}
~ImGuiTest();
void SetOwnedName(const char* name);
template <typename T>
void SetVarsDataType(void(*post_initialize)(ImGuiTestContext* ctx, T& vars) = NULL)
{
VarsSize = sizeof(T);
VarsConstructor = [](void* ptr) { IM_PLACEMENT_NEW(ptr) T; };
VarsDestructor = [](void* ptr) { IM_UNUSED(ptr); reinterpret_cast<T*>(ptr)->~T(); };
if (post_initialize != NULL)
{
VarsPostConstructorUserFn = (void*)post_initialize;
VarsPostConstructor = [](ImGuiTestContext* ctx, void* ptr, void* fn) { ((void (*)(ImGuiTestContext*, T&))(fn))(ctx, *(T*)ptr); };
}
}
};
// Stored in test queue
struct IMGUI_API ImGuiTestRunTask
{
ImGuiTest* Test = NULL;
ImGuiTestRunFlags RunFlags = ImGuiTestRunFlags_None;
};
//-------------------------------------------------------------------------

View File

@ -0,0 +1,303 @@
// dear imgui test engine
// (result exporters)
// Read https://github.com/ocornut/imgui_test_engine/wiki/Exporting-Results
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "imgui_te_exporters.h"
#include "imgui_te_engine.h"
#include "imgui_te_internal.h"
#include "thirdparty/Str/Str.h"
//-------------------------------------------------------------------------
// [SECTION] FORWARD DECLARATIONS
//-------------------------------------------------------------------------
static void ImGuiTestEngine_ExportJUnitXml(ImGuiTestEngine* engine, const char* output_file);
//-------------------------------------------------------------------------
// [SECTION] TEST ENGINE EXPORTER FUNCTIONS
//-------------------------------------------------------------------------
// - ImGuiTestEngine_PrintResultSummary()
// - ImGuiTestEngine_Export()
// - ImGuiTestEngine_ExportEx()
// - ImGuiTestEngine_ExportJUnitXml()
//-------------------------------------------------------------------------
void ImGuiTestEngine_PrintResultSummary(ImGuiTestEngine* engine)
{
int count_tested = 0;
int count_success = 0;
ImGuiTestEngine_GetResult(engine, count_tested, count_success);
if (count_success < count_tested)
{
printf("\nFailing tests:\n");
for (ImGuiTest* test : engine->TestsAll)
if (test->Output.Status == ImGuiTestStatus_Error)
printf("- %s\n", test->Name);
}
ImOsConsoleSetTextColor(ImOsConsoleStream_StandardOutput, (count_success == count_tested) ? ImOsConsoleTextColor_BrightGreen : ImOsConsoleTextColor_BrightRed);
printf("\nTests Result: %s\n", (count_success == count_tested) ? "OK" : "Errors");
printf("(%d/%d tests passed)\n", count_success, count_tested);
ImOsConsoleSetTextColor(ImOsConsoleStream_StandardOutput, ImOsConsoleTextColor_White);
}
// This is mostly a copy of ImGuiTestEngine_PrintResultSummary with few additions.
static void ImGuiTestEngine_ExportResultSummary(ImGuiTestEngine* engine, FILE* fp, int indent_count, ImGuiTestGroup group)
{
int count_tested = 0;
int count_success = 0;
for (ImGuiTest* test : engine->TestsAll)
{
if (test->Group != group)
continue;
if (test->Output.Status != ImGuiTestStatus_Unknown)
count_tested++;
if (test->Output.Status == ImGuiTestStatus_Success)
count_success++;
}
Str64 indent_str;
indent_str.reserve(indent_count + 1);
memset(indent_str.c_str(), ' ', indent_count);
indent_str[indent_count] = 0;
const char* indent = indent_str.c_str();
if (count_success < count_tested)
{
fprintf(fp, "\n%sFailing tests:\n", indent);
for (ImGuiTest* test : engine->TestsAll)
{
if (test->Group != group)
continue;
if (test->Output.Status == ImGuiTestStatus_Error)
fprintf(fp, "%s- %s\n", indent, test->Name);
}
fprintf(fp, "\n");
}
fprintf(fp, "%sTests Result: %s\n", indent, (count_success == count_tested) ? "OK" : "Errors");
fprintf(fp, "%s(%d/%d tests passed)\n", indent, count_success, count_tested);
}
static bool ImGuiTestEngine_HasAnyLogLines(ImGuiTestLog* test_log, ImGuiTestVerboseLevel level)
{
for (auto& line_info : test_log->LineInfo)
if (line_info.Level <= level)
return true;
return false;
}
static void ImGuiTestEngine_PrintLogLines(FILE* fp, ImGuiTestLog* test_log, int indent, ImGuiTestVerboseLevel level)
{
Str128 log_line;
for (auto& line_info : test_log->LineInfo)
{
if (line_info.Level > level)
continue;
const char* line_start = test_log->Buffer.c_str() + line_info.LineOffset;
const char* line_end = strstr(line_start, "\n"); // FIXME: Incorrect.
log_line.set(line_start, line_end);
ImStrXmlEscape(&log_line); // FIXME: Should not be here considering the function name.
// Some users may want to disable indenting?
fprintf(fp, "%*s%s\n", indent, "", log_line.c_str());
}
}
// Export using settings stored in ImGuiTestEngineIO
// This is called by ImGuiTestEngine_CrashHandler().
void ImGuiTestEngine_Export(ImGuiTestEngine* engine)
{
ImGuiTestEngineIO& io = engine->IO;
ImGuiTestEngine_ExportEx(engine, io.ExportResultsFormat, io.ExportResultsFilename);
}
// Export using custom settings.
void ImGuiTestEngine_ExportEx(ImGuiTestEngine* engine, ImGuiTestEngineExportFormat format, const char* filename)
{
if (format == ImGuiTestEngineExportFormat_None)
return;
IM_ASSERT(filename != NULL);
if (format == ImGuiTestEngineExportFormat_JUnitXml)
ImGuiTestEngine_ExportJUnitXml(engine, filename);
else
IM_ASSERT(0);
}
void ImGuiTestEngine_ExportJUnitXml(ImGuiTestEngine* engine, const char* output_file)
{
IM_ASSERT(engine != NULL);
IM_ASSERT(output_file != NULL);
FILE* fp = fopen(output_file, "w+b");
if (fp == NULL)
{
fprintf(stderr, "Writing '%s' failed.\n", output_file);
return;
}
// Per-testsuite test statistics.
struct
{
const char* Name = NULL;
int Tests = 0;
int Failures = 0;
int Disabled = 0;
} testsuites[ImGuiTestGroup_COUNT];
testsuites[ImGuiTestGroup_Tests].Name = "tests";
testsuites[ImGuiTestGroup_Perfs].Name = "perfs";
for (int n = 0; n < engine->TestsAll.Size; n++)
{
ImGuiTest* test = engine->TestsAll[n];
auto* stats = &testsuites[test->Group];
stats->Tests += 1;
if (test->Output.Status == ImGuiTestStatus_Error)
stats->Failures += 1;
else if (test->Output.Status == ImGuiTestStatus_Unknown)
stats->Disabled += 1;
}
// Attributes for <testsuites> tag.
const char* testsuites_name = "Dear ImGui";
int testsuites_failures = 0;
int testsuites_tests = 0;
int testsuites_disabled = 0;
float testsuites_time = (float)((double)(engine->BatchEndTime - engine->BatchStartTime) / 1000000.0);
for (int testsuite_id = ImGuiTestGroup_Tests; testsuite_id < ImGuiTestGroup_COUNT; testsuite_id++)
{
testsuites_tests += testsuites[testsuite_id].Tests;
testsuites_failures += testsuites[testsuite_id].Failures;
testsuites_disabled += testsuites[testsuite_id].Disabled;
}
// FIXME: "errors" attribute and <error> tag in <testcase> may be supported if we have means to catch unexpected errors like assertions.
fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<testsuites disabled=\"%d\" errors=\"0\" failures=\"%d\" name=\"%s\" tests=\"%d\" time=\"%.3f\">\n",
testsuites_disabled, testsuites_failures, testsuites_name, testsuites_tests, testsuites_time);
for (int testsuite_id = ImGuiTestGroup_Tests; testsuite_id < ImGuiTestGroup_COUNT; testsuite_id++)
{
// Attributes for <testsuite> tag.
auto* testsuite = &testsuites[testsuite_id];
float testsuite_time = testsuites_time; // FIXME: We do not differentiate between tests and perfs, they are executed in one big batch.
Str30 testsuite_timestamp = "";
ImTimestampToISO8601(engine->BatchStartTime, &testsuite_timestamp);
fprintf(fp, " <testsuite name=\"%s\" tests=\"%d\" disabled=\"%d\" errors=\"0\" failures=\"%d\" hostname=\"\" id=\"%d\" package=\"\" skipped=\"0\" time=\"%.3f\" timestamp=\"%s\">\n",
testsuite->Name, testsuite->Tests, testsuite->Disabled, testsuite->Failures, testsuite_id, testsuite_time, testsuite_timestamp.c_str());
for (int n = 0; n < engine->TestsAll.Size; n++)
{
ImGuiTest* test = engine->TestsAll[n];
if (test->Group != testsuite_id)
continue;
ImGuiTestOutput* test_output = &test->Output;
ImGuiTestLog* test_log = &test_output->Log;
// Attributes for <testcase> tag.
const char* testcase_name = test->Name;
const char* testcase_classname = test->Category;
const char* testcase_status = ImGuiTestEngine_GetStatusName(test_output->Status);
const float testcase_time = (float)((double)(test_output->EndTime - test_output->StartTime) / 1000000.0);
fprintf(fp, " <testcase name=\"%s\" assertions=\"0\" classname=\"%s\" status=\"%s\" time=\"%.3f\">\n",
testcase_name, testcase_classname, testcase_status, testcase_time);
if (test_output->Status == ImGuiTestStatus_Error)
{
// Skip last error message because it is generic information that test failed.
Str128 log_line;
for (int i = test_log->LineInfo.Size - 2; i >= 0; i--)
{
ImGuiTestLogLineInfo* line_info = &test_log->LineInfo[i];
if (line_info->Level > engine->IO.ConfigVerboseLevelOnError)
continue;
if (line_info->Level == ImGuiTestVerboseLevel_Error)
{
const char* line_start = test_log->Buffer.c_str() + line_info->LineOffset;
const char* line_end = strstr(line_start, "\n");
log_line.set(line_start, line_end);
ImStrXmlEscape(&log_line);
break;
}
}
// Failing tests save their "on error" log output in text element of <failure> tag.
fprintf(fp, " <failure message=\"%s\" type=\"error\">\n", log_line.c_str());
ImGuiTestEngine_PrintLogLines(fp, test_log, 8, engine->IO.ConfigVerboseLevelOnError);
fprintf(fp, " </failure>\n");
}
if (test_output->Status == ImGuiTestStatus_Unknown)
{
fprintf(fp, " <skipped message=\"Skipped\" />\n");
}
else
{
// Succeeding tests save their default log output output as "stdout".
if (ImGuiTestEngine_HasAnyLogLines(test_log, engine->IO.ConfigVerboseLevel))
{
fprintf(fp, " <system-out>\n");
ImGuiTestEngine_PrintLogLines(fp, test_log, 8, engine->IO.ConfigVerboseLevel);
fprintf(fp, " </system-out>\n");
}
// Save error messages as "stderr".
if (ImGuiTestEngine_HasAnyLogLines(test_log, ImGuiTestVerboseLevel_Error))
{
fprintf(fp, " <system-err>\n");
ImGuiTestEngine_PrintLogLines(fp, test_log, 8, ImGuiTestVerboseLevel_Error);
fprintf(fp, " </system-err>\n");
}
}
fprintf(fp, " </testcase>\n");
}
if (testsuites[testsuite_id].Disabled < testsuites[testsuite_id].Tests) // Any tests executed
{
// Log all log messages as "stdout".
fprintf(fp, " <system-out>\n");
for (int n = 0; n < engine->TestsAll.Size; n++)
{
ImGuiTest* test = engine->TestsAll[n];
ImGuiTestOutput* test_output = &test->Output;
if (test->Group != testsuite_id)
continue;
if (test_output->Status == ImGuiTestStatus_Unknown)
continue;
fprintf(fp, " [0000] Test: '%s' '%s'..\n", test->Category, test->Name);
ImGuiTestVerboseLevel level = test_output->Status == ImGuiTestStatus_Error ? engine->IO.ConfigVerboseLevelOnError : engine->IO.ConfigVerboseLevel;
ImGuiTestEngine_PrintLogLines(fp, &test_output->Log, 6, level);
}
ImGuiTestEngine_ExportResultSummary(engine, fp, 6, (ImGuiTestGroup)testsuite_id);
fprintf(fp, " </system-out>\n");
// Log all warning and error messages as "stderr".
fprintf(fp, " <system-err>\n");
for (int n = 0; n < engine->TestsAll.Size; n++)
{
ImGuiTest* test = engine->TestsAll[n];
ImGuiTestOutput* test_output = &test->Output;
if (test->Group != testsuite_id)
continue;
if (test_output->Status == ImGuiTestStatus_Unknown)
continue;
fprintf(fp, " [0000] Test: '%s' '%s'..\n", test->Category, test->Name);
ImGuiTestEngine_PrintLogLines(fp, &test_output->Log, 6, ImGuiTestVerboseLevel_Warning);
}
ImGuiTestEngine_ExportResultSummary(engine, fp, 6, (ImGuiTestGroup)testsuite_id);
fprintf(fp, " </system-err>\n");
}
fprintf(fp, " </testsuite>\n");
}
fprintf(fp, "</testsuites>\n");
fclose(fp);
fprintf(stdout, "Saved test results to '%s' successfully.\n", output_file);
}

View File

@ -0,0 +1,59 @@
// dear imgui test engine
// (result exporters)
// Read https://github.com/ocornut/imgui_test_engine/wiki/Exporting-Results
#pragma once
//-------------------------------------------------------------------------
// Description
//-------------------------------------------------------------------------
//
// Test results may be exported in one of supported formats.
// To enable result exporting please configure test engine as follows:
//
// ImGuiTestEngineIO& test_io = ImGuiTestEngine_GetIO(engine);
// test_io.ExportResultsFile = "output_file.xml";
// test_io.ExportResultsFormat = ImGuiTestEngineExportFormat_<...>;
//
// JUnit XML format
//------------------
// JUnit XML format described at https://llg.cubic.org/docs/junit/. Many
// third party applications support consumption of this format. Some of
// of them are listed here:
// - Jenkins
// - Installation guide: https://www.jenkins.io/doc/book/installing/docker/
// - JUnit plugin: https://plugins.jenkins.io/junit/
// - xunit-viewer
// - Project: https://github.com/lukejpreston/xunit-viewer
// - Install npm: https://docs.npmjs.com/downloading-and-installing-node-js-and-npm
// - Install viewer and view test results:
// npm install xunit-viewer
// imgui_test_suite -nopause -v2 -ve4 -nogui -export-file junit.xml tests
// node_modules/xunit-viewer/bin/xunit-viewer -r junit.xml -o junit.html
// - Open junit.html
//
//-------------------------------------------------------------------------
// Forward Declarations
//-------------------------------------------------------------------------
struct ImGuiTestEngine;
//-------------------------------------------------------------------------
// Types
//-------------------------------------------------------------------------
enum ImGuiTestEngineExportFormat : int
{
ImGuiTestEngineExportFormat_None = 0,
ImGuiTestEngineExportFormat_JUnitXml,
};
//-------------------------------------------------------------------------
// Functions
//-------------------------------------------------------------------------
void ImGuiTestEngine_PrintResultSummary(ImGuiTestEngine* engine);
void ImGuiTestEngine_Export(ImGuiTestEngine* engine);
void ImGuiTestEngine_ExportEx(ImGuiTestEngine* engine, ImGuiTestEngineExportFormat format, const char* filename);

View File

@ -0,0 +1,57 @@
// dear imgui test engine
// (template for compile-time configuration)
// Replicate or #include this file in your imconfig.h to enable test engine.
// Compile Dear ImGui with test engine hooks
// (Important: This is a value-less define, to be consistent with other defines used in core dear imgui.)
#define IMGUI_ENABLE_TEST_ENGINE
// [Optional, default 0] Enable plotting of perflog data for comparing performance of different runs.
// This feature requires ImPlot to be linked in the application.
#ifndef IMGUI_TEST_ENGINE_ENABLE_IMPLOT
#define IMGUI_TEST_ENGINE_ENABLE_IMPLOT 0
#endif
// [Optional, default 1] Enable screen capture and PNG/GIF saving functionalities
// There's not much point to disable this but we provide it to reassure user that the dependencies on imstb_image_write.h and ffmpeg are technically optional.
#ifndef IMGUI_TEST_ENGINE_ENABLE_CAPTURE
#define IMGUI_TEST_ENGINE_ENABLE_CAPTURE 1
#endif
// [Optional, default 0] Using std::function and <functional> for function pointers such as ImGuiTest::TestFunc and ImGuiTest::GuiFunc
#ifndef IMGUI_TEST_ENGINE_ENABLE_STD_FUNCTION
#define IMGUI_TEST_ENGINE_ENABLE_STD_FUNCTION 0
#endif
// [Optional, default 0] Automatically fill ImGuiTestEngineIO::CoroutineFuncs with a default implementation using std::thread
#ifndef IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL
#define IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL 0
#endif
// Define IM_DEBUG_BREAK macros so it is accessible in imgui.h
// (this is a conveniance for app using test engine may define an IM_ASSERT() that uses this instead of an actual assert)
// (this is a copy of the block in imgui_internal.h. if the one in imgui_internal.h were to be defined at the top of imgui.h we wouldn't need this)
#ifndef IM_DEBUG_BREAK
#if defined (_MSC_VER)
#define IM_DEBUG_BREAK() __debugbreak()
#elif defined(__clang__)
#define IM_DEBUG_BREAK() __builtin_debugtrap()
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
#define IM_DEBUG_BREAK() __asm__ volatile("int $0x03")
#elif defined(__GNUC__) && defined(__thumb__)
#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xde01")
#elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__)
#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xe7f001f0");
#else
#define IM_DEBUG_BREAK() IM_ASSERT(0) // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger!
#endif
#endif // #ifndef IMGUI_DEBUG_BREAK
// [Options] We provide custom assert macro used by our our test suite, which you may use:
// - Calling IM_DEBUG_BREAK() instead of an actual assert, so we can easily recover and step over (compared to many assert implementations).
// - If a test is running, test name will be included in the log.
// - Macro is calling IM_DEBUG_BREAK() inline to get debugger to break in the calling function (instead of a deeper callstack level).
// - Macro is using comma operator instead of an if() to avoid "conditional expression is constant" warnings.
extern void ImGuiTestEngine_AssertLog(const char* expr, const char* file, const char* func, int line);
#define IM_TEST_ENGINE_ASSERT(_EXPR) do { if ((void)0, !(_EXPR)) { ImGuiTestEngine_AssertLog(#_EXPR, __FILE__, __func__, __LINE__); IM_DEBUG_BREAK(); } } while (0)
// V_ASSERT_CONTRACT, assertMacro:IM_ASSERT

View File

@ -0,0 +1,213 @@
// dear imgui test engine
// (internal api)
#pragma once
#include "imgui_te_coroutine.h"
#include "imgui_te_utils.h" // ImMovingAverage
#include "imgui_capture_tool.h" // ImGuiCaptureTool // FIXME
//-------------------------------------------------------------------------
// FORWARD DECLARATIONS
//-------------------------------------------------------------------------
class Str; // Str<> from thirdparty/Str/Str.h
struct ImGuiPerfTool;
//-------------------------------------------------------------------------
// DATA STRUCTURES
//-------------------------------------------------------------------------
// Query item position/window/state given ID.
struct ImGuiTestInfoTask
{
// Input
ImGuiID ID = 0;
int FrameCount = -1; // Timestamp of request
char DebugName[64] = ""; // Debug string representing the queried ID
// Output
ImGuiTestItemInfo Result;
};
// Gather item list in given parent ID.
struct ImGuiTestGatherTask
{
// Input
ImGuiID InParentID = 0;
int InMaxDepth = 0;
short InLayerMask = 0;
// Output/Temp
ImGuiTestItemList* OutList = NULL;
ImGuiTestItemInfo* LastItemInfo = NULL;
void Clear() { memset(this, 0, sizeof(*this)); }
};
// Find item ID given a label and a parent id
// Usually used by queries with wildcards such as ItemInfo("hello/**/foo/bar")
struct ImGuiTestFindByLabelTask
{
// Input
ImGuiID InPrefixId = 0; // A known base ID which appears BEFORE the wildcard ID (for "hello/**/foo/bar" it would be hash of "hello")
int InSuffixDepth = 0; // Number of labels in a path, after unknown base ID (for "hello/**/foo/bar" it would be 2)
const char* InSuffix = NULL; // A label string which appears on ID stack after unknown base ID (for "hello/**/foo/bar" it would be "foo/bar")
const char* InSuffixLastItem = NULL; // A last label string (for "hello/**/foo/bar" it would be "bar")
ImGuiID InSuffixLastItemHash = 0;
ImGuiItemStatusFlags InFilterItemStatusFlags = 0; // Flags required for item to be returned
// Output
ImGuiID OutItemId = 0; // Result item ID
};
enum ImGuiTestInputType
{
ImGuiTestInputType_None,
ImGuiTestInputType_Key,
ImGuiTestInputType_Char,
ImGuiTestInputType_ViewportFocus,
ImGuiTestInputType_ViewportClose
};
// FIXME: May want to strip further now that core imgui is using its own input queue
struct ImGuiTestInput
{
ImGuiTestInputType Type = ImGuiTestInputType_None;
ImGuiKeyChord KeyChord = ImGuiKey_None;
ImWchar Char = 0;
bool Down = false;
ImGuiID ViewportId = 0;
static ImGuiTestInput ForKeyChord(ImGuiKeyChord key_chord, bool down)
{
ImGuiTestInput inp;
inp.Type = ImGuiTestInputType_Key;
inp.KeyChord = key_chord;
inp.Down = down;
return inp;
}
static ImGuiTestInput ForChar(ImWchar v)
{
ImGuiTestInput inp;
inp.Type = ImGuiTestInputType_Char;
inp.Char = v;
return inp;
}
static ImGuiTestInput ForViewportFocus(ImGuiID viewport_id)
{
ImGuiTestInput inp;
inp.Type = ImGuiTestInputType_ViewportFocus;
inp.ViewportId = viewport_id;
return inp;
}
static ImGuiTestInput ForViewportClose(ImGuiID viewport_id)
{
ImGuiTestInput inp;
inp.Type = ImGuiTestInputType_ViewportClose;
inp.ViewportId = viewport_id;
return inp;
}
};
struct ImGuiTestInputs
{
ImVec2 MousePosValue; // Own non-rounded copy of MousePos in order facilitate simulating mouse movement very slow speed and high-framerate
ImVec2 MouseWheel;
ImGuiID MouseHoveredViewport = 0;
int MouseButtonsValue = 0x00; // FIXME-TESTS: Use simulated_io.MouseDown[] ?
ImVector<ImGuiTestInput> Queue;
bool HostEscDown = false;
float HostEscDownDuration = -1.0f; // Maintain our own DownDuration for host/backend ESC key so we can abort.
};
// [Internal] Test Engine Context
struct ImGuiTestEngine
{
ImGuiTestEngineIO IO;
ImGuiContext* UiContextTarget = NULL; // imgui context for testing
ImGuiContext* UiContextActive = NULL; // imgui context for testing == UiContextTarget or NULL
bool Started = false;
ImU64 BatchStartTime = 0;
ImU64 BatchEndTime = 0;
int FrameCount = 0;
float OverrideDeltaTime = -1.0f; // Inject custom delta time into imgui context to simulate clock passing faster than wall clock time.
ImVector<ImGuiTest*> TestsAll;
ImVector<ImGuiTestRunTask> TestsQueue;
ImGuiTestContext* TestContext = NULL;
ImVector<ImGuiTestInfoTask*>InfoTasks;
ImGuiTestGatherTask GatherTask;
ImGuiTestFindByLabelTask FindByLabelTask;
ImGuiTestCoroutineHandle TestQueueCoroutine = NULL; // Coroutine to run the test queue
bool TestQueueCoroutineShouldExit = false; // Flag to indicate that we are shutting down and the test queue coroutine should stop
// Inputs
ImGuiTestInputs Inputs;
// UI support
bool Abort = false;
ImGuiTest* UiSelectAndScrollToTest = NULL;
ImGuiTest* UiSelectedTest = NULL;
Str* UiFilterTests;
Str* UiFilterPerfs;
ImU32 UiFilterByStatusMask = ~0u;
bool UiMetricsOpen = false;
bool UiDebugLogOpen = false;
bool UiCaptureToolOpen = false;
bool UiStackToolOpen = false;
bool UiPerfToolOpen = false;
float UiLogHeight = 150.0f;
// Performance Monitor
double PerfRefDeltaTime;
ImMovingAverage<double> PerfDeltaTime100;
ImMovingAverage<double> PerfDeltaTime500;
ImGuiPerfTool* PerfTool = NULL;
// Screen/Video Capturing
ImGuiCaptureToolUI CaptureTool; // Capture tool UI
ImGuiCaptureContext CaptureContext; // Capture context used in tests
ImGuiCaptureArgs* CaptureCurrentArgs = NULL;
// Tools
bool PostSwapCalled = false;
bool ToolDebugRebootUiContext = false; // Completely shutdown and recreate the dear imgui context in place
bool ToolSlowDown = false;
int ToolSlowDownMs = 100;
ImGuiTestRunSpeed BackupConfigRunSpeed = ImGuiTestRunSpeed_Fast;
bool BackupConfigNoThrottle = false;
// Functions
ImGuiTestEngine();
~ImGuiTestEngine();
};
//-------------------------------------------------------------------------
// INTERNAL FUNCTIONS
//-------------------------------------------------------------------------
ImGuiTestItemInfo* ImGuiTestEngine_FindItemInfo(ImGuiTestEngine* engine, ImGuiID id, const char* debug_id);
void ImGuiTestEngine_Yield(ImGuiTestEngine* engine);
void ImGuiTestEngine_SetDeltaTime(ImGuiTestEngine* engine, float delta_time);
int ImGuiTestEngine_GetFrameCount(ImGuiTestEngine* engine);
bool ImGuiTestEngine_PassFilter(ImGuiTest* test, const char* filter);
void ImGuiTestEngine_RunTest(ImGuiTestEngine* engine, ImGuiTestContext* ctx, ImGuiTest* test, ImGuiTestRunFlags run_flags);
void ImGuiTestEngine_RebootUiContext(ImGuiTestEngine* engine);
ImGuiPerfTool* ImGuiTestEngine_GetPerfTool(ImGuiTestEngine* engine);
// Screen/Video Capturing
bool ImGuiTestEngine_CaptureScreenshot(ImGuiTestEngine* engine, ImGuiCaptureArgs* args);
bool ImGuiTestEngine_CaptureBeginVideo(ImGuiTestEngine* engine, ImGuiCaptureArgs* args);
bool ImGuiTestEngine_CaptureEndVideo(ImGuiTestEngine* engine, ImGuiCaptureArgs* args);
// Helper functions
const char* ImGuiTestEngine_GetStatusName(ImGuiTestStatus v);
const char* ImGuiTestEngine_GetRunSpeedName(ImGuiTestRunSpeed v);
const char* ImGuiTestEngine_GetVerboseLevelName(ImGuiTestVerboseLevel v);
//-------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,131 @@
// dear imgui test engine
// (performance tool)
// Browse and visualize samples recorded by ctx->PerfCapture() calls.
// User access via 'Test Engine UI -> Tools -> Perf Tool'
#pragma once
#include "imgui.h"
// Forward Declaration
struct ImGuiPerfToolColumnInfo;
struct ImGuiTestEngine;
struct ImGuiCsvParser;
// Configuration
#define IMGUI_PERFLOG_DEFAULT_FILENAME "output/imgui_perflog.csv"
// [Internal] Perf log entry. Changes to this struct should be reflected in ImGuiTestContext::PerfCapture() and ImGuiTestEngine_Start().
// This struct assumes strings stored here will be available until next ImGuiPerfTool::Clear() call. Fortunately we do not have to actively
// manage lifetime of these strings. New entries are created only in two cases:
// 1. ImGuiTestEngine_PerfToolAppendToCSV() call after perf test has run. This call receives ImGuiPerfToolEntry with const strings stored indefinitely by application.
// 2. As a consequence of ImGuiPerfTool::LoadCSV() call, we persist the ImGuiCSVParser instance, which keeps parsed CSV text, from which strings are referenced.
// As a result our solution also doesn't make many allocations.
struct IMGUI_API ImGuiPerfToolEntry
{
ImU64 Timestamp = 0; // Title of a particular batch of perftool entries.
const char* Category = NULL; // Name of category perf test is in.
const char* TestName = NULL; // Name of perf test.
double DtDeltaMs = 0.0; // Result of perf test.
double DtDeltaMsMin = +FLT_MAX; // May be used by perftool.
double DtDeltaMsMax = -FLT_MAX; // May be used by perftool.
int NumSamples = 1; // Number aggregated samples.
int PerfStressAmount = 0; //
const char* GitBranchName = NULL; // Build information.
const char* BuildType = NULL; //
const char* Cpu = NULL; //
const char* OS = NULL; //
const char* Compiler = NULL; //
const char* Date = NULL; // Date of this entry or min date of combined entries.
//const char* DateMax = NULL; // Max date of combined entries, or NULL.
double VsBaseline = 0.0; // Percent difference vs baseline.
int LabelIndex = 0; // Index of TestName in ImGuiPerfTool::_LabelsVisible.
ImGuiPerfToolEntry() { }
ImGuiPerfToolEntry(const ImGuiPerfToolEntry& rhs) { Set(rhs); }
ImGuiPerfToolEntry& operator=(const ImGuiPerfToolEntry& rhs){ Set(rhs); return *this; }
void Set(const ImGuiPerfToolEntry& rhs);
};
// [Internal] Perf log batch.
struct ImGuiPerfToolBatch
{
ImU64 BatchID = 0; // Timestamp of the batch, or unique ID of the build in combined mode.
int NumSamples = 0; // A number of unique batches aggregated.
int BranchIndex = 0; // For per-branch color mapping.
ImVector<ImGuiPerfToolEntry> Entries; // Aggregated perf test entries. Order follows ImGuiPerfTool::_LabelsVisible order.
~ImGuiPerfToolBatch() { Entries.clear_destruct(); } // FIXME: Misleading: nothing to destruct in that struct?
};
enum ImGuiPerfToolDisplayType : int
{
ImGuiPerfToolDisplayType_Simple, // Each run will be displayed individually.
ImGuiPerfToolDisplayType_PerBranchColors, // Use one bar color per branch.
ImGuiPerfToolDisplayType_CombineByBuildInfo, // Entries with same build information will be averaged.
};
//
struct IMGUI_API ImGuiPerfTool
{
ImVector<ImGuiPerfToolEntry> _SrcData; // Raw entries from CSV file (with string pointer into CSV data).
ImVector<const char*> _Labels;
ImVector<const char*> _LabelsVisible; // ImPlot requires a pointer of all labels beforehand. Always contains a dummy "" entry at the end!
ImVector<ImGuiPerfToolBatch> _Batches;
ImGuiStorage _LabelBarCounts; // Number bars each label will render.
int _NumVisibleBuilds = 0; // Cached number of visible builds.
int _NumUniqueBuilds = 0; // Cached number of unique builds.
ImGuiPerfToolDisplayType _DisplayType = ImGuiPerfToolDisplayType_CombineByBuildInfo;
int _BaselineBatchIndex = 0; // Index of baseline build.
ImU64 _BaselineTimestamp = 0;
ImU64 _BaselineBuildId = 0;
char _Filter[128]; // Context menu filtering substring.
char _FilterDateFrom[11] = {};
char _FilterDateTo[11] = {};
float _InfoTableHeight = 180.0f;
int _AlignStress = 0; // Alignment values for build info components, so they look aligned in the legend.
int _AlignType = 0;
int _AlignOs = 0;
int _AlignCpu = 0;
int _AlignCompiler = 0;
int _AlignBranch = 0;
int _AlignSamples = 0;
bool _InfoTableSortDirty = false;
ImVector<ImU64> _InfoTableSort; // _InfoTableSort[_LabelsVisible.Size * _Batches.Size]. Contains sorted batch indices for each label.
const ImGuiTableSortSpecs* _InfoTableSortSpecs = NULL; // Current table sort specs.
ImGuiStorage _TempSet; // Used as a set
int _TableHoveredTest = -1; // Index within _VisibleLabelPointers array.
int _TableHoveredBatch = -1;
int _PlotHoverTest = -1;
int _PlotHoverBatch = -1;
bool _PlotHoverTestLabel = false;
bool _ReportGenerating = false;
ImGuiStorage _Visibility;
ImGuiCsvParser* _CsvParser = NULL; // We keep this around and point to its fields
ImGuiPerfTool();
~ImGuiPerfTool();
void Clear();
bool LoadCSV(const char* filename = NULL);
void AddEntry(ImGuiPerfToolEntry* entry);
void ShowPerfToolWindow(ImGuiTestEngine* engine, bool* p_open);
void ViewOnly(const char* perf_name);
void ViewOnly(const char** perf_names);
ImGuiPerfToolEntry* GetEntryByBatchIdx(int idx, const char* perf_name = NULL);
bool SaveHtmlReport(const char* file_name, const char* image_file = NULL);
inline bool Empty() { return _SrcData.empty(); }
void _Rebuild();
bool _IsVisibleBuild(ImGuiPerfToolBatch* batch);
bool _IsVisibleBuild(ImGuiPerfToolEntry* batch);
bool _IsVisibleTest(const char* test_name);
void _CalculateLegendAlignment();
void _ShowEntriesPlot();
void _ShowEntriesTable();
void _SetBaseline(int batch_index);
void _AddSettingsHandler();
void _UnpackSortedKey(ImU64 key, int* batch_index, int* entry_index, int* monotonic_index = NULL);
};
IMGUI_API void ImGuiTestEngine_PerfToolAppendToCSV(ImGuiPerfTool* perf_log, ImGuiPerfToolEntry* entry, const char* filename = NULL);

View File

@ -0,0 +1,842 @@
// dear imgui test engine
// (ui)
// If you run tests in an interactive or visible application, you may want to call ImGuiTestEngine_ShowTestEngineWindows()
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#define IMGUI_DEFINE_MATH_OPERATORS
#include "imgui_te_ui.h"
#include "imgui.h"
#include "imgui_internal.h"
#include "imgui_te_engine.h"
#include "imgui_te_context.h"
#include "imgui_te_internal.h"
#include "imgui_te_perftool.h"
#include "thirdparty/Str/Str.h"
//-------------------------------------------------------------------------
// TEST ENGINE: USER INTERFACE
//-------------------------------------------------------------------------
// - DrawTestLog() [internal]
// - GetVerboseLevelName() [internal]
// - ShowTestGroup() [internal]
// - ImGuiTestEngine_ShowTestWindows()
//-------------------------------------------------------------------------
// Look for " filename:number " in the string and add menu option to open source.
static bool ParseLineAndDrawFileOpenItemForSourceFile(ImGuiTestEngine* e, ImGuiTest* test, const char* line_start, const char* line_end)
{
const char* separator = ImStrchrRange(line_start, line_end, ':');
if (separator == NULL)
return false;
const char* path_end = separator;
const char* path_begin = separator - 1;
while (path_begin > line_start&& path_begin[-1] != ' ')
path_begin--;
if (path_begin == path_end)
return false;
int line_no = -1;
sscanf(separator + 1, "%d ", &line_no);
if (line_no == -1)
return false;
Str256f buf("Open '%.*s' at line %d", (int)(path_end - path_begin), path_begin, line_no);
if (ImGui::MenuItem(buf.c_str()))
{
// FIXME-TESTS: Assume folder is same as folder of test->SourceFile!
const char* src_path = test->SourceFile;
const char* src_name = ImPathFindFilename(src_path);
buf.setf("%.*s%.*s", (int)(src_name - src_path), src_path, (int)(path_end - path_begin), path_begin);
ImGuiTestEngineIO& e_io = ImGuiTestEngine_GetIO(e);
e_io.SrcFileOpenFunc(buf.c_str(), line_no, e_io.SrcFileOpenUserData);
}
return true;
}
// Look for "[ ,"]filename.png" in the string and add menu option to open image.
static bool ParseLineAndDrawFileOpenItemForImageFile(ImGuiTestEngine* e, ImGuiTest* test, const char* line_start, const char* line_end, const char* file_ext)
{
IM_UNUSED(e);
IM_UNUSED(test);
const char* extension = ImStristr(line_start, line_end, file_ext, NULL);
if (extension == NULL)
return false;
const char* path_end = extension + strlen(file_ext);
const char* path_begin = extension - 1;
while (path_begin > line_start && path_begin[-1] != ' ' && path_begin[-1] != '\'' && path_begin[-1] != '\"')
path_begin--;
if (path_begin == path_end)
return false;
Str256 buf;
// Open file
buf.setf("Open file: %.*s", (int)(path_end - path_begin), path_begin);
if (ImGui::MenuItem(buf.c_str()))
{
buf.setf("%.*s", (int)(path_end - path_begin), path_begin);
ImPathFixSeparatorsForCurrentOS(buf.c_str());
ImOsOpenInShell(buf.c_str());
}
// Open folder
const char* folder_begin = path_begin;
const char* folder_end = ImPathFindFilename(path_begin, path_end);
buf.setf("Open folder: %.*s", (int)(folder_end - folder_begin), path_begin);
if (ImGui::MenuItem(buf.c_str()))
{
buf.setf("%.*s", (int)(folder_end - folder_begin), folder_begin);
ImPathFixSeparatorsForCurrentOS(buf.c_str());
ImOsOpenInShell(buf.c_str());
}
return true;
}
static bool ParseLineAndDrawFileOpenItem(ImGuiTestEngine* e, ImGuiTest* test, const char* line_start, const char* line_end)
{
if (ParseLineAndDrawFileOpenItemForSourceFile(e, test, line_start, line_end))
return true;
if (ParseLineAndDrawFileOpenItemForImageFile(e, test, line_start, line_end, ".png"))
return true;
if (ParseLineAndDrawFileOpenItemForImageFile(e, test, line_start, line_end, ".gif"))
return true;
if (ParseLineAndDrawFileOpenItemForImageFile(e, test, line_start, line_end, ".mp4"))
return true;
return false;
}
static float GetDpiScale()
{
#ifdef IMGUI_HAS_VIEWPORT
return ImGui::GetWindowViewport()->DpiScale;
#else
return 1.0f;
#endif
}
static void DrawTestLog(ImGuiTestEngine* e, ImGuiTest* test)
{
const ImU32 error_col = IM_COL32(255, 150, 150, 255);
const ImU32 warning_col = IM_COL32(240, 240, 150, 255);
const ImU32 unimportant_col = IM_COL32(190, 190, 190, 255);
const float dpi_scale = GetDpiScale();
ImGuiTestOutput* test_output = &test->Output;
ImGuiTestLog* log = &test_output->Log;
const char* text = log->Buffer.begin();
const char* text_end = log->Buffer.end();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 2.0f) * dpi_scale);
ImGuiListClipper clipper;
ImGuiTestVerboseLevel max_log_level = test_output->Status == ImGuiTestStatus_Error ? e->IO.ConfigVerboseLevelOnError : e->IO.ConfigVerboseLevel;
int line_count = log->ExtractLinesForVerboseLevels(ImGuiTestVerboseLevel_Silent, max_log_level, NULL);
int current_index_clipped = -1;
int current_index_abs = 0;
clipper.Begin(line_count);
while (clipper.Step())
{
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
{
// Advance index_by_log_level to find log entry indicated by line_no.
ImGuiTestLogLineInfo* line_info = NULL;
while (current_index_clipped < line_no)
{
line_info = &log->LineInfo[current_index_abs];
if (line_info->Level <= max_log_level)
current_index_clipped++;
current_index_abs++;
}
const char* line_start = text + line_info->LineOffset;
const char* line_end = strchr(line_start, '\n');
if (line_end == NULL)
line_end = text_end;
switch (line_info->Level)
{
case ImGuiTestVerboseLevel_Error:
ImGui::PushStyleColor(ImGuiCol_Text, error_col);
break;
case ImGuiTestVerboseLevel_Warning:
ImGui::PushStyleColor(ImGuiCol_Text, warning_col);
break;
case ImGuiTestVerboseLevel_Debug:
case ImGuiTestVerboseLevel_Trace:
ImGui::PushStyleColor(ImGuiCol_Text, unimportant_col);
break;
default:
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32_WHITE);
break;
}
ImGui::TextUnformatted(line_start, line_end);
ImGui::PopStyleColor();
ImGui::PushID(line_no);
if (ImGui::BeginPopupContextItem("Context", 1))
{
if (!ParseLineAndDrawFileOpenItem(e, test, line_start, line_end))
ImGui::MenuItem("No options", NULL, false, false);
ImGui::EndPopup();
}
ImGui::PopID();
}
}
ImGui::PopStyleVar();
}
#if IMGUI_VERSION_NUM <= 18963
namespace ImGui
{
void SetItemTooltip(const char* fmt, ...)
{
if (ImGui::IsItemHovered())
{
va_list args;
va_start(args, fmt);
ImGui::SetTooltipV(fmt, args);
va_end(args);
}
}
} // namespace ImGui
#endif
static bool ShowTestGroupFilterTest(ImGuiTestEngine* e, ImGuiTestGroup group, const char* filter, ImGuiTest* test)
{
if (test->Group != group)
return false;
if (!ImGuiTestEngine_PassFilter(test, *filter ? filter : "all"))
return false;
if ((e->UiFilterByStatusMask & (1 << test->Output.Status)) == 0)
return false;
return true;
}
static void GetFailingTestsAsString(ImGuiTestEngine* e, ImGuiTestGroup group, char separator, Str* out_string)
{
IM_ASSERT(out_string != NULL);
bool first = true;
for (int i = 0; i < e->TestsAll.Size; i++)
{
ImGuiTest* failing_test = e->TestsAll[i];
Str* filter = (group == ImGuiTestGroup_Tests) ? e->UiFilterTests : e->UiFilterPerfs;
if (failing_test->Group != group)
continue;
if (failing_test->Output.Status != ImGuiTestStatus_Error)
continue;
if (!ImGuiTestEngine_PassFilter(failing_test, filter->empty() ? "all" : filter->c_str()))
continue;
if (!first)
out_string->append(separator);
out_string->append(failing_test->Name);
first = false;
}
}
static void TestStatusButton(const char* id, const ImVec4& color, bool running, int display_counter)
{
ImGuiContext& g = *GImGui;
ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true);
ImGui::ColorButton(id, color, ImGuiColorEditFlags_NoTooltip);
ImGui::PopItemFlag();
if (running)
{
//ImRect r = g.LastItemData.Rect;
ImVec2 center = g.LastItemData.Rect.GetCenter();
float radius = ImFloor(ImMin(g.LastItemData.Rect.GetWidth(), g.LastItemData.Rect.GetHeight()) * 0.40f);
float t = (float)(ImGui::GetTime() * 20.0f);
ImVec2 off(ImCos(t) * radius, ImSin(t) * radius);
ImGui::GetWindowDrawList()->AddLine(center - off, center + off, ImGui::GetColorU32(ImGuiCol_Text), 1.5f);
//ImGui::RenderText(r.Min + style.FramePadding + ImVec2(0, 0), &"|\0/\0-\0\\"[(((ImGui::GetFrameCount() / 5) & 3) << 1)], NULL);
}
else if (display_counter >= 0)
{
ImVec2 center = g.LastItemData.Rect.GetCenter();
Str30f buf("%d", display_counter);
ImGui::GetWindowDrawList()->AddText(center - ImGui::CalcTextSize(buf.c_str()) * 0.5f, ImGui::GetColorU32(ImGuiCol_Text), buf.c_str());
}
}
static void ShowTestGroup(ImGuiTestEngine* e, ImGuiTestGroup group, Str* filter)
{
ImGuiStyle& style = ImGui::GetStyle();
ImGuiIO& io = ImGui::GetIO();
const float dpi_scale = GetDpiScale();
// Colored Status button: will be displayed later below
// - Save position of test run status button and make space for it.
const ImVec2 status_button_pos = ImGui::GetCursorPos();
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetFrameHeight() + style.ItemInnerSpacing.x);
//ImGui::Text("TESTS (%d)", engine->TestsAll.Size);
#if IMGUI_VERSION_NUM >= 18837
bool run = ImGui::Button("Run") || ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_R);
#else
bool = ImGui::Button("Run");
#endif
#if IMGUI_VERSION_NUM > 18963
ImGui::SetItemTooltip("Ctrl+R");
#endif
if (run)
{
for (int n = 0; n < e->TestsAll.Size; n++)
{
ImGuiTest* test = e->TestsAll[n];
if (!ShowTestGroupFilterTest(e, group, filter->c_str(), test))
continue;
ImGuiTestEngine_QueueTest(e, test, ImGuiTestRunFlags_None);
}
}
ImGui::SameLine();
{
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 6.0f);
const char* filter_by_status_desc = "";
if (e->UiFilterByStatusMask == ~0u)
filter_by_status_desc = "All";
else if (e->UiFilterByStatusMask == ~(1u << ImGuiTestStatus_Success))
filter_by_status_desc = "Not OK";
else if (e->UiFilterByStatusMask == (1u << ImGuiTestStatus_Error))
filter_by_status_desc = "Errors";
if (ImGui::BeginCombo("##filterbystatus", filter_by_status_desc))
{
if (ImGui::Selectable("All", e->UiFilterByStatusMask == ~0u))
e->UiFilterByStatusMask = (ImU32)~0u;
if (ImGui::Selectable("Not OK", e->UiFilterByStatusMask == ~(1u << ImGuiTestStatus_Success)))
e->UiFilterByStatusMask = (ImU32)~(1u << ImGuiTestStatus_Success);
if (ImGui::Selectable("Errors", e->UiFilterByStatusMask == (1u << ImGuiTestStatus_Error)))
e->UiFilterByStatusMask = (ImU32)(1u << ImGuiTestStatus_Error);
ImGui::EndCombo();
}
}
ImGui::SameLine();
const char* perflog_label = "Perf Tool";
float filter_width = ImGui::GetWindowContentRegionMax().x - ImGui::GetCursorPos().x;
float perf_stress_factor_width = (30 * dpi_scale);
if (group == ImGuiTestGroup_Perfs)
{
filter_width -= style.ItemSpacing.x + perf_stress_factor_width;
filter_width -= style.ItemSpacing.x + style.FramePadding.x * 2 + ImGui::CalcTextSize(perflog_label).x;
}
filter_width -= ImGui::CalcTextSize("(?)").x + style.ItemSpacing.x;
ImGui::SetNextItemWidth(ImMax(20.0f, filter_width));
ImGui::InputText("##filter", filter);
ImGui::SameLine();
ImGui::TextDisabled("(?)");
ImGui::SetItemTooltip("Query is composed of one or more comma-separated filter terms with optional modifiers.\n"
"Available modifiers:\n"
"- '-' prefix excludes tests matched by the term.\n"
"- '^' prefix anchors term matching to the start of the string.\n"
"- '$' suffix anchors term matching to the end of the string.");
if (group == ImGuiTestGroup_Perfs)
{
ImGui::SameLine();
ImGui::SetNextItemWidth(perf_stress_factor_width);
ImGui::DragInt("##PerfStress", &e->IO.PerfStressAmount, 0.1f, 1, 20, "x%d");
ImGui::SetItemTooltip("Increase workload of performance tests (higher means longer run)."); // FIXME: Move?
ImGui::SameLine();
if (ImGui::Button(perflog_label))
{
e->UiPerfToolOpen = true;
ImGui::FocusWindow(ImGui::FindWindowByName("Dear ImGui Perf Tool"));
}
}
int tests_completed = 0;
int tests_succeeded = 0;
int tests_failed = 0;
if (ImGui::BeginTable("Tests", 3, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable | ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_SizingFixedFit))
{
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("Status");
ImGui::TableSetupColumn("Category");
ImGui::TableSetupColumn("Test", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableHeadersRow();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6, 4) * dpi_scale);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4, 0) * dpi_scale);
//ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(100, 10) * dpi_scale);
for (int test_n = 0; test_n < e->TestsAll.Size; test_n++)
{
ImGuiTest* test = e->TestsAll[test_n];
if (!ShowTestGroupFilterTest(e, group, filter->c_str(), test))
continue;
ImGuiTestOutput* test_output = &test->Output;
ImGuiTestContext* test_context = (e->TestContext && e->TestContext->Test == test) ? e->TestContext : NULL; // Running context, if any
ImGui::TableNextRow();
ImGui::PushID(test_n);
// Colors match general test status colors defined below.
ImVec4 status_color;
switch (test_output->Status)
{
case ImGuiTestStatus_Error:
status_color = ImVec4(0.9f, 0.1f, 0.1f, 1.0f);
tests_completed++;
tests_failed++;
break;
case ImGuiTestStatus_Success:
status_color = ImVec4(0.1f, 0.9f, 0.1f, 1.0f);
tests_completed++;
tests_succeeded++;
break;
case ImGuiTestStatus_Queued:
case ImGuiTestStatus_Running:
case ImGuiTestStatus_Suspended:
if (test_context && (test_context->RunFlags & ImGuiTestRunFlags_GuiFuncOnly))
status_color = ImVec4(0.8f, 0.0f, 0.8f, 1.0f);
else
status_color = ImVec4(0.8f, 0.4f, 0.1f, 1.0f);
break;
default:
status_color = ImVec4(0.4f, 0.4f, 0.4f, 1.0f);
break;
}
ImGui::TableNextColumn();
TestStatusButton("status", status_color, test_output->Status == ImGuiTestStatus_Running || test_output->Status == ImGuiTestStatus_Suspended, -1);
ImGui::SameLine();
bool queue_test = false;
bool queue_gui_func_toggle = false;
bool select_test = false;
if (test_output->Status == ImGuiTestStatus_Suspended)
{
// Resume IM_SUSPEND_TESTFUNC
// FIXME: Terrible user experience to have this here.
if (ImGui::Button("Con###Run"))
test_output->Status = ImGuiTestStatus_Running;
ImGui::SetItemTooltip("CTRL+Space to continue.");
if (ImGui::IsKeyPressed(ImGuiKey_Space) && io.KeyCtrl)
test_output->Status = ImGuiTestStatus_Running;
}
else
{
if (ImGui::Button("Run###Run"))
queue_test = select_test = true;
}
ImGui::TableNextColumn();
if (ImGui::Selectable(test->Category, test == e->UiSelectedTest, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SelectOnNav))
select_test = true;
// Double-click to run test, CTRL+Double-click to run GUI function
const bool is_running_gui_func = (test_context && (test_context->RunFlags & ImGuiTestRunFlags_GuiFuncOnly));
const bool has_gui_func = (test->GuiFunc != NULL);
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0))
{
if (ImGui::GetIO().KeyCtrl)
queue_gui_func_toggle = true;
else
queue_test = true;
}
/*if (ImGui::IsItemHovered() && test->TestLog.size() > 0)
{
ImGui::BeginTooltip();
DrawTestLog(engine, test, false);
ImGui::EndTooltip();
}*/
if (e->UiSelectAndScrollToTest == test)
ImGui::SetScrollHereY();
bool view_source = false;
if (ImGui::BeginPopupContextItem())
{
select_test = true;
if (ImGui::MenuItem("Run test"))
queue_test = true;
if (ImGui::MenuItem("Run GUI func", "Ctrl+DblClick", is_running_gui_func, has_gui_func))
queue_gui_func_toggle = true;
ImGui::Separator();
const bool open_source_available = (test->SourceFile != NULL) && (e->IO.SrcFileOpenFunc != NULL);
Str128 buf;
if (test->SourceFile != NULL) // This is normally set by IM_REGISTER_TEST() but custom registration may omit it.
buf.setf("Open source (%s:%d)", ImPathFindFilename(test->SourceFile), test->SourceLine);
else
buf.set("Open source");
if (ImGui::MenuItem(buf.c_str(), NULL, false, open_source_available))
e->IO.SrcFileOpenFunc(test->SourceFile, test->SourceLine, e->IO.SrcFileOpenUserData);
if (ImGui::MenuItem("View source...", NULL, false, test->SourceFile != NULL))
view_source = true;
if (group == ImGuiTestGroup_Perfs && ImGui::MenuItem("View perflog"))
{
e->PerfTool->ViewOnly(test->Name);
e->UiPerfToolOpen = true;
}
ImGui::Separator();
if (ImGui::MenuItem("Copy name", NULL, false))
ImGui::SetClipboardText(test->Name);
if (test_output->Status == ImGuiTestStatus_Error)
if (ImGui::MenuItem("Copy names of all failing tests"))
{
Str256 failing_tests;
GetFailingTestsAsString(e, group, ',', &failing_tests);
ImGui::SetClipboardText(failing_tests.c_str());
}
ImGuiTestLog* test_log = &test_output->Log;
if (ImGui::BeginMenu("Copy log", !test_log->IsEmpty()))
{
for (int level_n = ImGuiTestVerboseLevel_Error; level_n < ImGuiTestVerboseLevel_COUNT; level_n++)
{
ImGuiTestVerboseLevel level = (ImGuiTestVerboseLevel)level_n;
int count = test_log->ExtractLinesForVerboseLevels((ImGuiTestVerboseLevel)0, level, NULL);
if (ImGui::MenuItem(Str64f("%s (%d lines)", ImGuiTestEngine_GetVerboseLevelName(level), count).c_str(), NULL, false, count > 0))
{
ImGuiTextBuffer buffer;
test_log->ExtractLinesForVerboseLevels((ImGuiTestVerboseLevel)0, level, &buffer);
ImGui::SetClipboardText(buffer.c_str());
}
}
ImGui::EndMenu();
}
if (ImGui::MenuItem("Clear log", NULL, false, !test_log->IsEmpty()))
test_log->Clear();
ImGui::EndPopup();
}
// Process source popup
static ImGuiTextBuffer source_blurb;
static int goto_line = -1;
if (view_source)
{
source_blurb.clear();
size_t file_size = 0;
char* file_data = (char*)ImFileLoadToMemory(test->SourceFile, "rb", &file_size);
if (file_data)
source_blurb.append(file_data, file_data + file_size);
else
source_blurb.append("<Error loading sources>");
goto_line = (test->SourceLine + test->SourceLineEnd) / 2;
ImGui::OpenPopup("Source");
}
if (ImGui::BeginPopup("Source"))
{
// FIXME: Local vs screen pos too messy :(
const ImVec2 start_pos = ImGui::GetCursorStartPos();
const float line_height = ImGui::GetTextLineHeight();
if (goto_line != -1)
ImGui::SetScrollFromPosY(start_pos.y + (goto_line - 1) * line_height, 0.5f);
goto_line = -1;
ImRect r(0.0f, test->SourceLine * line_height, ImGui::GetWindowWidth(), (test->SourceLine + 1) * line_height); // SourceLineEnd is too flaky
ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetWindowPos() + start_pos + r.Min, ImGui::GetWindowPos() + start_pos + r.Max, IM_COL32(80, 80, 150, 150));
ImGui::TextUnformatted(source_blurb.c_str(), source_blurb.end());
ImGui::EndPopup();
}
ImGui::TableNextColumn();
ImGui::TextUnformatted(test->Name);
// Process selection
if (select_test)
e->UiSelectedTest = test;
// Process queuing
if (queue_gui_func_toggle && is_running_gui_func)
ImGuiTestEngine_AbortCurrentTest(e);
else if (queue_gui_func_toggle && !e->IO.IsRunningTests)
ImGuiTestEngine_QueueTest(e, test, ImGuiTestRunFlags_RunFromGui | ImGuiTestRunFlags_GuiFuncOnly);
if (queue_test && !e->IO.IsRunningTests)
ImGuiTestEngine_QueueTest(e, test, ImGuiTestRunFlags_RunFromGui);
ImGui::PopID();
}
ImGui::Spacing();
ImGui::PopStyleVar(2);
ImGui::EndTable();
}
// Display test status recap (colors match per-test run button colors defined above)
{
ImVec4 status_color;
if (tests_failed > 0)
status_color = ImVec4(0.9f, 0.1f, 0.1f, 1.0f); // Red
else if (e->IO.IsRunningTests)
status_color = ImVec4(0.8f, 0.4f, 0.1f, 1.0f);
else if (tests_succeeded > 0 && tests_completed == tests_succeeded)
status_color = ImVec4(0.1f, 0.9f, 0.1f, 1.0f);
else
status_color = ImVec4(0.4f, 0.4f, 0.4f, 1.0f);
//ImVec2 cursor_pos_bkp = ImGui::GetCursorPos();
ImGui::SetCursorPos(status_button_pos);
TestStatusButton("status", status_color, false, tests_failed > 0 ? tests_failed : -1);// e->IO.IsRunningTests);
ImGui::SetItemTooltip("Filtered: %d\n- OK: %d\n- Errors: %d", tests_completed, tests_succeeded, tests_failed);
//ImGui::SetCursorPos(cursor_pos_bkp); // Restore cursor position for rendering further widgets
}
}
static void ImGuiTestEngine_ShowLogAndTools(ImGuiTestEngine* engine)
{
ImGuiContext& g = *GImGui;
const float dpi_scale = GetDpiScale();
if (!ImGui::BeginTabBar("##tools"))
return;
if (ImGui::BeginTabItem("LOG"))
{
ImGuiTest* selected_test = engine->UiSelectedTest;
if (selected_test != NULL)
ImGui::Text("Log for '%s' '%s'", selected_test->Category, selected_test->Name);
else
ImGui::Text("N/A");
if (ImGui::SmallButton("Clear"))
if (selected_test)
selected_test->Output.Log.Clear();
ImGui::SameLine();
if (ImGui::SmallButton("Copy to clipboard"))
if (engine->UiSelectedTest)
ImGui::SetClipboardText(selected_test->Output.Log.Buffer.c_str());
ImGui::Separator();
ImGui::BeginChild("Log");
if (engine->UiSelectedTest)
{
DrawTestLog(engine, engine->UiSelectedTest);
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY();
}
ImGui::EndChild();
ImGui::EndTabItem();
}
// Options
if (ImGui::BeginTabItem("OPTIONS"))
{
ImGuiIO& io = ImGui::GetIO();
ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
ImGui::Text("TestEngine: HookItems: %d, HookPushId: %d, InfoTasks: %d", g.TestEngineHookItems, g.DebugHookIdInfo != 0, engine->InfoTasks.Size);
ImGui::Separator();
if (ImGui::Button("Reboot UI context"))
engine->ToolDebugRebootUiContext = true;
const ImGuiInputTextCallback filter_callback = [](ImGuiInputTextCallbackData* data) { return (data->EventChar == ',' || data->EventChar == ';') ? 1 : 0; };
ImGui::InputText("Branch/Annotation", engine->IO.GitBranchName, IM_ARRAYSIZE(engine->IO.GitBranchName), ImGuiInputTextFlags_CallbackCharFilter, filter_callback, NULL);
ImGui::SetItemTooltip("This will be stored in the CSV file for performance tools.");
ImGui::Separator();
if (ImGui::TreeNode("Screen/video capture"))
{
ImGui::Checkbox("Capture when requested by API", &engine->IO.ConfigCaptureEnabled);
ImGui::SetItemTooltip("Enable or disable screen capture API completely.");
ImGui::Checkbox("Capture screen on error", &engine->IO.ConfigCaptureOnError);
ImGui::SetItemTooltip("Capture a screenshot on test failure.");
// Fields modified by in this call will be synced to engine->CaptureContext.
engine->CaptureTool._ShowEncoderConfigFields(&engine->CaptureContext);
ImGui::TreePop();
}
if (ImGui::TreeNode("Performances"))
{
ImGui::Checkbox("Slow down whole app", &engine->ToolSlowDown);
ImGui::SameLine(); ImGui::SetNextItemWidth(70 * dpi_scale);
ImGui::SliderInt("##ms", &engine->ToolSlowDownMs, 0, 400, "%d ms");
// FIXME-TESTS: Need to be visualizing the samples/spikes.
double dt_1 = 1.0 / ImGui::GetIO().Framerate;
double fps_now = 1.0 / dt_1;
double dt_100 = engine->PerfDeltaTime100.GetAverage();
double dt_500 = engine->PerfDeltaTime500.GetAverage();
//if (engine->PerfRefDeltaTime <= 0.0 && engine->PerfRefDeltaTime.IsFull())
// engine->PerfRefDeltaTime = dt_2000;
ImGui::Checkbox("Unthrolled", &engine->IO.ConfigNoThrottle);
ImGui::SameLine();
if (ImGui::Button("Pick ref dt"))
engine->PerfRefDeltaTime = dt_500;
double dt_ref = engine->PerfRefDeltaTime;
ImGui::Text("[ref dt] %6.3f ms", engine->PerfRefDeltaTime * 1000);
ImGui::Text("[last 001] %6.3f ms (%.1f FPS) ++ %6.3f ms", dt_1 * 1000.0, 1.0 / dt_1, (dt_1 - dt_ref) * 1000);
ImGui::Text("[last 100] %6.3f ms (%.1f FPS) ++ %6.3f ms ~ converging in %.1f secs", dt_100 * 1000.0, 1.0 / dt_100, (dt_1 - dt_ref) * 1000, 100.0 / fps_now);
ImGui::Text("[last 500] %6.3f ms (%.1f FPS) ++ %6.3f ms ~ converging in %.1f secs", dt_500 * 1000.0, 1.0 / dt_500, (dt_1 - dt_ref) * 1000, 500.0 / fps_now);
//ImGui::PlotLines("Last 100", &engine->PerfDeltaTime100.Samples.Data, engine->PerfDeltaTime100.Samples.Size, engine->PerfDeltaTime100.Idx, NULL, 0.0f, dt_1000 * 1.10f, ImVec2(0.0f, ImGui::GetFontSize()));
ImVec2 plot_size(0.0f, ImGui::GetFrameHeight() * 3);
ImMovingAverage<double>* ma = &engine->PerfDeltaTime500;
ImGui::PlotLines("Last 500",
[](void* data, int n) { ImMovingAverage<double>* ma = (ImMovingAverage<double>*)data; return (float)(ma->Samples[n] * 1000); },
ma, ma->Samples.Size, 0 * ma->Idx, NULL, 0.0f, (float)(ImMax(dt_100, dt_500) * 1000.0 * 1.2f), plot_size);
ImGui::TreePop();
}
if (ImGui::TreeNode("Dear ImGui Configuration Flags"))
{
ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", &io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard);
ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", &io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad);
#ifdef IMGUI_HAS_DOCK
ImGui::Checkbox("io.ConfigDockingAlwaysTabBar", &io.ConfigDockingAlwaysTabBar);
#endif
ImGui::TreePop();
}
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
static void ImGuiTestEngine_ShowTestTool(ImGuiTestEngine* engine, bool* p_open)
{
const float dpi_scale = GetDpiScale();
ImGui::SetNextWindowSize(ImVec2(ImGui::GetFontSize() * 50, ImGui::GetFontSize() * 40), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Dear ImGui Test Engine", p_open, ImGuiWindowFlags_MenuBar))
{
ImGui::End();
return;
}
if (ImGui::BeginMenuBar())
{
if (ImGui::BeginMenu("Tools"))
{
ImGuiContext& g = *GImGui;
ImGui::MenuItem("Metrics/Debugger", "", &engine->UiMetricsOpen);
ImGui::MenuItem("Debug Log", "", &engine->UiDebugLogOpen);
ImGui::MenuItem("Stack Tool", "", &engine->UiStackToolOpen);
ImGui::MenuItem("Item Picker", "", &g.DebugItemPickerActive);
ImGui::Separator();
ImGui::MenuItem("Capture Tool", "", &engine->UiCaptureToolOpen);
ImGui::MenuItem("Perf Tool", "", &engine->UiPerfToolOpen);
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
ImGui::SetNextItemWidth(90 * dpi_scale);
if (ImGui::BeginCombo("##RunSpeed", ImGuiTestEngine_GetRunSpeedName(engine->IO.ConfigRunSpeed), ImGuiComboFlags_None))
{
for (ImGuiTestRunSpeed level = (ImGuiTestRunSpeed)0; level < ImGuiTestRunSpeed_COUNT; level = (ImGuiTestRunSpeed)(level + 1))
if (ImGui::Selectable(ImGuiTestEngine_GetRunSpeedName(level), engine->IO.ConfigRunSpeed == level))
engine->IO.ConfigRunSpeed = level;
ImGui::EndCombo();
}
ImGui::SetItemTooltip(
"Running speed\n"
"- Fast: Run tests as fast as possible (no delay/vsync, teleport mouse, etc.).\n"
"- Normal: Run tests at human watchable speed (for debugging).\n"
"- Cinematic: Run tests with pauses between actions (for e.g. tutorials)."
);
ImGui::SameLine();
//ImGui::Checkbox("Fast", &engine->IO.ConfigRunFast);
//ImGui::SameLine();
ImGui::Checkbox("Stop", &engine->IO.ConfigStopOnError);
ImGui::SetItemTooltip("Stop running tests when hitting an error.");
ImGui::SameLine();
ImGui::Checkbox("DbgBrk", &engine->IO.ConfigBreakOnError);
ImGui::SetItemTooltip("Break in debugger when hitting an error.");
ImGui::SameLine();
ImGui::Checkbox("KeepGUI", &engine->IO.ConfigKeepGuiFunc);
ImGui::SetItemTooltip("Keep GUI function running after a test fails, or when a single queued test is finished.\nHold ESC to abort a running GUI function.");
ImGui::SameLine();
ImGui::Checkbox("Refocus", &engine->IO.ConfigRestoreFocusAfterTests);
ImGui::SetItemTooltip("Restore focus back after running tests.");
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine();
ImGui::SetNextItemWidth(70 * dpi_scale);
if (ImGui::BeginCombo("##Verbose", ImGuiTestEngine_GetVerboseLevelName(engine->IO.ConfigVerboseLevel), ImGuiComboFlags_None))
{
for (ImGuiTestVerboseLevel level = (ImGuiTestVerboseLevel)0; level < ImGuiTestVerboseLevel_COUNT; level = (ImGuiTestVerboseLevel)(level + 1))
if (ImGui::Selectable(ImGuiTestEngine_GetVerboseLevelName(level), engine->IO.ConfigVerboseLevel == level))
engine->IO.ConfigVerboseLevel = engine->IO.ConfigVerboseLevelOnError = level;
ImGui::EndCombo();
}
ImGui::SetItemTooltip("Verbose level.");
//ImGui::PopStyleVar();
ImGui::Separator();
// SPLITTER
// FIXME-OPT: A better splitter API supporting arbitrary number of splits would be useful.
float list_height = 0.0f;
float& log_height = engine->UiLogHeight;
ImGui::Splitter("splitter", &list_height, &log_height, ImGuiAxis_Y, +1);
// TESTS
ImGui::BeginChild("List", ImVec2(0, list_height), false, ImGuiWindowFlags_NoScrollbar);
if (ImGui::BeginTabBar("##Tests", ImGuiTabBarFlags_NoTooltip)) // Add _NoPushId flag in TabBar?
{
if (ImGui::BeginTabItem("TESTS", NULL, ImGuiTabItemFlags_NoPushId))
{
ShowTestGroup(engine, ImGuiTestGroup_Tests, engine->UiFilterTests);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("PERFS", NULL, ImGuiTabItemFlags_NoPushId))
{
ShowTestGroup(engine, ImGuiTestGroup_Perfs, engine->UiFilterPerfs);
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
ImGui::EndChild();
engine->UiSelectAndScrollToTest = NULL;
// LOG & TOOLS
ImGui::BeginChild("Log", ImVec2(0, log_height));
ImGuiTestEngine_ShowLogAndTools(engine);
ImGui::EndChild();
ImGui::End();
}
void ImGuiTestEngine_ShowTestEngineWindows(ImGuiTestEngine* e, bool* p_open)
{
// Test Tool
ImGuiTestEngine_ShowTestTool(e, p_open);
// Stack Tool
#if IMGUI_VERSION_NUM < 18993
if (e->UiStackToolOpen)
ImGui::ShowStackToolWindow(&e->UiStackToolOpen);
#else
if (e->UiStackToolOpen)
ImGui::ShowIDStackToolWindow(&e->UiStackToolOpen);
#endif
// Capture Tool
if (e->UiCaptureToolOpen)
e->CaptureTool.ShowCaptureToolWindow(&e->CaptureContext, &e->UiCaptureToolOpen);
// Performance tool
if (e->UiPerfToolOpen)
e->PerfTool->ShowPerfToolWindow(e, &e->UiPerfToolOpen);;
// Show Dear ImGui windows
// (we cannot show demo window here because it could lead to duplicate display, which demo windows isn't guarded for)
if (e->UiMetricsOpen)
ImGui::ShowMetricsWindow(&e->UiMetricsOpen);
if (e->UiDebugLogOpen)
ImGui::ShowDebugLogWindow(&e->UiDebugLogOpen);
}

View File

@ -0,0 +1,21 @@
// dear imgui test engine
// (ui)
// If you run tests in an interactive or visible application, you may want to call ImGuiTestEngine_ShowTestEngineWindows()
// Provide access to:
// - "Dear ImGui Test Engine" main interface
// - "Dear ImGui Capture Tool"
// - "Dear ImGui Perf Tool"
// - other core debug functions: Metrics, Debug Log
#pragma once
#ifndef IMGUI_VERSION
#include "imgui.h" // IMGUI_API
#endif
// Forward declarations
struct ImGuiTestEngine;
// Functions
IMGUI_API void ImGuiTestEngine_ShowTestEngineWindows(ImGuiTestEngine* engine, bool* p_open);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,221 @@
// dear imgui test engine
// (helpers/utilities. do NOT use this as a general purpose library)
#pragma once
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include <math.h> // fabsf
#include <stdint.h> // uint64_t
#include <stdio.h> // FILE*
#include "imgui.h" // ImGuiID, ImGuiKey
class Str; // Str<> from thirdparty/Str/Str.h
//-----------------------------------------------------------------------------
// Function Pointers
//-----------------------------------------------------------------------------
#if IMGUI_TEST_ENGINE_ENABLE_STD_FUNCTION
#include <functional>
#define ImFuncPtr(FUNC_TYPE) std::function<FUNC_TYPE>
#else
#define ImFuncPtr(FUNC_TYPE) FUNC_TYPE*
#endif
//-----------------------------------------------------------------------------
// Hashing Helpers
//-----------------------------------------------------------------------------
ImGuiID ImHashDecoratedPath(const char* str, const char* str_end = NULL, ImGuiID seed = 0);
const char* ImFindNextDecoratedPartInPath(const char* str, const char* str_end = NULL);
//-----------------------------------------------------------------------------
// File/Directory Helpers
//-----------------------------------------------------------------------------
bool ImFileExist(const char* filename);
bool ImFileDelete(const char* filename);
bool ImFileCreateDirectoryChain(const char* path, const char* path_end = NULL);
bool ImFileFindInParents(const char* sub_path, int max_parent_count, Str* output);
bool ImFileLoadSourceBlurb(const char* filename, int line_no_start, int line_no_end, ImGuiTextBuffer* out_buf);
//-----------------------------------------------------------------------------
// Path Helpers
//-----------------------------------------------------------------------------
// Those are strictly string manipulation functions
const char* ImPathFindFilename(const char* path, const char* path_end = NULL); // Return value always between path and path_end
const char* ImPathFindExtension(const char* path, const char* path_end = NULL); // Return value always between path and path_end
void ImPathFixSeparatorsForCurrentOS(char* buf);
//-----------------------------------------------------------------------------
// String Helpers
//-----------------------------------------------------------------------------
void ImStrReplace(Str* s, const char* find, const char* repl);
const char* ImStrchrRangeWithEscaping(const char* str, const char* str_end, char find_c);
void ImStrXmlEscape(Str* s);
int ImStrBase64Encode(const unsigned char* src, char* dst, int length);
//-----------------------------------------------------------------------------
// Parsing Helpers
//-----------------------------------------------------------------------------
void ImParseExtractArgcArgvFromCommandLine(int* out_argc, char const*** out_argv, const char* cmd_line);
bool ImParseFindIniSection(const char* ini_config, const char* header, ImVector<char>* result);
//-----------------------------------------------------------------------------
// Time Helpers
//-----------------------------------------------------------------------------
uint64_t ImTimeGetInMicroseconds();
void ImTimestampToISO8601(uint64_t timestamp, Str* out_date);
//-----------------------------------------------------------------------------
// Threading Helpers
//-----------------------------------------------------------------------------
void ImThreadSleepInMilliseconds(int ms);
void ImThreadSetCurrentThreadDescription(const char* description);
//-----------------------------------------------------------------------------
// Build Info helpers
//-----------------------------------------------------------------------------
// All the pointers are expect to be literals/persistent
struct ImBuildInfo
{
const char* Type = "";
const char* Cpu = "";
const char* OS = "";
const char* Compiler = "";
char Date[32]; // "YYYY-MM-DD"
const char* Time = "";
};
const ImBuildInfo* ImBuildGetCompilationInfo();
bool ImBuildFindGitBranchName(const char* git_repo_path, Str* branch_name);
//-----------------------------------------------------------------------------
// Operating System Helpers
//-----------------------------------------------------------------------------
enum ImOsConsoleStream
{
ImOsConsoleStream_StandardOutput,
ImOsConsoleStream_StandardError,
};
enum ImOsConsoleTextColor
{
ImOsConsoleTextColor_Black,
ImOsConsoleTextColor_White,
ImOsConsoleTextColor_BrightWhite,
ImOsConsoleTextColor_BrightRed,
ImOsConsoleTextColor_BrightGreen,
ImOsConsoleTextColor_BrightBlue,
ImOsConsoleTextColor_BrightYellow,
};
bool ImOsCreateProcess(const char* cmd_line);
FILE* ImOsPOpen(const char* cmd_line, const char* mode);
void ImOsPClose(FILE* fp);
void ImOsOpenInShell(const char* path);
bool ImOsIsDebuggerPresent();
void ImOsOutputDebugString(const char* message);
void ImOsConsoleSetTextColor(ImOsConsoleStream stream, ImOsConsoleTextColor color);
//-----------------------------------------------------------------------------
// Miscellaneous functions
//-----------------------------------------------------------------------------
// Tables functions
struct ImGuiTable;
ImGuiID TableGetHeaderID(ImGuiTable* table, const char* column, int instance_no = 0);
ImGuiID TableGetHeaderID(ImGuiTable* table, int column_n, int instance_no = 0);
void TableDiscardInstanceAndSettings(ImGuiID table_id);
// DrawData functions
void DrawDataVerifyMatchingBufferCount(ImDrawData* draw_data);
//-----------------------------------------------------------------------------
// Helper: maintain/calculate moving average
//-----------------------------------------------------------------------------
template<typename TYPE>
struct ImMovingAverage
{
// Internal Fields
ImVector<TYPE> Samples;
TYPE Accum;
int Idx;
int FillAmount;
// Functions
ImMovingAverage() { Accum = (TYPE)0; Idx = FillAmount = 0; }
void Init(int count) { Samples.resize(count); memset(Samples.Data, 0, (size_t)Samples.Size * sizeof(TYPE)); Accum = (TYPE)0; Idx = FillAmount = 0; }
void AddSample(TYPE v) { Accum += v - Samples[Idx]; Samples[Idx] = v; if (++Idx == Samples.Size) Idx = 0; if (FillAmount < Samples.Size) FillAmount++; }
TYPE GetAverage() const { return Accum / (TYPE)FillAmount; }
int GetSampleCount() const { return Samples.Size; }
bool IsFull() const { return FillAmount == Samples.Size; }
};
//-----------------------------------------------------------------------------
// Helper: Simple/dumb CSV parser
//-----------------------------------------------------------------------------
struct ImGuiCsvParser
{
// Public fields
int Columns = 0; // Number of columns in CSV file.
int Rows = 0; // Number of rows in CSV file.
// Internal fields
char* _Data = NULL; // CSV file data.
ImVector<char*> _Index; // CSV table: _Index[row * _Columns + col].
// Functions
ImGuiCsvParser(int columns = -1) { Columns = columns; }
~ImGuiCsvParser() { Clear(); }
bool Load(const char* file_name); // Open and parse a CSV file.
void Clear(); // Free allocated buffers.
const char* GetCell(int row, int col) { IM_ASSERT(0 <= row && row < Rows && 0 <= col && col < Columns); return _Index[row * Columns + col]; }
};
//-----------------------------------------------------------------------------
// Misc Dear ImGui extensions
//-----------------------------------------------------------------------------
#if IMGUI_VERSION_NUM < 18924
struct ImGuiTabBar;
struct ImGuiTabItem;
#endif
namespace ImGui
{
IMGUI_API void ItemErrorFrame(ImU32 col);
#if IMGUI_VERSION_NUM < 18927
ImGuiID TableGetInstanceID(ImGuiTable* table, int instance_no = 0);
#endif
// Str support for InputText()
IMGUI_API bool InputText(const char* label, Str* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
IMGUI_API bool InputTextWithHint(const char* label, const char* hint, Str* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
IMGUI_API bool InputTextMultiline(const char* label, Str* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
// Splitter
IMGUI_API bool Splitter(const char* id, float* value_1, float* value_2, int axis, int anchor = 0, float min_size_0 = -1.0f, float min_size_1 = -1.0f);
// Misc
IMGUI_API ImFont* FindFontByPrefix(const char* name);
// Legacy version support
#if IMGUI_VERSION_NUM < 18924
IMGUI_API const char* TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);
#endif
}

View File

@ -0,0 +1,7 @@
## Third party libraries used by Test Engine
Always used:
- `Str/Str.h` simple string type, used by `imgui_test_engine` (Public Domain)
Used if `IMGUI_TEST_ENGINE_ENABLE_CAPTURE` is defined to 1 (default: 1)
- `stb/imstb_image_write.h` image writer, used by `imgui_capture_tool` (MIT Licence OR Public Domain)

View File

@ -0,0 +1,71 @@
```
Str
Simple C++ string type with an optional local buffer, by Omar Cornut
https://github.com/ocornut/str
LICENSE
This software is in the public domain. Where that dedication is not
recognized, you are granted a perpetual, irrevocable license to copy,
distribute, and modify this file as you see fit.
USAGE
Include Str.h in whatever places need to refer to it.
In ONE .cpp file, write '#define STR_IMPLEMENTATION' before the #include.
This expands out the actual implementation into that C/C++ file.
NOTES
- This isn't a fully featured string class.
- It is a simple, bearable replacement to std::string that isn't heap abusive nor bloated (can actually be debugged by humans!).
- String are mutable. We don't maintain size so length() is not-constant time.
- Maximum string size currently limited to 2 MB (we allocate 21 bits to hold capacity).
- Local buffer size is currently limited to 1023 bytes (we allocate 10 bits to hold local buffer size).
- We could easily raise those limits if we are ok to increase the structure overhead in 32-bits mode.
- In "non-owned" mode for literals/reference we don't do any tracking/counting of references.
- Overhead is 8-bytes in 32-bits, 16-bytes in 64-bits (12 + alignment).
- I'm using this code but it hasn't been tested thoroughly.
The idea is that you can provide an arbitrary sized local buffer if you expect string to fit
most of the time, and then you avoid using costly heap.
No local buffer, always use heap, sizeof()==8~16 (depends if your pointers are 32-bits or 64-bits)
Str s = "hey"; // use heap
With a local buffer of 16 bytes, sizeof() == 8~16 + 16 bytes.
Str16 s = "filename.h"; // copy into local buffer
Str16 s = "long_filename_not_very_long_but_longer_than_expected.h"; // use heap
With a local buffer of 256 bytes, sizeof() == 8~16 + 256 bytes.
Str256 s = "long_filename_not_very_long_but_longer_than_expected.h"; // copy into local buffer
Common sizes are defined at the bottom of Str.h, you may define your own.
Functions:
Str256 s;
s.set("hello sailor"); // set (copy)
s.setf("%s/%s.tmp", folder, filename); // set (w/format)
s.append("hello"); // append. cost a length() calculation!
s.appendf("hello %d", 42); // append (w/format). cost a length() calculation!
s.set_ref("Hey!"); // set (literal/reference, just copy pointer, no tracking)
Constructor helper for format string: add a trailing 'f' to the type. Underlying type is the same.
Str256f filename("%s/%s.tmp", folder, filename); // construct (w/format)
fopen(Str256f("%s/%s.tmp, folder, filename).c_str(), "rb"); // construct (w/format), use as function param, destruct
Constructor helper for reference/literal:
StrRef ref("literal"); // copy pointer, no allocation, no string copy
StrRef ref2(GetDebugName()); // copy pointer. no tracking of anything whatsoever, know what you are doing!
All StrXXX types derives from Str and instance hold the local buffer capacity.
So you can pass e.g. Str256* to a function taking base type Str* and it will be functional!
void MyFunc(Str& s) { s = "Hello"; } // will use local buffer if available in Str instance
(Using a template e.g. Str<N> we could remove the LocalBufSize storage but it would make passing typed Str<> to functions tricky.
Instead we don't use template so you can pass them around as the base type Str*. Also, templates are ugly.)
```

View File

@ -0,0 +1,658 @@
// Str v0.32
// Simple C++ string type with an optional local buffer, by Omar Cornut
// https://github.com/ocornut/str
// LICENSE
// This software is in the public domain. Where that dedication is not
// recognized, you are granted a perpetual, irrevocable license to copy,
// distribute, and modify this file as you see fit.
// USAGE
// Include this file in whatever places need to refer to it.
// In ONE .cpp file, write '#define STR_IMPLEMENTATION' before the #include of this file.
// This expands out the actual implementation into that C/C++ file.
/*
- This isn't a fully featured string class.
- It is a simple, bearable replacement to std::string that isn't heap abusive nor bloated (can actually be debugged by humans).
- String are mutable. We don't maintain size so length() is not-constant time.
- Maximum string size currently limited to 2 MB (we allocate 21 bits to hold capacity).
- Local buffer size is currently limited to 1023 bytes (we allocate 10 bits to hold local buffer size).
- In "non-owned" mode for literals/reference we don't do any tracking/counting of references.
- Overhead is 8-bytes in 32-bits, 16-bytes in 64-bits (12 + alignment).
- This code hasn't been tested very much. it is probably incomplete or broken. Made it for my own use.
The idea is that you can provide an arbitrary sized local buffer if you expect string to fit
most of the time, and then you avoid using costly heap.
No local buffer, always use heap, sizeof()==8~16 (depends if your pointers are 32-bits or 64-bits)
Str s = "hey";
With a local buffer of 16 bytes, sizeof() == 8~16 + 16 bytes.
Str16 s = "filename.h"; // copy into local buffer
Str16 s = "long_filename_not_very_long_but_longer_than_expected.h"; // use heap
With a local buffer of 256 bytes, sizeof() == 8~16 + 256 bytes.
Str256 s = "long_filename_not_very_long_but_longer_than_expected.h"; // copy into local buffer
Common sizes are defined at the bottom of Str.h, you may define your own.
Functions:
Str256 s;
s.set("hello sailor"); // set (copy)
s.setf("%s/%s.tmp", folder, filename); // set (w/format)
s.append("hello"); // append. cost a length() calculation!
s.appendf("hello %d", 42); // append (w/format). cost a length() calculation!
s.set_ref("Hey!"); // set (literal/reference, just copy pointer, no tracking)
Constructor helper for format string: add a trailing 'f' to the type. Underlying type is the same.
Str256f filename("%s/%s.tmp", folder, filename); // construct (w/format)
fopen(Str256f("%s/%s.tmp, folder, filename).c_str(), "rb"); // construct (w/format), use as function param, destruct
Constructor helper for reference/literal:
StrRef ref("literal"); // copy pointer, no allocation, no string copy
StrRef ref2(GetDebugName()); // copy pointer. no tracking of anything whatsoever, know what you are doing!
All StrXXX types derives from Str and instance hold the local buffer capacity. So you can pass e.g. Str256* to a function taking base type Str* and it will be functional.
void MyFunc(Str& s) { s = "Hello"; } // will use local buffer if available in Str instance
(Using a template e.g. Str<N> we could remove the LocalBufSize storage but it would make passing typed Str<> to functions tricky.
Instead we don't use template so you can pass them around as the base type Str*. Also, templates are ugly.)
*/
/*
CHANGELOG
0.32 - added owned() accessor.
0.31 - fixed various warnings.
0.30 - turned into a single header file, removed Str.cpp.
0.29 - fixed bug when calling reserve on non-owned strings (ie. when using StrRef or set_ref), and fixed <string> include.
0.28 - breaking change: replaced Str32 by Str30 to avoid collision with Str32 from MacTypes.h .
0.27 - added STR_API and basic .natvis file.
0.26 - fixed set(cont char* src, const char* src_end) writing null terminator to the wrong position.
0.25 - allow set(const char* NULL) or operator= NULL to clear the string. note that set() from range or other types are not allowed.
0.24 - allow set_ref(const char* NULL) to clear the string. include fixes for linux.
0.23 - added append(char). added append_from(int idx, XXX) functions. fixed some compilers warnings.
0.22 - documentation improvements, comments. fixes for some compilers.
0.21 - added StrXXXf() constructor to construct directly from a format string.
*/
/*
TODO
- Since we lose 4-bytes of padding on 64-bits architecture, perhaps just spread the header to 8-bytes and lift size limits?
- More functions/helpers.
*/
#ifndef STR_INCLUDED
#define STR_INCLUDED
//-------------------------------------------------------------------------
// CONFIGURATION
//-------------------------------------------------------------------------
#ifndef STR_MEMALLOC
#define STR_MEMALLOC malloc
#include <stdlib.h>
#endif
#ifndef STR_MEMFREE
#define STR_MEMFREE free
#include <stdlib.h>
#endif
#ifndef STR_ASSERT
#define STR_ASSERT assert
#include <assert.h>
#endif
#ifndef STR_API
#define STR_API
#endif
#include <stdarg.h> // for va_list
#include <string.h> // for strlen, strcmp, memcpy, etc.
// Configuration: #define STR_SUPPORT_STD_STRING 0 to disable setters variants using const std::string& (on by default)
#ifndef STR_SUPPORT_STD_STRING
#define STR_SUPPORT_STD_STRING 1
#endif
// Configuration: #define STR_DEFINE_STR32 1 to keep defining Str32/Str32f, but be warned: on macOS/iOS, MacTypes.h also defines a type named Str32.
#ifndef STR_DEFINE_STR32
#define STR_DEFINE_STR32 0
#endif
#if STR_SUPPORT_STD_STRING
#include <string>
#endif
//-------------------------------------------------------------------------
// HEADERS
//-------------------------------------------------------------------------
// This is the base class that you can pass around
// Footprint is 8-bytes (32-bits arch) or 16-bytes (64-bits arch)
class STR_API Str
{
char* Data; // Point to LocalBuf() or heap allocated
int Capacity : 21; // Max 2 MB
int LocalBufSize : 10; // Max 1023 bytes
unsigned int Owned : 1; // Set when we have ownership of the pointed data (most common, unless using set_ref() method or StrRef constructor)
public:
inline char* c_str() { return Data; }
inline const char* c_str() const { return Data; }
inline bool empty() const { return Data[0] == 0; }
inline int length() const { return (int)strlen(Data); } // by design, allow user to write into the buffer at any time
inline int capacity() const { return Capacity; }
inline bool owned() const { return Owned ? true : false; }
inline void set_ref(const char* src);
int setf(const char* fmt, ...);
int setfv(const char* fmt, va_list args);
int setf_nogrow(const char* fmt, ...);
int setfv_nogrow(const char* fmt, va_list args);
int append(char c);
int append(const char* s, const char* s_end = NULL);
int appendf(const char* fmt, ...);
int appendfv(const char* fmt, va_list args);
int append_from(int idx, char c);
int append_from(int idx, const char* s, const char* s_end = NULL); // If you know the string length or want to append from a certain point
int appendf_from(int idx, const char* fmt, ...);
int appendfv_from(int idx, const char* fmt, va_list args);
void clear();
void reserve(int cap);
void reserve_discard(int cap);
void shrink_to_fit();
inline char& operator[](size_t i) { return Data[i]; }
inline char operator[](size_t i) const { return Data[i]; }
//explicit operator const char*() const{ return Data; }
inline Str();
inline Str(const char* rhs);
inline void set(const char* src);
inline void set(const char* src, const char* src_end);
inline Str& operator=(const char* rhs) { set(rhs); return *this; }
inline bool operator==(const char* rhs) const { return strcmp(c_str(), rhs) == 0; }
inline Str(const Str& rhs);
inline void set(const Str& src);
inline Str& operator=(const Str& rhs) { set(rhs); return *this; }
inline bool operator==(const Str& rhs) const { return strcmp(c_str(), rhs.c_str()) == 0; }
#if STR_SUPPORT_STD_STRING
inline Str(const std::string& rhs);
inline void set(const std::string& src);
inline Str& operator=(const std::string& rhs) { set(rhs); return *this; }
inline bool operator==(const std::string& rhs)const { return strcmp(c_str(), rhs.c_str()) == 0; }
#endif
// Destructor for all variants
inline ~Str()
{
if (Owned && !is_using_local_buf())
STR_MEMFREE(Data);
}
static char* EmptyBuffer;
protected:
inline char* local_buf() { return (char*)this + sizeof(Str); }
inline const char* local_buf() const { return (char*)this + sizeof(Str); }
inline bool is_using_local_buf() const { return Data == local_buf() && LocalBufSize != 0; }
// Constructor for StrXXX variants with local buffer
Str(unsigned short local_buf_size)
{
STR_ASSERT(local_buf_size < 1024);
Data = local_buf();
Data[0] = '\0';
Capacity = local_buf_size;
LocalBufSize = local_buf_size;
Owned = 1;
}
};
void Str::set(const char* src)
{
// We allow set(NULL) or via = operator to clear the string.
if (src == NULL)
{
clear();
return;
}
int buf_len = (int)strlen(src)+1;
if (Capacity < buf_len)
reserve_discard(buf_len);
memcpy(Data, src, (size_t)buf_len);
Owned = 1;
}
void Str::set(const char* src, const char* src_end)
{
STR_ASSERT(src != NULL && src_end >= src);
int buf_len = (int)(src_end-src)+1;
if ((int)Capacity < buf_len)
reserve_discard(buf_len);
memcpy(Data, src, (size_t)(buf_len - 1));
Data[buf_len-1] = 0;
Owned = 1;
}
void Str::set(const Str& src)
{
int buf_len = (int)strlen(src.c_str())+1;
if ((int)Capacity < buf_len)
reserve_discard(buf_len);
memcpy(Data, src.c_str(), (size_t)buf_len);
Owned = 1;
}
#if STR_SUPPORT_STD_STRING
void Str::set(const std::string& src)
{
int buf_len = (int)src.length()+1;
if ((int)Capacity < buf_len)
reserve_discard(buf_len);
memcpy(Data, src.c_str(), (size_t)buf_len);
Owned = 1;
}
#endif
inline void Str::set_ref(const char* src)
{
if (Owned && !is_using_local_buf())
STR_MEMFREE(Data);
Data = src ? (char*)src : EmptyBuffer;
Capacity = 0;
Owned = 0;
}
Str::Str()
{
Data = EmptyBuffer; // Shared READ-ONLY initial buffer for 0 capacity
Capacity = 0;
LocalBufSize = 0;
Owned = 0;
}
Str::Str(const Str& rhs) : Str()
{
set(rhs);
}
Str::Str(const char* rhs) : Str()
{
set(rhs);
}
#if STR_SUPPORT_STD_STRING
Str::Str(const std::string& rhs) : Str()
{
set(rhs);
}
#endif
// Literal/reference string
class StrRef : public Str
{
public:
StrRef(const char* s) : Str() { set_ref(s); }
};
// Types embedding a local buffer
// NB: we need to override the constructor and = operator for both Str& and TYPENAME (without the later compiler will call a default copy operator)
#if STR_SUPPORT_STD_STRING
#define STR_DEFINETYPE(TYPENAME, LOCALBUFSIZE) \
class TYPENAME : public Str \
{ \
char local_buf[LOCALBUFSIZE]; \
public: \
TYPENAME() : Str(LOCALBUFSIZE) {} \
TYPENAME(const Str& rhs) : Str(LOCALBUFSIZE) { set(rhs); } \
TYPENAME(const char* rhs) : Str(LOCALBUFSIZE) { set(rhs); } \
TYPENAME(const TYPENAME& rhs) : Str(LOCALBUFSIZE) { set(rhs); } \
TYPENAME(const std::string& rhs) : Str(LOCALBUFSIZE) { set(rhs); } \
TYPENAME& operator=(const char* rhs) { set(rhs); return *this; } \
TYPENAME& operator=(const Str& rhs) { set(rhs); return *this; } \
TYPENAME& operator=(const TYPENAME& rhs) { set(rhs); return *this; } \
TYPENAME& operator=(const std::string& rhs) { set(rhs); return *this; } \
};
#else
#define STR_DEFINETYPE(TYPENAME, LOCALBUFSIZE) \
class TYPENAME : public Str \
{ \
char local_buf[LOCALBUFSIZE]; \
public: \
TYPENAME() : Str(LOCALBUFSIZE) {} \
TYPENAME(const Str& rhs) : Str(LOCALBUFSIZE) { set(rhs); } \
TYPENAME(const char* rhs) : Str(LOCALBUFSIZE) { set(rhs); } \
TYPENAME(const TYPENAME& rhs) : Str(LOCALBUFSIZE) { set(rhs); } \
TYPENAME& operator=(const char* rhs) { set(rhs); return *this; } \
TYPENAME& operator=(const Str& rhs) { set(rhs); return *this; } \
TYPENAME& operator=(const TYPENAME& rhs) { set(rhs); return *this; } \
};
#endif
// Disable PVS-Studio warning V730: Not all members of a class are initialized inside the constructor (local_buf is not initialized and that is fine)
// -V:STR_DEFINETYPE:730
// Helper to define StrXXXf constructors
#define STR_DEFINETYPE_F(TYPENAME, TYPENAME_F) \
class TYPENAME_F : public TYPENAME \
{ \
public: \
TYPENAME_F(const char* fmt, ...) : TYPENAME() { va_list args; va_start(args, fmt); setfv(fmt, args); va_end(args); } \
};
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-private-field" // warning : private field 'local_buf' is not used
#endif
// Declaring types for common sizes here
STR_DEFINETYPE(Str16, 16)
STR_DEFINETYPE(Str30, 30)
STR_DEFINETYPE(Str64, 64)
STR_DEFINETYPE(Str128, 128)
STR_DEFINETYPE(Str256, 256)
STR_DEFINETYPE(Str512, 512)
// Declaring helper constructors to pass in format strings in one statement
STR_DEFINETYPE_F(Str16, Str16f)
STR_DEFINETYPE_F(Str30, Str30f)
STR_DEFINETYPE_F(Str64, Str64f)
STR_DEFINETYPE_F(Str128, Str128f)
STR_DEFINETYPE_F(Str256, Str256f)
STR_DEFINETYPE_F(Str512, Str512f)
#if STR_DEFINE_STR32
STR_DEFINETYPE(Str32, 32)
STR_DEFINETYPE_F(Str32, Str32f)
#endif
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif // #ifndef STR_INCLUDED
//-------------------------------------------------------------------------
// IMPLEMENTATION
//-------------------------------------------------------------------------
#ifdef STR_IMPLEMENTATION
#include <stdio.h> // for vsnprintf
// On some platform vsnprintf() takes va_list by reference and modifies it.
// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
#ifndef va_copy
#define va_copy(dest, src) (dest = src)
#endif
// Static empty buffer we can point to for empty strings
// Pointing to a literal increases the like-hood of getting a crash if someone attempts to write in the empty string buffer.
char* Str::EmptyBuffer = (char*)"\0NULL";
// Clear
void Str::clear()
{
if (Owned && !is_using_local_buf())
STR_MEMFREE(Data);
if (LocalBufSize)
{
Data = local_buf();
Data[0] = '\0';
Capacity = LocalBufSize;
Owned = 1;
}
else
{
Data = EmptyBuffer;
Capacity = 0;
Owned = 0;
}
}
// Reserve memory, preserving the current of the buffer
void Str::reserve(int new_capacity)
{
if (new_capacity <= Capacity)
return;
char* new_data;
if (new_capacity < LocalBufSize)
{
// Disowned -> LocalBuf
new_data = local_buf();
new_capacity = LocalBufSize;
}
else
{
// Disowned or LocalBuf -> Heap
new_data = (char*)STR_MEMALLOC((size_t)new_capacity * sizeof(char));
}
// string in Data might be longer than new_capacity if it wasn't owned, don't copy too much
#ifdef _MSC_VER
strncpy_s(new_data, (size_t)new_capacity, Data, (size_t)new_capacity - 1);
#else
strncpy(new_data, Data, (size_t)new_capacity - 1);
#endif
new_data[new_capacity - 1] = 0;
if (Owned && !is_using_local_buf())
STR_MEMFREE(Data);
Data = new_data;
Capacity = new_capacity;
Owned = 1;
}
// Reserve memory, discarding the current of the buffer (if we expect to be fully rewritten)
void Str::reserve_discard(int new_capacity)
{
if (new_capacity <= Capacity)
return;
if (Owned && !is_using_local_buf())
STR_MEMFREE(Data);
if (new_capacity < LocalBufSize)
{
// Disowned -> LocalBuf
Data = local_buf();
Capacity = LocalBufSize;
}
else
{
// Disowned or LocalBuf -> Heap
Data = (char*)STR_MEMALLOC((size_t)new_capacity * sizeof(char));
Capacity = new_capacity;
}
Owned = 1;
}
void Str::shrink_to_fit()
{
if (!Owned || is_using_local_buf())
return;
int new_capacity = length() + 1;
if (Capacity <= new_capacity)
return;
char* new_data = (char*)STR_MEMALLOC((size_t)new_capacity * sizeof(char));
memcpy(new_data, Data, (size_t)new_capacity);
STR_MEMFREE(Data);
Data = new_data;
Capacity = new_capacity;
}
// FIXME: merge setfv() and appendfv()?
int Str::setfv(const char* fmt, va_list args)
{
// Needed for portability on platforms where va_list are passed by reference and modified by functions
va_list args2;
va_copy(args2, args);
// MSVC returns -1 on overflow when writing, which forces us to do two passes
// FIXME-OPT: Find a way around that.
#ifdef _MSC_VER
int len = vsnprintf(NULL, 0, fmt, args);
STR_ASSERT(len >= 0);
if (Capacity < len + 1)
reserve_discard(len + 1);
len = vsnprintf(Data, len + 1, fmt, args2);
#else
// First try
int len = vsnprintf(Owned ? Data : NULL, Owned ? (size_t)Capacity : 0, fmt, args);
STR_ASSERT(len >= 0);
if (Capacity < len + 1)
{
reserve_discard(len + 1);
len = vsnprintf(Data, (size_t)len + 1, fmt, args2);
}
#endif
STR_ASSERT(Owned);
return len;
}
int Str::setf(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
int len = setfv(fmt, args);
va_end(args);
return len;
}
int Str::setfv_nogrow(const char* fmt, va_list args)
{
STR_ASSERT(Owned);
if (Capacity == 0)
return 0;
int w = vsnprintf(Data, (size_t)Capacity, fmt, args);
Data[Capacity - 1] = 0;
Owned = 1;
return (w == -1) ? Capacity - 1 : w;
}
int Str::setf_nogrow(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
int len = setfv_nogrow(fmt, args);
va_end(args);
return len;
}
int Str::append_from(int idx, char c)
{
int add_len = 1;
if (Capacity < idx + add_len + 1)
reserve(idx + add_len + 1);
Data[idx] = c;
Data[idx + add_len] = 0;
STR_ASSERT(Owned);
return add_len;
}
int Str::append_from(int idx, const char* s, const char* s_end)
{
if (!s_end)
s_end = s + strlen(s);
int add_len = (int)(s_end - s);
if (Capacity < idx + add_len + 1)
reserve(idx + add_len + 1);
memcpy(Data + idx, (const void*)s, (size_t)add_len);
Data[idx + add_len] = 0; // Our source data isn't necessarily zero-terminated
STR_ASSERT(Owned);
return add_len;
}
// FIXME: merge setfv() and appendfv()?
int Str::appendfv_from(int idx, const char* fmt, va_list args)
{
// Needed for portability on platforms where va_list are passed by reference and modified by functions
va_list args2;
va_copy(args2, args);
// MSVC returns -1 on overflow when writing, which forces us to do two passes
// FIXME-OPT: Find a way around that.
#ifdef _MSC_VER
int add_len = vsnprintf(NULL, 0, fmt, args);
STR_ASSERT(add_len >= 0);
if (Capacity < idx + add_len + 1)
reserve(idx + add_len + 1);
add_len = vsnprintf(Data + idx, add_len + 1, fmt, args2);
#else
// First try
int add_len = vsnprintf(Owned ? Data + idx : NULL, Owned ? (size_t)(Capacity - idx) : 0, fmt, args);
STR_ASSERT(add_len >= 0);
if (Capacity < idx + add_len + 1)
{
reserve(idx + add_len + 1);
add_len = vsnprintf(Data + idx, (size_t)add_len + 1, fmt, args2);
}
#endif
STR_ASSERT(Owned);
return add_len;
}
int Str::appendf_from(int idx, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
int len = appendfv_from(idx, fmt, args);
va_end(args);
return len;
}
int Str::append(char c)
{
int cur_len = length();
return append_from(cur_len, c);
}
int Str::append(const char* s, const char* s_end)
{
int cur_len = length();
return append_from(cur_len, s, s_end);
}
int Str::appendfv(const char* fmt, va_list args)
{
int cur_len = length();
return appendfv_from(cur_len, fmt, args);
}
int Str::appendf(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
int len = appendfv(fmt, args);
va_end(args);
return len;
}
#endif // #define STR_IMPLEMENTATION
//-------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,692 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
/*
* Parts of this library are derived by:
*
* Posix Threads library for Microsoft Windows
*
* Use at own risk, there is no implied warranty to this code.
* It uses undocumented features of Microsoft Windows that can change
* at any time in the future.
*
* (C) 2010 Lockless Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Lockless Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AN
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef WIN_PTHREADS_H
#define WIN_PTHREADS_H
#include <stddef.h>
#include <errno.h>
#include <sys/types.h>
#include <process.h>
#include <limits.h>
#include <signal.h>
#include <time.h>
#include <sys/timeb.h>
#include "pthread_compat.h"
#ifdef __cplusplus
extern "C" {
#endif
#define __WINPTHREADS_VERSION_MAJOR 0
#define __WINPTHREADS_VERSION_MINOR 5
#define __WINPTHREADS_VERSION_PATCHLEVEL 0
/* MSB 8-bit major version, 8-bit minor version, 16-bit patch level. */
#define __WINPTHREADS_VERSION 0x00050000
#if defined(IN_WINPTHREAD)
# if defined(DLL_EXPORT)
# define WINPTHREAD_API __declspec(dllexport) /* building the DLL */
# else
# define WINPTHREAD_API /* building the static library */
# endif
#else
# if defined(WINPTHREADS_USE_DLLIMPORT)
# define WINPTHREAD_API __declspec(dllimport) /* user wants explicit `dllimport` */
# else
# define WINPTHREAD_API /* the default; auto imported in case of DLL */
# endif
#endif
/* #define WINPTHREAD_DBG 1 */
/* Compatibility stuff: */
#define RWLS_PER_THREAD 8
/* Error-codes. */
#ifndef ETIMEDOUT
#define ETIMEDOUT 138
#endif
#ifndef ENOTSUP
#define ENOTSUP 129
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK 140
#endif
/* pthread specific defines. */
#define PTHREAD_CANCEL_DISABLE 0
#define PTHREAD_CANCEL_ENABLE 0x01
#define PTHREAD_CANCEL_DEFERRED 0
#define PTHREAD_CANCEL_ASYNCHRONOUS 0x02
#define PTHREAD_CREATE_JOINABLE 0
#define PTHREAD_CREATE_DETACHED 0x04
#define PTHREAD_EXPLICIT_SCHED 0
#define PTHREAD_INHERIT_SCHED 0x08
#define PTHREAD_SCOPE_PROCESS 0
#define PTHREAD_SCOPE_SYSTEM 0x10
#define PTHREAD_DEFAULT_ATTR (PTHREAD_CANCEL_ENABLE)
#define PTHREAD_CANCELED ((void *) (intptr_t) 0xDEADBEEF)
#define _PTHREAD_NULL_THREAD ((pthread_t) 0)
#define PTHREAD_ONCE_INIT 0
#define PTHREAD_DESTRUCTOR_ITERATIONS 256
#define PTHREAD_KEYS_MAX (1<<20)
#define PTHREAD_MUTEX_NORMAL 0
#define PTHREAD_MUTEX_ERRORCHECK 1
#define PTHREAD_MUTEX_RECURSIVE 2
#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL
#define PTHREAD_MUTEX_SHARED 1
#define PTHREAD_MUTEX_PRIVATE 0
#define PTHREAD_PRIO_NONE 0
#define PTHREAD_PRIO_INHERIT 8
#define PTHREAD_PRIO_PROTECT 16
#define PTHREAD_PRIO_MULT 32
#define PTHREAD_PROCESS_SHARED 1
#define PTHREAD_PROCESS_PRIVATE 0
#define PTHREAD_MUTEX_FAST_NP PTHREAD_MUTEX_NORMAL
#define PTHREAD_MUTEX_TIMED_NP PTHREAD_MUTEX_FAST_NP
#define PTHREAD_MUTEX_ADAPTIVE_NP PTHREAD_MUTEX_FAST_NP
#define PTHREAD_MUTEX_ERRORCHECK_NP PTHREAD_MUTEX_ERRORCHECK
#define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
WINPTHREAD_API void * pthread_timechange_handler_np(void * dummy);
WINPTHREAD_API int pthread_delay_np (const struct timespec *interval);
WINPTHREAD_API int pthread_num_processors_np(void);
WINPTHREAD_API int pthread_set_num_processors_np(int n);
#define PTHREAD_BARRIER_SERIAL_THREAD 1
/* maximum number of times a read lock may be obtained */
#define MAX_READ_LOCKS (INT_MAX - 1)
/* No fork() in windows - so ignore this */
#define pthread_atfork(F1,F2,F3) 0
/* unsupported stuff: */
#define pthread_mutex_getprioceiling(M, P) ENOTSUP
#define pthread_mutex_setprioceiling(M, P) ENOTSUP
#define pthread_getcpuclockid(T, C) ENOTSUP
#define pthread_attr_getguardsize(A, S) ENOTSUP
#define pthread_attr_setgaurdsize(A, S) ENOTSUP
typedef long pthread_once_t;
typedef unsigned pthread_mutexattr_t;
typedef unsigned pthread_key_t;
typedef void *pthread_barrierattr_t;
typedef int pthread_condattr_t;
typedef int pthread_rwlockattr_t;
/*
struct _pthread_v;
typedef struct pthread_t {
struct _pthread_v *p;
int x;
} pthread_t;
*/
typedef uintptr_t pthread_t;
typedef struct _pthread_cleanup _pthread_cleanup;
struct _pthread_cleanup
{
void (*func)(void *);
void *arg;
_pthread_cleanup *next;
};
#define pthread_cleanup_push(F, A) \
do { \
const _pthread_cleanup _pthread_cup = \
{ (F), (A), *pthread_getclean() }; \
MemoryBarrier(); \
*pthread_getclean() = (_pthread_cleanup *) &_pthread_cup; \
MemoryBarrier(); \
do { \
do {} while (0)
/* Note that if async cancelling is used, then there is a race here */
#define pthread_cleanup_pop(E) \
} while (0); \
*pthread_getclean() = _pthread_cup.next; \
if ((E)) _pthread_cup.func((pthread_once_t *)_pthread_cup.arg); \
} while (0)
#ifndef SCHED_OTHER
/* Some POSIX realtime extensions, mostly stubbed */
#define SCHED_OTHER 0
#define SCHED_FIFO 1
#define SCHED_RR 2
#define SCHED_MIN SCHED_OTHER
#define SCHED_MAX SCHED_RR
struct sched_param {
int sched_priority;
};
WINPTHREAD_API int sched_yield(void);
WINPTHREAD_API int sched_get_priority_min(int pol);
WINPTHREAD_API int sched_get_priority_max(int pol);
WINPTHREAD_API int sched_getscheduler(pid_t pid);
WINPTHREAD_API int sched_setscheduler(pid_t pid, int pol, const struct sched_param *param);
#endif
typedef struct pthread_attr_t pthread_attr_t;
struct pthread_attr_t
{
unsigned p_state;
void *stack;
size_t s_size;
struct sched_param param;
};
WINPTHREAD_API int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);
WINPTHREAD_API int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
WINPTHREAD_API int pthread_getschedparam(pthread_t thread, int *pol, struct sched_param *param);
WINPTHREAD_API int pthread_setschedparam(pthread_t thread, int pol, const struct sched_param *param);
WINPTHREAD_API int pthread_attr_setschedpolicy (pthread_attr_t *attr, int pol);
WINPTHREAD_API int pthread_attr_getschedpolicy (const pthread_attr_t *attr, int *pol);
/* synchronization objects */
typedef intptr_t pthread_spinlock_t;
typedef intptr_t pthread_mutex_t;
typedef intptr_t pthread_cond_t;
typedef intptr_t pthread_rwlock_t;
typedef void *pthread_barrier_t;
#define PTHREAD_MUTEX_NORMAL 0
#define PTHREAD_MUTEX_ERRORCHECK 1
#define PTHREAD_MUTEX_RECURSIVE 2
#define GENERIC_INITIALIZER -1
#define GENERIC_ERRORCHECK_INITIALIZER -2
#define GENERIC_RECURSIVE_INITIALIZER -3
#define GENERIC_NORMAL_INITIALIZER -1
#define PTHREAD_MUTEX_INITIALIZER (pthread_mutex_t)GENERIC_INITIALIZER
#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER (pthread_mutex_t)GENERIC_RECURSIVE_INITIALIZER
#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER (pthread_mutex_t)GENERIC_ERRORCHECK_INITIALIZER
#define PTHREAD_NORMAL_MUTEX_INITIALIZER (pthread_mutex_t)GENERIC_NORMAL_INITIALIZER
#define PTHREAD_DEFAULT_MUTEX_INITIALIZER PTHREAD_NORMAL_MUTEX_INITIALIZER
#define PTHREAD_COND_INITIALIZER (pthread_cond_t)GENERIC_INITIALIZER
#define PTHREAD_RWLOCK_INITIALIZER (pthread_rwlock_t)GENERIC_INITIALIZER
#define PTHREAD_SPINLOCK_INITIALIZER (pthread_spinlock_t)GENERIC_INITIALIZER
WINPTHREAD_API extern void (**_pthread_key_dest)(void *);
WINPTHREAD_API int pthread_key_create(pthread_key_t *key, void (* dest)(void *));
WINPTHREAD_API int pthread_key_delete(pthread_key_t key);
WINPTHREAD_API void * pthread_getspecific(pthread_key_t key);
WINPTHREAD_API int pthread_setspecific(pthread_key_t key, const void *value);
WINPTHREAD_API pthread_t pthread_self(void);
WINPTHREAD_API int pthread_once(pthread_once_t *o, void (*func)(void));
WINPTHREAD_API void pthread_testcancel(void);
WINPTHREAD_API int pthread_equal(pthread_t t1, pthread_t t2);
WINPTHREAD_API void pthread_tls_init(void);
WINPTHREAD_API void _pthread_cleanup_dest(pthread_t t);
WINPTHREAD_API int pthread_get_concurrency(int *val);
WINPTHREAD_API int pthread_set_concurrency(int val);
WINPTHREAD_API void pthread_exit(void *res);
WINPTHREAD_API void _pthread_invoke_cancel(void);
WINPTHREAD_API int pthread_cancel(pthread_t t);
WINPTHREAD_API int pthread_kill(pthread_t t, int sig);
WINPTHREAD_API unsigned _pthread_get_state(const pthread_attr_t *attr, unsigned flag);
WINPTHREAD_API int _pthread_set_state(pthread_attr_t *attr, unsigned flag, unsigned val);
WINPTHREAD_API int pthread_setcancelstate(int state, int *oldstate);
WINPTHREAD_API int pthread_setcanceltype(int type, int *oldtype);
WINPTHREAD_API unsigned __stdcall pthread_create_wrapper(void *args);
WINPTHREAD_API int pthread_create(pthread_t *th, const pthread_attr_t *attr, void *(* func)(void *), void *arg);
WINPTHREAD_API int pthread_join(pthread_t t, void **res);
WINPTHREAD_API int pthread_detach(pthread_t t);
WINPTHREAD_API int pthread_setname_np(pthread_t thread, const char *name);
WINPTHREAD_API int pthread_getname_np(pthread_t thread, char *name, size_t len);
WINPTHREAD_API int pthread_rwlock_init(pthread_rwlock_t *rwlock_, const pthread_rwlockattr_t *attr);
WINPTHREAD_API int pthread_rwlock_wrlock(pthread_rwlock_t *l);
WINPTHREAD_API int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, const struct timespec *ts);
WINPTHREAD_API int pthread_rwlock_rdlock(pthread_rwlock_t *l);
WINPTHREAD_API int pthread_rwlock_timedrdlock(pthread_rwlock_t *l, const struct timespec *ts);
WINPTHREAD_API int pthread_rwlock_unlock(pthread_rwlock_t *l);
WINPTHREAD_API int pthread_rwlock_tryrdlock(pthread_rwlock_t *l);
WINPTHREAD_API int pthread_rwlock_trywrlock(pthread_rwlock_t *l);
WINPTHREAD_API int pthread_rwlock_destroy (pthread_rwlock_t *l);
WINPTHREAD_API int pthread_cond_init(pthread_cond_t *cv, const pthread_condattr_t *a);
WINPTHREAD_API int pthread_cond_destroy(pthread_cond_t *cv);
WINPTHREAD_API int pthread_cond_signal (pthread_cond_t *cv);
WINPTHREAD_API int pthread_cond_broadcast (pthread_cond_t *cv);
WINPTHREAD_API int pthread_cond_wait (pthread_cond_t *cv, pthread_mutex_t *external_mutex);
WINPTHREAD_API int pthread_cond_timedwait(pthread_cond_t *cv, pthread_mutex_t *external_mutex, const struct timespec *t);
WINPTHREAD_API int pthread_cond_timedwait_relative_np(pthread_cond_t *cv, pthread_mutex_t *external_mutex, const struct timespec *t);
WINPTHREAD_API int pthread_mutex_lock(pthread_mutex_t *m);
WINPTHREAD_API int pthread_mutex_timedlock(pthread_mutex_t *m, const struct timespec *ts);
WINPTHREAD_API int pthread_mutex_unlock(pthread_mutex_t *m);
WINPTHREAD_API int pthread_mutex_trylock(pthread_mutex_t *m);
WINPTHREAD_API int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a);
WINPTHREAD_API int pthread_mutex_destroy(pthread_mutex_t *m);
WINPTHREAD_API int pthread_barrier_destroy(pthread_barrier_t *b);
WINPTHREAD_API int pthread_barrier_init(pthread_barrier_t *b, const void *attr, unsigned int count);
WINPTHREAD_API int pthread_barrier_wait(pthread_barrier_t *b);
WINPTHREAD_API int pthread_spin_init(pthread_spinlock_t *l, int pshared);
WINPTHREAD_API int pthread_spin_destroy(pthread_spinlock_t *l);
/* No-fair spinlock due to lack of knowledge of thread number. */
WINPTHREAD_API int pthread_spin_lock(pthread_spinlock_t *l);
WINPTHREAD_API int pthread_spin_trylock(pthread_spinlock_t *l);
WINPTHREAD_API int pthread_spin_unlock(pthread_spinlock_t *l);
WINPTHREAD_API int pthread_attr_init(pthread_attr_t *attr);
WINPTHREAD_API int pthread_attr_destroy(pthread_attr_t *attr);
WINPTHREAD_API int pthread_attr_setdetachstate(pthread_attr_t *a, int flag);
WINPTHREAD_API int pthread_attr_getdetachstate(const pthread_attr_t *a, int *flag);
WINPTHREAD_API int pthread_attr_setinheritsched(pthread_attr_t *a, int flag);
WINPTHREAD_API int pthread_attr_getinheritsched(const pthread_attr_t *a, int *flag);
WINPTHREAD_API int pthread_attr_setscope(pthread_attr_t *a, int flag);
WINPTHREAD_API int pthread_attr_getscope(const pthread_attr_t *a, int *flag);
WINPTHREAD_API int pthread_attr_getstack(const pthread_attr_t *attr, void **stack, size_t *size);
WINPTHREAD_API int pthread_attr_setstack(pthread_attr_t *attr, void *stack, size_t size);
WINPTHREAD_API int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stack);
WINPTHREAD_API int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stack);
WINPTHREAD_API int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *size);
WINPTHREAD_API int pthread_attr_setstacksize(pthread_attr_t *attr, size_t size);
WINPTHREAD_API int pthread_mutexattr_init(pthread_mutexattr_t *a);
WINPTHREAD_API int pthread_mutexattr_destroy(pthread_mutexattr_t *a);
WINPTHREAD_API int pthread_mutexattr_gettype(const pthread_mutexattr_t *a, int *type);
WINPTHREAD_API int pthread_mutexattr_settype(pthread_mutexattr_t *a, int type);
WINPTHREAD_API int pthread_mutexattr_getpshared(const pthread_mutexattr_t *a, int *type);
WINPTHREAD_API int pthread_mutexattr_setpshared(pthread_mutexattr_t * a, int type);
WINPTHREAD_API int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *a, int *type);
WINPTHREAD_API int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int type);
WINPTHREAD_API int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *a, int * prio);
WINPTHREAD_API int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *a, int prio);
WINPTHREAD_API int pthread_getconcurrency(void);
WINPTHREAD_API int pthread_setconcurrency(int new_level);
WINPTHREAD_API int pthread_condattr_destroy(pthread_condattr_t *a);
WINPTHREAD_API int pthread_condattr_init(pthread_condattr_t *a);
WINPTHREAD_API int pthread_condattr_getpshared(const pthread_condattr_t *a, int *s);
WINPTHREAD_API int pthread_condattr_setpshared(pthread_condattr_t *a, int s);
#ifndef __clockid_t_defined
typedef int clockid_t;
#define __clockid_t_defined 1
#endif /* __clockid_t_defined */
WINPTHREAD_API int pthread_condattr_getclock (const pthread_condattr_t *attr,
clockid_t *clock_id);
WINPTHREAD_API int pthread_condattr_setclock(pthread_condattr_t *attr,
clockid_t clock_id);
WINPTHREAD_API int __pthread_clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp, struct timespec *rmtp);
WINPTHREAD_API int pthread_barrierattr_init(void **attr);
WINPTHREAD_API int pthread_barrierattr_destroy(void **attr);
WINPTHREAD_API int pthread_barrierattr_setpshared(void **attr, int s);
WINPTHREAD_API int pthread_barrierattr_getpshared(void **attr, int *s);
/* Private extensions for analysis and internal use. */
WINPTHREAD_API struct _pthread_cleanup ** pthread_getclean (void);
WINPTHREAD_API void * pthread_gethandle (pthread_t t);
WINPTHREAD_API void * pthread_getevent (void);
WINPTHREAD_API unsigned long long _pthread_rel_time_in_ms(const struct timespec *ts);
WINPTHREAD_API unsigned long long _pthread_time_in_ms(void);
WINPTHREAD_API unsigned long long _pthread_time_in_ms_from_timespec(const struct timespec *ts);
WINPTHREAD_API int _pthread_tryjoin (pthread_t t, void **res);
WINPTHREAD_API int pthread_rwlockattr_destroy(pthread_rwlockattr_t *a);
WINPTHREAD_API int pthread_rwlockattr_getpshared(pthread_rwlockattr_t *a, int *s);
WINPTHREAD_API int pthread_rwlockattr_init(pthread_rwlockattr_t *a);
WINPTHREAD_API int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *a, int s);
#ifndef SIG_BLOCK
#define SIG_BLOCK 0
#endif
#ifndef SIG_UNBLOCK
#define SIG_UNBLOCK 1
#endif
#ifndef SIG_SETMASK
#define SIG_SETMASK 2
#endif
#include <pthread_unistd.h>
#undef _POSIX_THREAD_DESTRUCTOR_ITERATIONS
#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS
#undef _POSIX_THREAD_KEYS_MAX
#define _POSIX_THREAD_KEYS_MAX PTHREAD_KEYS_MAX
#undef PTHREAD_THREADS_MAX
#define PTHREAD_THREADS_MAX 2019
#undef _POSIX_SEM_NSEMS_MAX
#define _POSIX_SEM_NSEMS_MAX 256
#undef SEM_NSEMS_MAX
#define SEM_NSEMS_MAX 1024
/* Wrap cancellation points. */
#if defined(__WINPTHREAD_ENABLE_WRAP_API) \
|| defined(__WINPTRHEAD_ENABLE_WRAP_API) /* historical typo */
#define accept(...) (pthread_testcancel(), accept(__VA_ARGS__))
#define aio_suspend(...) (pthread_testcancel(), aio_suspend(__VA_ARGS__))
#define clock_nanosleep(...) (pthread_testcancel(), clock_nanosleep(__VA_ARGS__))
#define close(...) (pthread_testcancel(), close(__VA_ARGS__))
#define connect(...) (pthread_testcancel(), connect(__VA_ARGS__))
#define creat(...) (pthread_testcancel(), creat(__VA_ARGS__))
#define fcntl(...) (pthread_testcancel(), fcntl(__VA_ARGS__))
#define fdatasync(...) (pthread_testcancel(), fdatasync(__VA_ARGS__))
#define fsync(...) (pthread_testcancel(), fsync(__VA_ARGS__))
#define getmsg(...) (pthread_testcancel(), getmsg(__VA_ARGS__))
#define getpmsg(...) (pthread_testcancel(), getpmsg(__VA_ARGS__))
#define lockf(...) (pthread_testcancel(), lockf(__VA_ARGS__))
#define mg_receive(...) (pthread_testcancel(), mg_receive(__VA_ARGS__))
#define mg_send(...) (pthread_testcancel(), mg_send(__VA_ARGS__))
#define mg_timedreceive(...) (pthread_testcancel(), mg_timedreceive(__VA_ARGS__))
#define mg_timessend(...) (pthread_testcancel(), mg_timedsend(__VA_ARGS__))
#define msgrcv(...) (pthread_testcancel(), msgrecv(__VA_ARGS__))
#define msgsnd(...) (pthread_testcancel(), msgsnd(__VA_ARGS__))
#define msync(...) (pthread_testcancel(), msync(__VA_ARGS__))
#define nanosleep(...) (pthread_testcancel(), nanosleep(__VA_ARGS__))
#define open(...) (pthread_testcancel(), open(__VA_ARGS__))
#define pause(...) (pthread_testcancel(), pause(__VA_ARGS__))
#define poll(...) (pthread_testcancel(), poll(__VA_ARGS__))
#define pread(...) (pthread_testcancel(), pread(__VA_ARGS__))
#define pselect(...) (pthread_testcancel(), pselect(__VA_ARGS__))
#define putmsg(...) (pthread_testcancel(), putmsg(__VA_ARGS__))
#define putpmsg(...) (pthread_testcancel(), putpmsg(__VA_ARGS__))
#define pwrite(...) (pthread_testcancel(), pwrite(__VA_ARGS__))
#define read(...) (pthread_testcancel(), read(__VA_ARGS__))
#define readv(...) (pthread_testcancel(), readv(__VA_ARGS__))
#define recv(...) (pthread_testcancel(), recv(__VA_ARGS__))
#define recvfrom(...) (pthread_testcancel(), recvfrom(__VA_ARGS__))
#define recvmsg(...) (pthread_testcancel(), recvmsg(__VA_ARGS__))
#define select(...) (pthread_testcancel(), select(__VA_ARGS__))
#define sem_timedwait(...) (pthread_testcancel(), sem_timedwait(__VA_ARGS__))
#define sem_wait(...) (pthread_testcancel(), sem_wait(__VA_ARGS__))
#define send(...) (pthread_testcancel(), send(__VA_ARGS__))
#define sendmsg(...) (pthread_testcancel(), sendmsg(__VA_ARGS__))
#define sendto(...) (pthread_testcancel(), sendto(__VA_ARGS__))
#define sigpause(...) (pthread_testcancel(), sigpause(__VA_ARGS__))
#define sigsuspend(...) (pthread_testcancel(), sigsuspend(__VA_ARGS__))
#define sigwait(...) (pthread_testcancel(), sigwait(__VA_ARGS__))
#define sigwaitinfo(...) (pthread_testcancel(), sigwaitinfo(__VA_ARGS__))
#define sleep(...) (pthread_testcancel(), sleep(__VA_ARGS__))
//#define Sleep(...) (pthread_testcancel(), Sleep(__VA_ARGS__))
#define system(...) (pthread_testcancel(), system(__VA_ARGS__))
#define access(...) (pthread_testcancel(), access(__VA_ARGS__))
#define asctime(...) (pthread_testcancel(), asctime(__VA_ARGS__))
#define catclose(...) (pthread_testcancel(), catclose(__VA_ARGS__))
#define catgets(...) (pthread_testcancel(), catgets(__VA_ARGS__))
#define catopen(...) (pthread_testcancel(), catopen(__VA_ARGS__))
#define closedir(...) (pthread_testcancel(), closedir(__VA_ARGS__))
#define closelog(...) (pthread_testcancel(), closelog(__VA_ARGS__))
#define ctermid(...) (pthread_testcancel(), ctermid(__VA_ARGS__))
#define ctime(...) (pthread_testcancel(), ctime(__VA_ARGS__))
#define dbm_close(...) (pthread_testcancel(), dbm_close(__VA_ARGS__))
#define dbm_delete(...) (pthread_testcancel(), dbm_delete(__VA_ARGS__))
#define dbm_fetch(...) (pthread_testcancel(), dbm_fetch(__VA_ARGS__))
#define dbm_nextkey(...) (pthread_testcancel(), dbm_nextkey(__VA_ARGS__))
#define dbm_open(...) (pthread_testcancel(), dbm_open(__VA_ARGS__))
#define dbm_store(...) (pthread_testcancel(), dbm_store(__VA_ARGS__))
#define dlclose(...) (pthread_testcancel(), dlclose(__VA_ARGS__))
#define dlopen(...) (pthread_testcancel(), dlopen(__VA_ARGS__))
#define endgrent(...) (pthread_testcancel(), endgrent(__VA_ARGS__))
#define endhostent(...) (pthread_testcancel(), endhostent(__VA_ARGS__))
#define endnetent(...) (pthread_testcancel(), endnetent(__VA_ARGS__))
#define endprotoent(...) (pthread_testcancel(), endprotoend(__VA_ARGS__))
#define endpwent(...) (pthread_testcancel(), endpwent(__VA_ARGS__))
#define endservent(...) (pthread_testcancel(), endservent(__VA_ARGS__))
#define endutxent(...) (pthread_testcancel(), endutxent(__VA_ARGS__))
#define fclose(...) (pthread_testcancel(), fclose(__VA_ARGS__))
#define fflush(...) (pthread_testcancel(), fflush(__VA_ARGS__))
#define fgetc(...) (pthread_testcancel(), fgetc(__VA_ARGS__))
#define fgetpos(...) (pthread_testcancel(), fgetpos(__VA_ARGS__))
#define fgets(...) (pthread_testcancel(), fgets(__VA_ARGS__))
#define fgetwc(...) (pthread_testcancel(), fgetwc(__VA_ARGS__))
#define fgetws(...) (pthread_testcancel(), fgetws(__VA_ARGS__))
#define fmtmsg(...) (pthread_testcancel(), fmtmsg(__VA_ARGS__))
#define fopen(...) (pthread_testcancel(), fopen(__VA_ARGS__))
#define fpathconf(...) (pthread_testcancel(), fpathconf(__VA_ARGS__))
#define fprintf(...) (pthread_testcancel(), fprintf(__VA_ARGS__))
#define fputc(...) (pthread_testcancel(), fputc(__VA_ARGS__))
#define fputs(...) (pthread_testcancel(), fputs(__VA_ARGS__))
#define fputwc(...) (pthread_testcancel(), fputwc(__VA_ARGS__))
#define fputws(...) (pthread_testcancel(), fputws(__VA_ARGS__))
#define fread(...) (pthread_testcancel(), fread(__VA_ARGS__))
#define freopen(...) (pthread_testcancel(), freopen(__VA_ARGS__))
#define fscanf(...) (pthread_testcancel(), fscanf(__VA_ARGS__))
#define fseek(...) (pthread_testcancel(), fseek(__VA_ARGS__))
#define fseeko(...) (pthread_testcancel(), fseeko(__VA_ARGS__))
#define fsetpos(...) (pthread_testcancel(), fsetpos(__VA_ARGS__))
#define fstat(...) (pthread_testcancel(), fstat(__VA_ARGS__))
#define ftell(...) (pthread_testcancel(), ftell(__VA_ARGS__))
#define ftello(...) (pthread_testcancel(), ftello(__VA_ARGS__))
#define ftw(...) (pthread_testcancel(), ftw(__VA_ARGS__))
#define fwprintf(...) (pthread_testcancel(), fwprintf(__VA_ARGS__))
#define fwrite(...) (pthread_testcancel(), fwrite(__VA_ARGS__))
#define fwscanf(...) (pthread_testcancel(), fwscanf(__VA_ARGS__))
#define getaddrinfo(...) (pthread_testcancel(), getaddrinfo(__VA_ARGS__))
#define getc(...) (pthread_testcancel(), getc(__VA_ARGS__))
#define getc_unlocked(...) (pthread_testcancel(), getc_unlocked(__VA_ARGS__))
#define getchar(...) (pthread_testcancel(), getchar(__VA_ARGS__))
#define getchar_unlocked(...) (pthread_testcancel(), getchar_unlocked(__VA_ARGS__))
#define getcwd(...) (pthread_testcancel(), getcwd(__VA_ARGS__))
#define getdate(...) (pthread_testcancel(), getdate(__VA_ARGS__))
#define getgrent(...) (pthread_testcancel(), getgrent(__VA_ARGS__))
#define getgrgid(...) (pthread_testcancel(), getgrgid(__VA_ARGS__))
#define getgrgid_r(...) (pthread_testcancel(), getgrgid_r(__VA_ARGS__))
#define gergrnam(...) (pthread_testcancel(), getgrnam(__VA_ARGS__))
#define getgrnam_r(...) (pthread_testcancel(), getgrnam_r(__VA_ARGS__))
#define gethostbyaddr(...) (pthread_testcancel(), gethostbyaddr(__VA_ARGS__))
#define gethostbyname(...) (pthread_testcancel(), gethostbyname(__VA_ARGS__))
#define gethostent(...) (pthread_testcancel(), gethostent(__VA_ARGS__))
#define gethostid(...) (pthread_testcancel(), gethostid(__VA_ARGS__))
#define gethostname(...) (pthread_testcancel(), gethostname(__VA_ARGS__))
#define getlogin(...) (pthread_testcancel(), getlogin(__VA_ARGS__))
#define getlogin_r(...) (pthread_testcancel(), getlogin_r(__VA_ARGS__))
#define getnameinfo(...) (pthread_testcancel(), getnameinfo(__VA_ARGS__))
#define getnetbyaddr(...) (pthread_testcancel(), getnetbyaddr(__VA_ARGS__))
#define getnetbyname(...) (pthread_testcancel(), getnetbyname(__VA_ARGS__))
#define getnetent(...) (pthread_testcancel(), getnetent(__VA_ARGS__))
#define getopt(...) (pthread_testcancel(), getopt(__VA_ARGS__))
#define getprotobyname(...) (pthread_testcancel(), getprotobyname(__VA_ARGS__))
#define getprotobynumber(...) (pthread_testcancel(), getprotobynumber(__VA_ARGS__))
#define getprotoent(...) (pthread_testcancel(), getprotoent(__VA_ARGS__))
#define getpwent(...) (pthread_testcancel(), getpwent(__VA_ARGS__))
#define getpwnam(...) (pthread_testcancel(), getpwnam(__VA_ARGS__))
#define getpwnam_r(...) (pthread_testcancel(), getpwnam_r(__VA_ARGS__))
#define getpwuid(...) (pthread_testcancel(), getpwuid(__VA_ARGS__))
#define getpwuid_r(...) (pthread_testcancel(), getpwuid_r(__VA_ARGS__))
#define gets(...) (pthread_testcancel(), gets(__VA_ARGS__))
#define getservbyname(...) (pthread_testcancel(), getservbyname(__VA_ARGS__))
#define getservbyport(...) (pthread_testcancel(), getservbyport(__VA_ARGS__))
#define getservent(...) (pthread_testcancel(), getservent(__VA_ARGS__))
#define getutxent(...) (pthread_testcancel(), getutxent(__VA_ARGS__))
#define getutxid(...) (pthread_testcancel(), getutxid(__VA_ARGS__))
#define getutxline(...) (pthread_testcancel(), getutxline(__VA_ARGS__))
#undef getwc
#define getwc(...) (pthread_testcancel(), getwc(__VA_ARGS__))
#undef getwchar
#define getwchar(...) (pthread_testcancel(), getwchar(__VA_ARGS__))
#define getwd(...) (pthread_testcancel(), getwd(__VA_ARGS__))
#define glob(...) (pthread_testcancel(), glob(__VA_ARGS__))
#define iconv_close(...) (pthread_testcancel(), iconv_close(__VA_ARGS__))
#define iconv_open(...) (pthread_testcancel(), iconv_open(__VA_ARGS__))
#define ioctl(...) (pthread_testcancel(), ioctl(__VA_ARGS__))
#define link(...) (pthread_testcancel(), link(__VA_ARGS__))
#define localtime(...) (pthread_testcancel(), localtime(__VA_ARGS__))
#define lseek(...) (pthread_testcancel(), lseek(__VA_ARGS__))
#define lstat(...) (pthread_testcancel(), lstat(__VA_ARGS__))
#define mkstemp(...) (pthread_testcancel(), mkstemp(__VA_ARGS__))
#define nftw(...) (pthread_testcancel(), nftw(__VA_ARGS__))
#define opendir(...) (pthread_testcancel(), opendir(__VA_ARGS__))
#define openlog(...) (pthread_testcancel(), openlog(__VA_ARGS__))
#define pathconf(...) (pthread_testcancel(), pathconf(__VA_ARGS__))
#define pclose(...) (pthread_testcancel(), pclose(__VA_ARGS__))
#define perror(...) (pthread_testcancel(), perror(__VA_ARGS__))
#define popen(...) (pthread_testcancel(), popen(__VA_ARGS__))
#define posix_fadvise(...) (pthread_testcancel(), posix_fadvise(__VA_ARGS__))
#define posix_fallocate(...) (pthread_testcancel(), posix_fallocate(__VA_ARGS__))
#define posix_madvise(...) (pthread_testcancel(), posix_madvise(__VA_ARGS__))
#define posix_openpt(...) (pthread_testcancel(), posix_openpt(__VA_ARGS__))
#define posix_spawn(...) (pthread_testcancel(), posix_spawn(__VA_ARGS__))
#define posix_spawnp(...) (pthread_testcancel(), posix_spawnp(__VA_ARGS__))
#define posix_trace_clear(...) (pthread_testcancel(), posix_trace_clear(__VA_ARGS__))
#define posix_trace_close(...) (pthread_testcancel(), posix_trace_close(__VA_ARGS__))
#define posix_trace_create(...) (pthread_testcancel(), posix_trace_create(__VA_ARGS__))
#define posix_trace_create_withlog(...) (pthread_testcancel(), posix_trace_create_withlog(__VA_ARGS__))
#define posix_trace_eventtypelist_getne(...) (pthread_testcancel(), posix_trace_eventtypelist_getne(__VA_ARGS__))
#define posix_trace_eventtypelist_rewin(...) (pthread_testcancel(), posix_trace_eventtypelist_rewin(__VA_ARGS__))
#define posix_trace_flush(...) (pthread_testcancel(), posix_trace_flush(__VA_ARGS__))
#define posix_trace_get_attr(...) (pthread_testcancel(), posix_trace_get_attr(__VA_ARGS__))
#define posix_trace_get_filter(...) (pthread_testcancel(), posix_trace_get_filter(__VA_ARGS__))
#define posix_trace_get_status(...) (pthread_testcancel(), posix_trace_get_status(__VA_ARGS__))
#define posix_trace_getnext_event(...) (pthread_testcancel(), posix_trace_getnext_event(__VA_ARGS__))
#define posix_trace_open(...) (pthread_testcancel(), posix_trace_open(__VA_ARGS__))
#define posix_trace_rewind(...) (pthread_testcancel(), posix_trace_rewind(__VA_ARGS__))
#define posix_trace_setfilter(...) (pthread_testcancel(), posix_trace_setfilter(__VA_ARGS__))
#define posix_trace_shutdown(...) (pthread_testcancel(), posix_trace_shutdown(__VA_ARGS__))
#define posix_trace_timedgetnext_event(...) (pthread_testcancel(), posix_trace_timedgetnext_event(__VA_ARGS__))
#define posix_typed_mem_open(...) (pthread_testcancel(), posix_typed_mem_open(__VA_ARGS__))
#define printf(...) (pthread_testcancel(), printf(__VA_ARGS__))
#define putc(...) (pthread_testcancel(), putc(__VA_ARGS__))
#define putc_unlocked(...) (pthread_testcancel(), putc_unlocked(__VA_ARGS__))
#define putchar(...) (pthread_testcancel(), putchar(__VA_ARGS__))
#define putchar_unlocked(...) (pthread_testcancel(), putchar_unlocked(__VA_ARGS__))
#define puts(...) (pthread_testcancel(), puts(__VA_ARGS__))
#define pututxline(...) (pthread_testcancel(), pututxline(__VA_ARGS__))
#undef putwc
#define putwc(...) (pthread_testcancel(), putwc(__VA_ARGS__))
#undef putwchar
#define putwchar(...) (pthread_testcancel(), putwchar(__VA_ARGS__))
#define readdir(...) (pthread_testcancel(), readdir(__VA_ARSG__))
#define readdir_r(...) (pthread_testcancel(), readdir_r(__VA_ARGS__))
#define remove(...) (pthread_testcancel(), remove(__VA_ARGS__))
#define rename(...) (pthread_testcancel(), rename(__VA_ARGS__))
#define rewind(...) (pthread_testcancel(), rewind(__VA_ARGS__))
#define rewinddir(...) (pthread_testcancel(), rewinddir(__VA_ARGS__))
#define scanf(...) (pthread_testcancel(), scanf(__VA_ARGS__))
#define seekdir(...) (pthread_testcancel(), seekdir(__VA_ARGS__))
#define semop(...) (pthread_testcancel(), semop(__VA_ARGS__))
#define setgrent(...) (pthread_testcancel(), setgrent(__VA_ARGS__))
#define sethostent(...) (pthread_testcancel(), sethostemt(__VA_ARGS__))
#define setnetent(...) (pthread_testcancel(), setnetent(__VA_ARGS__))
#define setprotoent(...) (pthread_testcancel(), setprotoent(__VA_ARGS__))
#define setpwent(...) (pthread_testcancel(), setpwent(__VA_ARGS__))
#define setservent(...) (pthread_testcancel(), setservent(__VA_ARGS__))
#define setutxent(...) (pthread_testcancel(), setutxent(__VA_ARGS__))
#define stat(...) (pthread_testcancel(), stat(__VA_ARGS__))
#define strerror(...) (pthread_testcancel(), strerror(__VA_ARGS__))
#define strerror_r(...) (pthread_testcancel(), strerror_r(__VA_ARGS__))
#define strftime(...) (pthread_testcancel(), strftime(__VA_ARGS__))
#define symlink(...) (pthread_testcancel(), symlink(__VA_ARGS__))
#define sync(...) (pthread_testcancel(), sync(__VA_ARGS__))
#define syslog(...) (pthread_testcancel(), syslog(__VA_ARGS__))
#define tmpfile(...) (pthread_testcancel(), tmpfile(__VA_ARGS__))
#define tmpnam(...) (pthread_testcancel(), tmpnam(__VA_ARGS__))
#define ttyname(...) (pthread_testcancel(), ttyname(__VA_ARGS__))
#define ttyname_r(...) (pthread_testcancel(), ttyname_r(__VA_ARGS__))
#define tzset(...) (pthread_testcancel(), tzset(__VA_ARGS__))
#define ungetc(...) (pthread_testcancel(), ungetc(__VA_ARGS__))
#define ungetwc(...) (pthread_testcancel(), ungetwc(__VA_ARGS__))
#define unlink(...) (pthread_testcancel(), unlink(__VA_ARGS__))
#define vfprintf(...) (pthread_testcancel(), vfprintf(__VA_ARGS__))
#define vfwprintf(...) (pthread_testcancel(), vfwprintf(__VA_ARGS__))
#define vprintf(...) (pthread_testcancel(), vprintf(__VA_ARGS__))
#define vwprintf(...) (pthread_testcancel(), vwprintf(__VA_ARGS__))
#define wcsftime(...) (pthread_testcancel(), wcsftime(__VA_ARGS__))
#define wordexp(...) (pthread_testcancel(), wordexp(__VA_ARGS__))
#define wprintf(...) (pthread_testcancel(), wprintf(__VA_ARGS__))
#define wscanf(...) (pthread_testcancel(), wscanf(__VA_ARGS__))
#endif
/* We deal here with a gcc issue for posix threading on Windows.
We would need to change here gcc's gthr-posix.h header, but this
got rejected. So we deal it within this header. */
#ifdef _GTHREAD_USE_MUTEX_INIT_FUNC
#undef _GTHREAD_USE_MUTEX_INIT_FUNC
#endif
#define _GTHREAD_USE_MUTEX_INIT_FUNC 1
#ifdef __cplusplus
}
#endif
#endif /* WIN_PTHREADS_H */

View File

@ -0,0 +1,86 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
/*
* Parts of this library are derived by:
*
* Posix Threads library for Microsoft Windows
*
* Use at own risk, there is no implied warranty to this code.
* It uses undocumented features of Microsoft Windows that can change
* at any time in the future.
*
* (C) 2010 Lockless Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Lockless Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AN
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef WIN_PTHREADS_PTHREAD_COMPAT_H
#define WIN_PTHREADS_PTHREAD_COMPAT_H
#ifdef __GNUC__
#define WINPTHREADS_INLINE inline
#define WINPTHREADS_ATTRIBUTE(X) __attribute__(X)
#define WINPTHREADS_SECTION(X) __section__(X)
#elif _MSC_VER
#include "pthread_time.h"
#ifdef _WIN64
typedef __int64 pid_t;
#else
typedef int pid_t;
#endif
typedef int clockid_t;
#define WINPTHREADS_INLINE __inline
#define WINPTHREADS_ATTRIBUTE(X) __declspec X
#define WINPTHREADS_SECTION(X) allocate(X)
#endif
#endif

View File

@ -0,0 +1,29 @@
/*
Copyright (c) 2013-2016 mingw-w64 project
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.
*/
#ifndef WIN_PTHREADS_SIGNAL_H
#define WIN_PTHREADS_SIGNAL_H
/* Windows has rudimentary signals support. */
#define pthread_sigmask(H, S1, S2) 0
#endif /* WIN_PTHREADS_SIGNAL_H */

View File

@ -0,0 +1,102 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#include <sys/timeb.h>
#ifndef WIN_PTHREADS_TIME_H
#define WIN_PTHREADS_TIME_H
/* Posix timers are supported */
#ifndef _POSIX_TIMERS
#define _POSIX_TIMERS 200809L
#endif
/* Monotonic clocks are available. */
#ifndef _POSIX_MONOTONIC_CLOCK
#define _POSIX_MONOTONIC_CLOCK 200809L
#endif
/* CPU-time clocks are available. */
#ifndef _POSIX_CPUTIME
#define _POSIX_CPUTIME 200809L
#endif
/* Clock support in threads are available. */
#ifndef _POSIX_THREAD_CPUTIME
#define _POSIX_THREAD_CPUTIME 200809L
#endif
#ifndef __clockid_t_defined
typedef int clockid_t;
#define __clockid_t_defined 1
#endif /* __clockid_t_defined */
#ifndef TIMER_ABSTIME
#define TIMER_ABSTIME 1
#endif
#ifndef CLOCK_REALTIME
#define CLOCK_REALTIME 0
#endif
#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC 1
#endif
#ifndef CLOCK_PROCESS_CPUTIME_ID
#define CLOCK_PROCESS_CPUTIME_ID 2
#endif
#ifndef CLOCK_THREAD_CPUTIME_ID
#define CLOCK_THREAD_CPUTIME_ID 3
#endif
#ifndef CLOCK_REALTIME_COARSE
#define CLOCK_REALTIME_COARSE 4
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Make sure we provide default for WINPTHREAD_API, if not defined. */
#pragma push_macro("WINPTHREAD_API")
#ifndef WINPTHREAD_API
#define WINPTHREAD_API
#endif
/* These should really be dllimport'ed if using winpthread dll */
WINPTHREAD_API int __cdecl nanosleep(const struct timespec *request, struct timespec *remain);
WINPTHREAD_API int __cdecl clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request, struct timespec *remain);
WINPTHREAD_API int __cdecl clock_getres(clockid_t clock_id, struct timespec *res);
WINPTHREAD_API int __cdecl clock_gettime(clockid_t clock_id, struct timespec *tp);
WINPTHREAD_API int __cdecl clock_settime(clockid_t clock_id, const struct timespec *tp);
#pragma pop_macro("WINPTHREAD_API")
#ifdef __cplusplus
}
#endif
#endif /* WIN_PTHREADS_TIME_H */

View File

@ -0,0 +1,192 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#ifndef WIN_PTHREADS_UNISTD_H
#define WIN_PTHREADS_UNISTD_H
/* Set defines described by the POSIX Threads Extension (1003.1c-1995) */
/* _SC_THREADS
Basic support for POSIX threads is available. The functions
pthread_atfork(),
pthread_attr_destroy(),
pthread_attr_getdetachstate(),
pthread_attr_getschedparam(),
pthread_attr_init(),
pthread_attr_setdetachstate(),
pthread_attr_setschedparam(),
pthread_cancel(),
pthread_cleanup_push(),
pthread_cleanup_pop(),
pthread_cond_broadcast(),
pthread_cond_destroy(),
pthread_cond_init(),
pthread_cond_signal(),
pthread_cond_timedwait(),
pthread_cond_wait(),
pthread_condattr_destroy(),
pthread_condattr_init(),
pthread_create(),
pthread_detach(),
pthread_equal(),
pthread_exit(),
pthread_getspecific(),
pthread_join(,
pthread_key_create(),
pthread_key_delete(),
pthread_mutex_destroy(),
pthread_mutex_init(),
pthread_mutex_lock(),
pthread_mutex_trylock(),
pthread_mutex_unlock(),
pthread_mutexattr_destroy(),
pthread_mutexattr_init(),
pthread_once(),
pthread_rwlock_destroy(),
pthread_rwlock_init(),
pthread_rwlock_rdlock(),
pthread_rwlock_tryrdlock(),
pthread_rwlock_trywrlock(),
pthread_rwlock_unlock(),
pthread_rwlock_wrlock(),
pthread_rwlockattr_destroy(),
pthread_rwlockattr_init(),
pthread_self(),
pthread_setcancelstate(),
pthread_setcanceltype(),
pthread_setspecific(),
pthread_testcancel()
are present. */
#undef _POSIX_THREADS
#define _POSIX_THREADS 200112L
/* _SC_READER_WRITER_LOCKS
This option implies the _POSIX_THREADS option. Conversely, under
POSIX 1003.1-2001 the _POSIX_THREADS option implies this option.
The functions
pthread_rwlock_destroy(),
pthread_rwlock_init(),
pthread_rwlock_rdlock(),
pthread_rwlock_tryrdlock(),
pthread_rwlock_trywrlock(),
pthread_rwlock_unlock(),
pthread_rwlock_wrlock(),
pthread_rwlockattr_destroy(),
pthread_rwlockattr_init()
are present.
*/
#undef _POSIX_READER_WRITER_LOCKS
#define _POSIX_READER_WRITER_LOCKS 200112L
/* _SC_SPIN_LOCKS
This option implies the _POSIX_THREADS and _POSIX_THREAD_SAFE_FUNCTIONS
options. The functions
pthread_spin_destroy(),
pthread_spin_init(),
pthread_spin_lock(),
pthread_spin_trylock(),
pthread_spin_unlock()
are present. */
#undef _POSIX_SPIN_LOCKS
#define _POSIX_SPIN_LOCKS 200112L
/* _SC_BARRIERS
This option implies the _POSIX_THREADS and _POSIX_THREAD_SAFE_FUNCTIONS
options. The functions
pthread_barrier_destroy(),
pthread_barrier_init(),
pthread_barrier_wait(),
pthread_barrierattr_destroy(),
pthread_barrierattr_init()
are present.
*/
#undef _POSIX_BARRIERS
#define _POSIX_BARRIERS 200112L
/* _SC_TIMEOUTS
The functions
mq_timedreceive(), - not supported
mq_timedsend(), - not supported
posix_trace_timedgetnext_event(), - not supported
pthread_mutex_timedlock(),
pthread_rwlock_timedrdlock(),
pthread_rwlock_timedwrlock(),
sem_timedwait(),
are present. */
#undef _POSIX_TIMEOUTS
#define _POSIX_TIMEOUTS 200112L
/* _SC_TIMERS - not supported
The functions
clock_getres(),
clock_gettime(),
clock_settime(),
nanosleep(),
timer_create(),
timer_delete(),
timer_gettime(),
timer_getoverrun(),
timer_settime()
are present. */
/* #undef _POSIX_TIMERS */
/* _SC_CLOCK_SELECTION
This option implies the _POSIX_TIMERS option. The functions
pthread_condattr_getclock(),
pthread_condattr_setclock(),
clock_nanosleep()
are present.
*/
#undef _POSIX_CLOCK_SELECTION
#define _POSIX_CLOCK_SELECTION 200112
/* _SC_SEMAPHORES
The include file <semaphore.h> is present. The functions
sem_close(),
sem_destroy(),
sem_getvalue(),
sem_init(),
sem_open(),
sem_post(),
sem_trywait(),
sem_unlink(),
sem_wait()
are present. */
#undef _POSIX_SEMAPHORES
#define _POSIX_SEMAPHORES 200112
#endif /* WIN_PTHREADS_UNISTD_H */

View File

@ -0,0 +1,83 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#include <stddef.h>
#include <errno.h>
#include <sys/types.h>
#include <process.h>
#include <limits.h>
#include <signal.h>
#include <sys/timeb.h>
#ifndef WIN_PTHREADS_SCHED_H
#define WIN_PTHREADS_SCHED_H
#ifndef SCHED_OTHER
/* Some POSIX realtime extensions, mostly stubbed */
#define SCHED_OTHER 0
#define SCHED_FIFO 1
#define SCHED_RR 2
#define SCHED_MIN SCHED_OTHER
#define SCHED_MAX SCHED_RR
struct sched_param {
int sched_priority;
};
#ifdef __cplusplus
extern "C" {
#endif
#if defined(IN_WINPTHREAD)
# if defined(DLL_EXPORT) && !defined(WINPTHREAD_EXPORT_ALL_DEBUG)
# define WINPTHREAD_SCHED_API __declspec(dllexport) /* building the DLL */
# else
# define WINPTHREAD_SCHED_API /* building the static library */
# endif
#else
# if defined(WINPTHREADS_USE_DLLIMPORT)
# define WINPTHREAD_SCHED_API __declspec(dllimport) /* user wants explicit `dllimport` */
# else
# define WINPTHREAD_SCHED_API /* the default; auto imported in case of DLL */
# endif
#endif
WINPTHREAD_SCHED_API int sched_yield(void);
WINPTHREAD_SCHED_API int sched_get_priority_min(int pol);
WINPTHREAD_SCHED_API int sched_get_priority_max(int pol);
WINPTHREAD_SCHED_API int sched_getscheduler(pid_t pid);
WINPTHREAD_SCHED_API int sched_setscheduler(pid_t pid, int pol, const struct sched_param *param);
#ifdef __cplusplus
}
#endif
#endif
#ifndef sched_rr_get_interval
#define sched_rr_get_interval(_p, _i) \
( errno = ENOTSUP, (int) -1 )
#endif
#endif /* WIN_PTHREADS_SCHED_H */

View File

@ -0,0 +1,85 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#ifndef WIN_PTHREADS_SEMAPHORE_H
#define WIN_PTHREADS_SEMAPHORE_H
#ifdef __cplusplus
extern "C" {
#endif
#if defined(IN_WINPTHREAD)
# if defined(DLL_EXPORT) && !defined(WINPTHREAD_EXPORT_ALL_DEBUG)
# define WINPTHREAD_SEMA_API __declspec(dllexport) /* building the DLL */
# else
# define WINPTHREAD_SEMA_API /* building the static library */
# endif
#else
# if defined(WINPTHREADS_USE_DLLIMPORT)
# define WINPTHREAD_SEMA_API __declspec(dllimport) /* user wants explicit `dllimport` */
# else
# define WINPTHREAD_SEMA_API /* the default; auto imported in case of DLL */
# endif
#endif
/* Set this to 0 to disable it */
#define USE_SEM_CriticalSection_SpinCount 100
#define SEM_VALUE_MAX INT_MAX
#ifndef _MODE_T_
#define _MODE_T_
typedef unsigned short mode_t;
#endif
typedef void *sem_t;
#define SEM_FAILED NULL
WINPTHREAD_SEMA_API int sem_init(sem_t * sem, int pshared, unsigned int value);
WINPTHREAD_SEMA_API int sem_destroy(sem_t *sem);
WINPTHREAD_SEMA_API int sem_trywait(sem_t *sem);
WINPTHREAD_SEMA_API int sem_wait(sem_t *sem);
WINPTHREAD_SEMA_API int sem_timedwait(sem_t * sem, const struct timespec *t);
WINPTHREAD_SEMA_API int sem_post(sem_t *sem);
WINPTHREAD_SEMA_API int sem_post_multiple(sem_t *sem, int count);
/* yes, it returns a semaphore (or SEM_FAILED) */
WINPTHREAD_SEMA_API sem_t * sem_open(const char * name, int oflag, mode_t mode, unsigned int value);
WINPTHREAD_SEMA_API int sem_close(sem_t * sem);
WINPTHREAD_SEMA_API int sem_unlink(const char * name);
WINPTHREAD_SEMA_API int sem_getvalue(sem_t * sem, int * sval);
#ifdef __cplusplus
}
#endif
#endif /* WIN_PTHREADS_SEMAPHORE_H */

View File

@ -0,0 +1,246 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#include <windows.h>
#include <stdio.h>
#include <malloc.h>
#include "pthread.h"
#include "barrier.h"
#include "ref.h"
#include "misc.h"
static pthread_spinlock_t barrier_global = PTHREAD_SPINLOCK_INITIALIZER;
static WINPTHREADS_ATTRIBUTE((noinline)) int
barrier_unref(volatile pthread_barrier_t *barrier, int res)
{
pthread_spin_lock(&barrier_global);
#ifdef WINPTHREAD_DBG
assert((((barrier_t *)*barrier)->valid == LIFE_BARRIER) && (((barrier_t *)*barrier)->busy > 0));
#endif
((barrier_t *)*barrier)->busy -= 1;
pthread_spin_unlock(&barrier_global);
return res;
}
static WINPTHREADS_ATTRIBUTE((noinline)) int barrier_ref(volatile pthread_barrier_t *barrier)
{
int r = 0;
pthread_spin_lock(&barrier_global);
if (!barrier || !*barrier || ((barrier_t *)*barrier)->valid != LIFE_BARRIER) r = EINVAL;
else {
((barrier_t *)*barrier)->busy += 1;
}
pthread_spin_unlock(&barrier_global);
return r;
}
static WINPTHREADS_ATTRIBUTE((noinline)) int
barrier_ref_destroy(volatile pthread_barrier_t *barrier, pthread_barrier_t *bDestroy)
{
int r = 0;
*bDestroy = NULL;
pthread_spin_lock(&barrier_global);
if (!barrier || !*barrier || ((barrier_t *)*barrier)->valid != LIFE_BARRIER) r = EINVAL;
else {
barrier_t *b_ = (barrier_t *)*barrier;
if (b_->busy) r = EBUSY;
else {
*bDestroy = *barrier;
*barrier = NULL;
}
}
pthread_spin_unlock(&barrier_global);
return r;
}
static WINPTHREADS_ATTRIBUTE((noinline)) void
barrier_ref_set (volatile pthread_barrier_t *barrier, void *v)
{
pthread_spin_lock(&barrier_global);
*barrier = v;
pthread_spin_unlock(&barrier_global);
}
int pthread_barrier_destroy(pthread_barrier_t *b_)
{
pthread_barrier_t bDestroy;
barrier_t *b;
int r;
while ((r = barrier_ref_destroy(b_,&bDestroy)) == EBUSY)
Sleep(0);
if (r)
return r;
b = (barrier_t *)bDestroy;
pthread_mutex_lock(&b->m);
if (sem_destroy(&b->sems[0]) != 0)
{
/* Could this happen? */
*b_ = bDestroy;
pthread_mutex_unlock (&b->m);
return EBUSY;
}
if (sem_destroy(&b->sems[1]) != 0)
{
sem_init (&b->sems[0], b->share, 0);
*b_ = bDestroy;
pthread_mutex_unlock (&b->m);
return -1;
}
pthread_mutex_unlock(&b->m);
if(pthread_mutex_destroy(&b->m) != 0) {
sem_init (&b->sems[0], b->share, 0);
sem_init (&b->sems[1], b->share, 0);
*b_ = bDestroy;
return -1;
}
b->valid = DEAD_BARRIER;
free(bDestroy);
return 0;
}
int
pthread_barrier_init (pthread_barrier_t *b_, const void *attr,
unsigned int count)
{
barrier_t *b;
if (!count || !b_)
return EINVAL;
if ((b = (pthread_barrier_t)calloc(1,sizeof(*b))) == NULL)
return ENOMEM;
if (!attr || *((int **)attr) == NULL)
b->share = PTHREAD_PROCESS_PRIVATE;
else
memcpy (&b->share, *((void **) attr), sizeof (int));
b->total = count;
b->count = count;
b->valid = LIFE_BARRIER;
b->sel = 0;
if (pthread_mutex_init(&b->m, NULL) != 0)
{
free (b);
return ENOMEM;
}
if (sem_init(&b->sems[0], b->share, 0) != 0)
{
pthread_mutex_destroy(&b->m);
free (b);
return ENOMEM;
}
if (sem_init(&b->sems[1], b->share, 0) != 0)
{
pthread_mutex_destroy(&b->m);
sem_destroy(&b->sems[0]);
free (b);
return ENOMEM;
}
barrier_ref_set (b_,b);
return 0;
}
int pthread_barrier_wait(pthread_barrier_t *b_)
{
long sel;
int r, e, rslt;
barrier_t *b;
r = barrier_ref(b_);
if(r) return r;
b = (barrier_t *)*b_;
if ((r = pthread_mutex_lock(&b->m)) != 0) return barrier_unref(b_,EINVAL);
sel = b->sel;
InterlockedDecrement((long*)&b->total);
if (b->total == 0)
{
b->total = b->count;
b->sel = (sel != 0 ? 0 : 1);
e = 1;
rslt = PTHREAD_BARRIER_SERIAL_THREAD;
r = (b->count > 1 ? sem_post_multiple (&b->sems[sel], b->count - 1) : 0);
}
else { e = 0; rslt= 0; }
pthread_mutex_unlock(&b->m);
if (!e)
r = sem_wait(&b->sems[sel]);
if (!r) r = rslt;
return barrier_unref(b_,r);
}
int pthread_barrierattr_init(void **attr)
{
int *p;
if ((p = (int *) calloc (1, sizeof (int))) == NULL)
return ENOMEM;
*p = PTHREAD_PROCESS_PRIVATE;
*attr = p;
return 0;
}
int pthread_barrierattr_destroy(void **attr)
{
void *p;
if (!attr || (p = *attr) == NULL)
return EINVAL;
*attr = NULL;
free (p);
return 0;
}
int pthread_barrierattr_setpshared(void **attr, int s)
{
if (!attr || *attr == NULL
|| (s != PTHREAD_PROCESS_SHARED && s != PTHREAD_PROCESS_PRIVATE))
return EINVAL;
memcpy (*attr, &s, sizeof (int));
return 0;
}
int pthread_barrierattr_getpshared(void **attr, int *s)
{
if (!attr || !s || *attr == NULL)
return EINVAL;
memcpy (s, *attr, sizeof (int));
return 0;
}

View File

@ -0,0 +1,52 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#ifndef WIN_PTHREADS_BARRIER_H
#define WIN_PTHREADS_BARRIER_H
#define LIFE_BARRIER 0xBAB1FEED
#define DEAD_BARRIER 0xDEADB00F
#define _PTHREAD_BARRIER_FLAG (1<<30)
#define CHECK_BARRIER(b) \
do { \
if (!(b) || ( ((barrier_t *)(*b))->valid != (unsigned int)LIFE_BARRIER ) ) \
return EINVAL; \
} while (0)
#include "semaphore.h"
typedef struct barrier_t barrier_t;
struct barrier_t
{
int valid;
int busy;
int count;
int total;
int share;
long sel;
pthread_mutex_t m;
sem_t sems[2];
};
#endif

View File

@ -0,0 +1,257 @@
/**
* This file has no copyright assigned and is placed in the Public Domain.
* This file is part of the w64 mingw-runtime package.
* No warranty is given; refer to the file DISCLAIMER.PD within this package.
*/
#include <errno.h>
#include <stdint.h>
#include <time.h>
#include <windows.h>
#ifndef IN_WINPTHREAD
#define IN_WINPTHREAD 1
#endif
#include "pthread.h"
#include "pthread_time.h"
#include "misc.h"
#define POW10_7 10000000
#define POW10_9 1000000000
/* Number of 100ns-seconds between the beginning of the Windows epoch
* (Jan. 1, 1601) and the Unix epoch (Jan. 1, 1970)
*/
#define DELTA_EPOCH_IN_100NS INT64_C(116444736000000000)
static WINPTHREADS_INLINE int lc_set_errno(int result)
{
if (result != 0) {
errno = result;
return -1;
}
return 0;
}
/**
* Get the resolution of the specified clock clock_id and
* stores it in the struct timespec pointed to by res.
* @param clock_id The clock_id argument is the identifier of the particular
* clock on which to act. The following clocks are supported:
* <pre>
* CLOCK_REALTIME System-wide real-time clock. Setting this clock
* requires appropriate privileges.
* CLOCK_MONOTONIC Clock that cannot be set and represents monotonic
* time since some unspecified starting point.
* CLOCK_PROCESS_CPUTIME_ID High-resolution per-process timer from the CPU.
* CLOCK_THREAD_CPUTIME_ID Thread-specific CPU-time clock.
* </pre>
* @param res The pointer to a timespec structure to receive the time
* resolution.
* @return If the function succeeds, the return value is 0.
* If the function fails, the return value is -1,
* with errno set to indicate the error.
*/
int clock_getres(clockid_t clock_id, struct timespec *res)
{
clockid_t id = clock_id;
if (id == CLOCK_REALTIME && _pthread_get_system_time_best_as_file_time == GetSystemTimeAsFileTime)
id = CLOCK_REALTIME_COARSE; /* GetSystemTimePreciseAsFileTime() not available */
switch(id) {
case CLOCK_REALTIME:
case CLOCK_MONOTONIC:
{
LARGE_INTEGER pf;
if (QueryPerformanceFrequency(&pf) == 0)
return lc_set_errno(EINVAL);
res->tv_sec = 0;
res->tv_nsec = (int) ((POW10_9 + (pf.QuadPart >> 1)) / pf.QuadPart);
if (res->tv_nsec < 1)
res->tv_nsec = 1;
return 0;
}
case CLOCK_REALTIME_COARSE:
case CLOCK_PROCESS_CPUTIME_ID:
case CLOCK_THREAD_CPUTIME_ID:
{
DWORD timeAdjustment, timeIncrement;
BOOL isTimeAdjustmentDisabled;
(void) GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, &isTimeAdjustmentDisabled);
res->tv_sec = 0;
res->tv_nsec = timeIncrement * 100;
return 0;
}
default:
break;
}
return lc_set_errno(EINVAL);
}
/**
* Get the time of the specified clock clock_id and stores it in the struct
* timespec pointed to by tp.
* @param clock_id The clock_id argument is the identifier of the particular
* clock on which to act. The following clocks are supported:
* <pre>
* CLOCK_REALTIME System-wide real-time clock. Setting this clock
* requires appropriate privileges.
* CLOCK_MONOTONIC Clock that cannot be set and represents monotonic
* time since some unspecified starting point.
* CLOCK_PROCESS_CPUTIME_ID High-resolution per-process timer from the CPU.
* CLOCK_THREAD_CPUTIME_ID Thread-specific CPU-time clock.
* </pre>
* @param tp The pointer to a timespec structure to receive the time.
* @return If the function succeeds, the return value is 0.
* If the function fails, the return value is -1,
* with errno set to indicate the error.
*/
int clock_gettime(clockid_t clock_id, struct timespec *tp)
{
unsigned __int64 t;
LARGE_INTEGER pf, pc;
union {
unsigned __int64 u64;
FILETIME ft;
} ct, et, kt, ut;
switch(clock_id) {
case CLOCK_REALTIME:
{
_pthread_get_system_time_best_as_file_time(&ct.ft);
t = ct.u64 - DELTA_EPOCH_IN_100NS;
tp->tv_sec = t / POW10_7;
tp->tv_nsec = ((int) (t % POW10_7)) * 100;
return 0;
}
case CLOCK_REALTIME_COARSE:
{
GetSystemTimeAsFileTime(&ct.ft);
t = ct.u64 - DELTA_EPOCH_IN_100NS;
tp->tv_sec = t / POW10_7;
tp->tv_nsec = ((int) (t % POW10_7)) * 100;
return 0;
}
case CLOCK_MONOTONIC:
{
if (QueryPerformanceFrequency(&pf) == 0)
return lc_set_errno(EINVAL);
if (QueryPerformanceCounter(&pc) == 0)
return lc_set_errno(EINVAL);
tp->tv_sec = pc.QuadPart / pf.QuadPart;
tp->tv_nsec = (int) (((pc.QuadPart % pf.QuadPart) * POW10_9 + (pf.QuadPart >> 1)) / pf.QuadPart);
if (tp->tv_nsec >= POW10_9) {
tp->tv_sec ++;
tp->tv_nsec -= POW10_9;
}
return 0;
}
case CLOCK_PROCESS_CPUTIME_ID:
{
if(0 == GetProcessTimes(GetCurrentProcess(), &ct.ft, &et.ft, &kt.ft, &ut.ft))
return lc_set_errno(EINVAL);
t = kt.u64 + ut.u64;
tp->tv_sec = t / POW10_7;
tp->tv_nsec = ((int) (t % POW10_7)) * 100;
return 0;
}
case CLOCK_THREAD_CPUTIME_ID:
{
if(0 == GetThreadTimes(GetCurrentThread(), &ct.ft, &et.ft, &kt.ft, &ut.ft))
return lc_set_errno(EINVAL);
t = kt.u64 + ut.u64;
tp->tv_sec = t / POW10_7;
tp->tv_nsec = ((int) (t % POW10_7)) * 100;
return 0;
}
default:
break;
}
return lc_set_errno(EINVAL);
}
/**
* Sleep for the specified time.
* @param clock_id This argument should always be CLOCK_REALTIME (0).
* @param flags 0 for relative sleep interval, others for absolute waking up.
* @param request The desired sleep interval or absolute waking up time.
* @param remain The remain amount of time to sleep.
* The current implemention just ignore it.
* @return If the function succeeds, the return value is 0.
* If the function fails, the return value is -1,
* with errno set to indicate the error.
*/
int clock_nanosleep(clockid_t clock_id, int flags,
const struct timespec *request,
struct timespec *remain)
{
struct timespec tp;
if (clock_id != CLOCK_REALTIME)
return lc_set_errno(EINVAL);
if (flags == 0)
return nanosleep(request, remain);
/* TIMER_ABSTIME = 1 */
clock_gettime(CLOCK_REALTIME, &tp);
tp.tv_sec = request->tv_sec - tp.tv_sec;
tp.tv_nsec = request->tv_nsec - tp.tv_nsec;
if (tp.tv_nsec < 0) {
tp.tv_nsec += POW10_9;
tp.tv_sec --;
}
return nanosleep(&tp, remain);
}
/**
* Set the time of the specified clock clock_id.
* @param clock_id This argument should always be CLOCK_REALTIME (0).
* @param tp The requested time.
* @return If the function succeeds, the return value is 0.
* If the function fails, the return value is -1,
* with errno set to indicate the error.
*/
int clock_settime(clockid_t clock_id, const struct timespec *tp)
{
SYSTEMTIME st;
union {
unsigned __int64 u64;
FILETIME ft;
} t;
if (clock_id != CLOCK_REALTIME)
return lc_set_errno(EINVAL);
t.u64 = tp->tv_sec * (__int64) POW10_7 + tp->tv_nsec / 100 + DELTA_EPOCH_IN_100NS;
if (FileTimeToSystemTime(&t.ft, &st) == 0)
return lc_set_errno(EINVAL);
if (SetSystemTime(&st) == 0)
return lc_set_errno(EPERM);
return 0;
}

755
libs/winpthreads/src/cond.c Normal file
View File

@ -0,0 +1,755 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
/*
* Posix Condition Variables for Microsoft Windows.
* 22-9-2010 Partly based on the ACE framework implementation.
*/
#include <windows.h>
#include <stdio.h>
#include <malloc.h>
#include <time.h>
#include "pthread.h"
#include "pthread_time.h"
#include "ref.h"
#include "cond.h"
#include "thread.h"
#include "misc.h"
#include "winpthread_internal.h"
#include "pthread_compat.h"
int __pthread_shallcancel (void);
static int do_sema_b_wait (HANDLE sema, int nointerrupt, DWORD timeout,CRITICAL_SECTION *cs, LONG *val);
static int do_sema_b_release(HANDLE sema, LONG count,CRITICAL_SECTION *cs, LONG *val);
static void cleanup_wait(void *arg);
typedef struct sCondWaitHelper {
cond_t *c;
pthread_mutex_t *external_mutex;
int *r;
} sCondWaitHelper;
int do_sema_b_wait_intern (HANDLE sema, int nointerrupt, DWORD timeout);
#ifdef WINPTHREAD_DBG
static int print_state = 0;
static FILE *fo;
void cond_print_set(int state, FILE *f)
{
if (f) fo = f;
if (!fo) fo = stdout;
print_state = state;
}
void cond_print(volatile pthread_cond_t *c, char *txt)
{
if (!print_state) return;
cond_t *c_ = (cond_t *)*c;
if (c_ == NULL) {
fprintf(fo,"C%p %lu %s\n",(void *)*c,GetCurrentThreadId(),txt);
} else {
fprintf(fo,"C%p %lu V=%0X w=%ld %s\n",
(void *)*c,
GetCurrentThreadId(),
(int)c_->valid,
c_->waiters_count_,
txt
);
}
}
#endif
static pthread_spinlock_t cond_locked = PTHREAD_SPINLOCK_INITIALIZER;
static int
cond_static_init (pthread_cond_t *c)
{
int r = 0;
pthread_spin_lock (&cond_locked);
if (c == NULL)
r = EINVAL;
else if (*c == PTHREAD_COND_INITIALIZER)
r = pthread_cond_init (c, NULL);
else
/* We assume someone was faster ... */
r = 0;
pthread_spin_unlock (&cond_locked);
return r;
}
int
pthread_condattr_destroy (pthread_condattr_t *a)
{
if (!a)
return EINVAL;
*a = 0;
return 0;
}
int
pthread_condattr_init (pthread_condattr_t *a)
{
if (!a)
return EINVAL;
*a = 0;
return 0;
}
int
pthread_condattr_getpshared (const pthread_condattr_t *a, int *s)
{
if (!a || !s)
return EINVAL;
*s = *a;
return 0;
}
int
pthread_condattr_getclock (const pthread_condattr_t *a, clockid_t *clock_id)
{
if (!a || !clock_id)
return EINVAL;
*clock_id = 0;
return 0;
}
int
pthread_condattr_setclock(pthread_condattr_t *a, clockid_t clock_id)
{
if (!a || clock_id != 0)
return EINVAL;
return 0;
}
int
__pthread_clock_nanosleep (clockid_t clock_id, int flags, const struct timespec *rqtp,
struct timespec *rmtp)
{
unsigned long long tick, tick2;
unsigned long long delay;
DWORD dw;
if (clock_id != CLOCK_REALTIME
&& clock_id != CLOCK_MONOTONIC
&& clock_id != CLOCK_PROCESS_CPUTIME_ID)
return EINVAL;
if ((flags & TIMER_ABSTIME) != 0)
delay = _pthread_rel_time_in_ms (rqtp);
else
delay = _pthread_time_in_ms_from_timespec (rqtp);
do
{
dw = (DWORD) (delay >= 99999ULL ? 99999ULL : delay);
tick = _pthread_time_in_ms ();
pthread_delay_np_ms (dw);
tick2 = _pthread_time_in_ms ();
tick2 -= tick;
if (tick2 >= delay)
delay = 0;
else
delay -= tick2;
}
while (delay != 0ULL);
if (rmtp)
memset (rmtp, 0, sizeof (*rmtp));
return 0;
}
int
pthread_condattr_setpshared (pthread_condattr_t *a, int s)
{
if (!a || (s != PTHREAD_PROCESS_SHARED && s != PTHREAD_PROCESS_PRIVATE))
return EINVAL;
if (s == PTHREAD_PROCESS_SHARED)
{
*a = PTHREAD_PROCESS_PRIVATE;
return ENOSYS;
}
*a = s;
return 0;
}
int
pthread_cond_init (pthread_cond_t *c, const pthread_condattr_t *a)
{
cond_t *_c;
int r = 0;
if (!c)
return EINVAL;
if (a && *a == PTHREAD_PROCESS_SHARED)
return ENOSYS;
if ((_c = calloc(1, sizeof(*_c))) == NULL)
return ENOMEM;
_c->valid = DEAD_COND;
_c->busy = 0;
_c->waiters_count_ = 0;
_c->waiters_count_gone_ = 0;
_c->waiters_count_unblock_ = 0;
_c->sema_q = CreateSemaphore (NULL, /* no security */
0, /* initially 0 */
0x7fffffff, /* max count */
NULL); /* unnamed */
_c->sema_b = CreateSemaphore (NULL, /* no security */
0, /* initially 0 */
0x7fffffff, /* max count */
NULL);
if (_c->sema_q == NULL || _c->sema_b == NULL) {
if (_c->sema_q != NULL)
CloseHandle (_c->sema_q);
if (_c->sema_b != NULL)
CloseHandle (_c->sema_b);
free (_c);
r = EAGAIN;
} else {
InitializeCriticalSection(&_c->waiters_count_lock_);
InitializeCriticalSection(&_c->waiters_b_lock_);
InitializeCriticalSection(&_c->waiters_q_lock_);
_c->value_q = 0;
_c->value_b = 1;
}
if (!r)
{
_c->valid = LIFE_COND;
*c = (pthread_cond_t)_c;
}
else
*c = (pthread_cond_t)NULL;
return r;
}
int
pthread_cond_destroy (pthread_cond_t *c)
{
cond_t *_c;
int r;
if (!c || !*c)
return EINVAL;
if (*c == PTHREAD_COND_INITIALIZER)
{
pthread_spin_lock (&cond_locked);
if (*c == PTHREAD_COND_INITIALIZER)
{
*c = (pthread_cond_t)NULL;
r = 0;
}
else
r = EBUSY;
pthread_spin_unlock (&cond_locked);
return r;
}
_c = (cond_t *) *c;
r = do_sema_b_wait(_c->sema_b, 0, INFINITE,&_c->waiters_b_lock_,&_c->value_b);
if (r != 0)
return r;
if (!TryEnterCriticalSection (&_c->waiters_count_lock_))
{
do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
return EBUSY;
}
if (_c->waiters_count_ > _c->waiters_count_gone_)
{
r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
if (!r) r = EBUSY;
LeaveCriticalSection(&_c->waiters_count_lock_);
return r;
}
*c = (pthread_cond_t)NULL;
do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
if (!CloseHandle (_c->sema_q) && !r)
r = EINVAL;
if (!CloseHandle (_c->sema_b) && !r)
r = EINVAL;
LeaveCriticalSection (&_c->waiters_count_lock_);
DeleteCriticalSection(&_c->waiters_count_lock_);
DeleteCriticalSection(&_c->waiters_b_lock_);
DeleteCriticalSection(&_c->waiters_q_lock_);
_c->valid = DEAD_COND;
free(_c);
return 0;
}
int
pthread_cond_signal (pthread_cond_t *c)
{
cond_t *_c;
int r;
if (!c || !*c)
return EINVAL;
_c = (cond_t *)*c;
if (_c == (cond_t *)PTHREAD_COND_INITIALIZER)
return 0;
else if (_c->valid != (unsigned int)LIFE_COND)
return EINVAL;
EnterCriticalSection (&_c->waiters_count_lock_);
/* If there aren't any waiters, then this is a no-op. */
if (_c->waiters_count_unblock_ != 0)
{
if (_c->waiters_count_ == 0)
{
LeaveCriticalSection (&_c->waiters_count_lock_);
/* pthread_testcancel(); */
return 0;
}
_c->waiters_count_ -= 1;
_c->waiters_count_unblock_ += 1;
}
else if (_c->waiters_count_ > _c->waiters_count_gone_)
{
r = do_sema_b_wait (_c->sema_b, 1, INFINITE,&_c->waiters_b_lock_,&_c->value_b);
if (r != 0)
{
LeaveCriticalSection (&_c->waiters_count_lock_);
/* pthread_testcancel(); */
return r;
}
if (_c->waiters_count_gone_ != 0)
{
_c->waiters_count_ -= _c->waiters_count_gone_;
_c->waiters_count_gone_ = 0;
}
_c->waiters_count_ -= 1;
_c->waiters_count_unblock_ = 1;
}
else
{
LeaveCriticalSection (&_c->waiters_count_lock_);
/* pthread_testcancel(); */
return 0;
}
LeaveCriticalSection (&_c->waiters_count_lock_);
r = do_sema_b_release(_c->sema_q, 1,&_c->waiters_q_lock_,&_c->value_q);
/* pthread_testcancel(); */
return r;
}
int
pthread_cond_broadcast (pthread_cond_t *c)
{
cond_t *_c;
int r;
int relCnt = 0;
if (!c || !*c)
return EINVAL;
_c = (cond_t *)*c;
if (_c == (cond_t*)PTHREAD_COND_INITIALIZER)
return 0;
else if (_c->valid != (unsigned int)LIFE_COND)
return EINVAL;
EnterCriticalSection (&_c->waiters_count_lock_);
/* If there aren't any waiters, then this is a no-op. */
if (_c->waiters_count_unblock_ != 0)
{
if (_c->waiters_count_ == 0)
{
LeaveCriticalSection (&_c->waiters_count_lock_);
/* pthread_testcancel(); */
return 0;
}
relCnt = _c->waiters_count_;
_c->waiters_count_ = 0;
_c->waiters_count_unblock_ += relCnt;
}
else if (_c->waiters_count_ > _c->waiters_count_gone_)
{
r = do_sema_b_wait (_c->sema_b, 1, INFINITE,&_c->waiters_b_lock_,&_c->value_b);
if (r != 0)
{
LeaveCriticalSection (&_c->waiters_count_lock_);
/* pthread_testcancel(); */
return r;
}
if (_c->waiters_count_gone_ != 0)
{
_c->waiters_count_ -= _c->waiters_count_gone_;
_c->waiters_count_gone_ = 0;
}
relCnt = _c->waiters_count_;
_c->waiters_count_ = 0;
_c->waiters_count_unblock_ = relCnt;
}
else
{
LeaveCriticalSection (&_c->waiters_count_lock_);
/* pthread_testcancel(); */
return 0;
}
LeaveCriticalSection (&_c->waiters_count_lock_);
r = do_sema_b_release(_c->sema_q, relCnt,&_c->waiters_q_lock_,&_c->value_q);
/* pthread_testcancel(); */
return r;
}
int
pthread_cond_wait (pthread_cond_t *c, pthread_mutex_t *external_mutex)
{
sCondWaitHelper ch;
cond_t *_c;
int r;
/* pthread_testcancel(); */
if (!c || *c == (pthread_cond_t)NULL)
return EINVAL;
_c = (cond_t *)*c;
if (*c == PTHREAD_COND_INITIALIZER)
{
r = cond_static_init(c);
if (r != 0 && r != EBUSY)
return r;
_c = (cond_t *) *c;
} else if (_c->valid != (unsigned int)LIFE_COND)
return EINVAL;
tryagain:
r = do_sema_b_wait (_c->sema_b, 0, INFINITE,&_c->waiters_b_lock_,&_c->value_b);
if (r != 0)
return r;
if (!TryEnterCriticalSection (&_c->waiters_count_lock_))
{
r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
if (r != 0)
return r;
sched_yield();
goto tryagain;
}
_c->waiters_count_++;
LeaveCriticalSection(&_c->waiters_count_lock_);
r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
if (r != 0)
return r;
ch.c = _c;
ch.r = &r;
ch.external_mutex = external_mutex;
pthread_cleanup_push(cleanup_wait, (void *) &ch);
r = pthread_mutex_unlock(external_mutex);
if (!r)
r = do_sema_b_wait (_c->sema_q, 0, INFINITE,&_c->waiters_q_lock_,&_c->value_q);
pthread_cleanup_pop(1);
return r;
}
static int
pthread_cond_timedwait_impl (pthread_cond_t *c, pthread_mutex_t *external_mutex, const struct timespec *t, int rel)
{
sCondWaitHelper ch;
DWORD dwr;
int r;
cond_t *_c;
/* pthread_testcancel(); */
if (!c || !*c)
return EINVAL;
_c = (cond_t *)*c;
if (_c == (cond_t *)PTHREAD_COND_INITIALIZER)
{
r = cond_static_init(c);
if (r && r != EBUSY)
return r;
_c = (cond_t *) *c;
} else if ((_c)->valid != (unsigned int)LIFE_COND)
return EINVAL;
if (rel == 0)
{
dwr = dwMilliSecs(_pthread_rel_time_in_ms(t));
}
else
{
dwr = dwMilliSecs(_pthread_time_in_ms_from_timespec(t));
}
tryagain:
r = do_sema_b_wait (_c->sema_b, 0, INFINITE,&_c->waiters_b_lock_,&_c->value_b);
if (r != 0)
return r;
if (!TryEnterCriticalSection (&_c->waiters_count_lock_))
{
r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
if (r != 0)
return r;
sched_yield();
goto tryagain;
}
_c->waiters_count_++;
LeaveCriticalSection(&_c->waiters_count_lock_);
r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
if (r != 0)
return r;
ch.c = _c;
ch.r = &r;
ch.external_mutex = external_mutex;
{
pthread_cleanup_push(cleanup_wait, (void *) &ch);
r = pthread_mutex_unlock(external_mutex);
if (!r)
r = do_sema_b_wait (_c->sema_q, 0, dwr,&_c->waiters_q_lock_,&_c->value_q);
pthread_cleanup_pop(1);
}
return r;
}
int
pthread_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, const struct timespec *t)
{
return pthread_cond_timedwait_impl(c, m, t, 0);
}
int
pthread_cond_timedwait_relative_np(pthread_cond_t *c, pthread_mutex_t *m, const struct timespec *t)
{
return pthread_cond_timedwait_impl(c, m, t, 1);
}
static void
cleanup_wait (void *arg)
{
int n, r;
sCondWaitHelper *ch = (sCondWaitHelper *) arg;
cond_t *_c;
_c = ch->c;
EnterCriticalSection (&_c->waiters_count_lock_);
n = _c->waiters_count_unblock_;
if (n != 0)
_c->waiters_count_unblock_ -= 1;
else if ((INT_MAX/2) - 1 == _c->waiters_count_gone_)
{
_c->waiters_count_gone_ += 1;
r = do_sema_b_wait (_c->sema_b, 1, INFINITE,&_c->waiters_b_lock_,&_c->value_b);
if (r != 0)
{
LeaveCriticalSection(&_c->waiters_count_lock_);
ch->r[0] = r;
return;
}
_c->waiters_count_ -= _c->waiters_count_gone_;
r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
if (r != 0)
{
LeaveCriticalSection(&_c->waiters_count_lock_);
ch->r[0] = r;
return;
}
_c->waiters_count_gone_ = 0;
}
else
_c->waiters_count_gone_ += 1;
LeaveCriticalSection (&_c->waiters_count_lock_);
if (n == 1)
{
r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
if (r != 0)
{
ch->r[0] = r;
return;
}
}
r = pthread_mutex_lock(ch->external_mutex);
if (r != 0)
ch->r[0] = r;
}
static int
do_sema_b_wait (HANDLE sema, int nointerrupt, DWORD timeout,CRITICAL_SECTION *cs, LONG *val)
{
int r;
LONG v;
EnterCriticalSection(cs);
InterlockedDecrement(val);
v = val[0];
LeaveCriticalSection(cs);
if (v >= 0)
return 0;
r = do_sema_b_wait_intern (sema, nointerrupt, timeout);
EnterCriticalSection(cs);
if (r != 0)
InterlockedIncrement(val);
LeaveCriticalSection(cs);
return r;
}
int
do_sema_b_wait_intern (HANDLE sema, int nointerrupt, DWORD timeout)
{
HANDLE arr[2];
DWORD maxH = 1;
int r = 0;
DWORD res, dt;
if (nointerrupt == 1)
{
res = _pthread_wait_for_single_object(sema, timeout);
switch (res) {
case WAIT_TIMEOUT:
r = ETIMEDOUT;
break;
case WAIT_ABANDONED:
r = EPERM;
break;
case WAIT_OBJECT_0:
break;
default:
/*We can only return EINVAL though it might not be posix compliant */
r = EINVAL;
}
if (r != 0 && r != EINVAL && WaitForSingleObject(sema, 0) == WAIT_OBJECT_0)
r = 0;
return r;
}
arr[0] = sema;
arr[1] = (HANDLE) pthread_getevent ();
if (arr[1] != NULL) maxH += 1;
if (maxH == 2)
{
redo:
res = _pthread_wait_for_multiple_objects(maxH, arr, 0, timeout);
switch (res) {
case WAIT_TIMEOUT:
r = ETIMEDOUT;
break;
case (WAIT_OBJECT_0 + 1):
ResetEvent(arr[1]);
if (nointerrupt != 2)
{
pthread_testcancel();
return EINVAL;
}
pthread_testcancel ();
goto redo;
case WAIT_ABANDONED:
r = EPERM;
break;
case WAIT_OBJECT_0:
r = 0;
break;
default:
/*We can only return EINVAL though it might not be posix compliant */
r = EINVAL;
}
if (r != 0 && r != EINVAL && WaitForSingleObject(arr[0], 0) == WAIT_OBJECT_0)
r = 0;
if (r != 0 && nointerrupt != 2 && __pthread_shallcancel ())
return EINVAL;
return r;
}
if (timeout == INFINITE)
{
do {
res = _pthread_wait_for_single_object(sema, 40);
switch (res) {
case WAIT_TIMEOUT:
r = ETIMEDOUT;
break;
case WAIT_ABANDONED:
r = EPERM;
break;
case WAIT_OBJECT_0:
r = 0;
break;
default:
/*We can only return EINVAL though it might not be posix compliant */
r = EINVAL;
}
if (r != 0 && __pthread_shallcancel ())
{
if (nointerrupt != 2)
pthread_testcancel();
return EINVAL;
}
} while (r == ETIMEDOUT);
if (r != 0 && r != EINVAL && WaitForSingleObject(sema, 0) == WAIT_OBJECT_0)
r = 0;
return r;
}
dt = 20;
do {
if (dt > timeout) dt = timeout;
res = _pthread_wait_for_single_object(sema, dt);
switch (res) {
case WAIT_TIMEOUT:
r = ETIMEDOUT;
break;
case WAIT_ABANDONED:
r = EPERM;
break;
case WAIT_OBJECT_0:
r = 0;
break;
default:
/*We can only return EINVAL though it might not be posix compliant */
r = EINVAL;
}
timeout -= dt;
if (timeout != 0 && r != 0 && __pthread_shallcancel ())
return EINVAL;
} while (r == ETIMEDOUT && timeout != 0);
if (r != 0 && r == ETIMEDOUT && WaitForSingleObject(sema, 0) == WAIT_OBJECT_0)
r = 0;
if (r != 0 && nointerrupt != 2)
pthread_testcancel();
return r;
}
static int
do_sema_b_release(HANDLE sema, LONG count,CRITICAL_SECTION *cs, LONG *val)
{
int wc;
EnterCriticalSection(cs);
if (((long long) val[0] + (long long) count) > (long long) 0x7fffffffLL)
{
LeaveCriticalSection(cs);
return ERANGE;
}
wc = -val[0];
InterlockedExchangeAdd(val, count);
if (wc <= 0 || ReleaseSemaphore(sema, (wc < count ? wc : count), NULL))
{
LeaveCriticalSection(cs);
return 0;
}
InterlockedExchangeAdd(val, -count);
LeaveCriticalSection(cs);
return EINVAL;
}

View File

@ -0,0 +1,63 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#ifndef WIN_PTHREADS_COND_H
#define WIN_PTHREADS_COND_H
#include <windows.h>
#define CHECK_COND(c) \
do { \
if (!(c) || !*c || (*c == PTHREAD_COND_INITIALIZER) \
|| ( ((cond_t *)(*c))->valid != (unsigned int)LIFE_COND ) ) \
return EINVAL; \
} while (0)
#define LIFE_COND 0xC0BAB1FD
#define DEAD_COND 0xC0DEADBF
#define STATIC_COND_INITIALIZER(x) ((pthread_cond_t)(x) == ((pthread_cond_t)PTHREAD_COND_INITIALIZER))
typedef struct cond_t cond_t;
struct cond_t
{
unsigned int valid;
int busy;
LONG waiters_count_; /* Number of waiting threads. */
LONG waiters_count_unblock_; /* Number of waiting threads whitch can be unblocked. */
LONG waiters_count_gone_; /* Number of waiters which are gone. */
CRITICAL_SECTION waiters_count_lock_; /* Serialize access to <waiters_count_>. */
CRITICAL_SECTION waiters_q_lock_; /* Serialize access to sema_q. */
LONG value_q;
CRITICAL_SECTION waiters_b_lock_; /* Serialize access to sema_b. */
LONG value_b;
HANDLE sema_q; /* Semaphore used to queue up threads waiting for the condition to
become signaled. */
HANDLE sema_b; /* Semaphore used to queue up threads waiting for the condition which
became signaled. */
};
void cond_print_set(int state, FILE *f);
void cond_print(volatile pthread_cond_t *c, char *txt);
#endif

View File

@ -0,0 +1,586 @@
/*-
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This software was developed by the Computer Systems Engineering group
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
* contributed to Berkeley.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _LIBKERN_QUAD_H_
#define _LIBKERN_QUAD_H_
/*
* Quad arithmetic.
*
* This library makes the following assumptions:
*
* - The type long long (aka quad_t) exists.
*
* - A quad variable is exactly twice as long as `long'.
*
* - The machine's arithmetic is two's complement.
*
* This library can provide 128-bit arithmetic on a machine with 128-bit
* quads and 64-bit longs, for instance, or 96-bit arithmetic on machines
* with 48-bit longs.
*/
/*
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/limits.h>
#include <sys/syslimits.h>
*/
#include <limits.h>
typedef long long quad_t;
typedef unsigned long long u_quad_t;
typedef unsigned long u_long;
#define CHAR_BIT __CHAR_BIT__
/*
* Define the order of 32-bit words in 64-bit words.
* For little endian only.
*/
#define _QUAD_HIGHWORD 1
#define _QUAD_LOWWORD 0
/*
* Depending on the desired operation, we view a `long long' (aka quad_t) in
* one or more of the following formats.
*/
union uu {
quad_t q; /* as a (signed) quad */
quad_t uq; /* as an unsigned quad */
long sl[2]; /* as two signed longs */
u_long ul[2]; /* as two unsigned longs */
};
/*
* Define high and low longwords.
*/
#define H _QUAD_HIGHWORD
#define L _QUAD_LOWWORD
/*
* Total number of bits in a quad_t and in the pieces that make it up.
* These are used for shifting, and also below for halfword extraction
* and assembly.
*/
#define QUAD_BITS (sizeof(quad_t) * CHAR_BIT)
#define LONG_BITS (sizeof(long) * CHAR_BIT)
#define HALF_BITS (sizeof(long) * CHAR_BIT / 2)
/*
* Extract high and low shortwords from longword, and move low shortword of
* longword to upper half of long, i.e., produce the upper longword of
* ((quad_t)(x) << (number_of_bits_in_long/2)). (`x' must actually be u_long.)
*
* These are used in the multiply code, to split a longword into upper
* and lower halves, and to reassemble a product as a quad_t, shifted left
* (sizeof(long)*CHAR_BIT/2).
*/
#define HHALF(x) ((x) >> HALF_BITS)
#define LHALF(x) ((x) & ((1 << HALF_BITS) - 1))
#define LHUP(x) ((x) << HALF_BITS)
typedef unsigned int qshift_t;
quad_t __ashldi3(quad_t, qshift_t);
quad_t __ashrdi3(quad_t, qshift_t);
int __cmpdi2(quad_t a, quad_t b);
quad_t __divdi3(quad_t a, quad_t b);
quad_t __lshrdi3(quad_t, qshift_t);
quad_t __moddi3(quad_t a, quad_t b);
u_quad_t __qdivrem(u_quad_t u, u_quad_t v, u_quad_t *rem);
u_quad_t __udivdi3(u_quad_t a, u_quad_t b);
u_quad_t __umoddi3(u_quad_t a, u_quad_t b);
int __ucmpdi2(u_quad_t a, u_quad_t b);
quad_t __divmoddi4(quad_t a, quad_t b, quad_t *rem);
u_quad_t __udivmoddi4(u_quad_t a, u_quad_t b, u_quad_t *rem);
#endif /* !_LIBKERN_QUAD_H_ */
#if defined (_X86_) && !defined (__x86_64__)
/*
* Shift a (signed) quad value left (arithmetic shift left).
* This is the same as logical shift left!
*/
quad_t
__ashldi3(a, shift)
quad_t a;
qshift_t shift;
{
union uu aa;
aa.q = a;
if (shift >= LONG_BITS) {
aa.ul[H] = shift >= QUAD_BITS ? 0 :
aa.ul[L] << (shift - LONG_BITS);
aa.ul[L] = 0;
} else if (shift > 0) {
aa.ul[H] = (aa.ul[H] << shift) |
(aa.ul[L] >> (LONG_BITS - shift));
aa.ul[L] <<= shift;
}
return (aa.q);
}
/*
* Shift a (signed) quad value right (arithmetic shift right).
*/
quad_t
__ashrdi3(a, shift)
quad_t a;
qshift_t shift;
{
union uu aa;
aa.q = a;
if (shift >= LONG_BITS) {
long s;
/*
* Smear bits rightward using the machine's right-shift
* method, whether that is sign extension or zero fill,
* to get the `sign word' s. Note that shifting by
* LONG_BITS is undefined, so we shift (LONG_BITS-1),
* then 1 more, to get our answer.
*/
s = (aa.sl[H] >> (LONG_BITS - 1)) >> 1;
aa.ul[L] = shift >= QUAD_BITS ? s :
aa.sl[H] >> (shift - LONG_BITS);
aa.ul[H] = s;
} else if (shift > 0) {
aa.ul[L] = (aa.ul[L] >> shift) |
(aa.ul[H] << (LONG_BITS - shift));
aa.sl[H] >>= shift;
}
return (aa.q);
}
/*
* Return 0, 1, or 2 as a <, =, > b respectively.
* Both a and b are considered signed---which means only the high word is
* signed.
*/
int
__cmpdi2(a, b)
quad_t a, b;
{
union uu aa, bb;
aa.q = a;
bb.q = b;
return (aa.sl[H] < bb.sl[H] ? 0 : aa.sl[H] > bb.sl[H] ? 2 :
aa.ul[L] < bb.ul[L] ? 0 : aa.ul[L] > bb.ul[L] ? 2 : 1);
}
/*
* Divide two signed quads.
* ??? if -1/2 should produce -1 on this machine, this code is wrong
*/
quad_t
__divdi3(a, b)
quad_t a, b;
{
u_quad_t ua, ub, uq;
int neg;
if (a < 0)
ua = -(u_quad_t)a, neg = 1;
else
ua = a, neg = 0;
if (b < 0)
ub = -(u_quad_t)b, neg ^= 1;
else
ub = b;
uq = __qdivrem(ua, ub, (u_quad_t *)0);
return (neg ? -uq : uq);
}
/*
* Shift an (unsigned) quad value right (logical shift right).
*/
quad_t
__lshrdi3(a, shift)
quad_t a;
qshift_t shift;
{
union uu aa;
aa.q = a;
if (shift >= LONG_BITS) {
aa.ul[L] = shift >= QUAD_BITS ? 0 :
aa.ul[H] >> (shift - LONG_BITS);
aa.ul[H] = 0;
} else if (shift > 0) {
aa.ul[L] = (aa.ul[L] >> shift) |
(aa.ul[H] << (LONG_BITS - shift));
aa.ul[H] >>= shift;
}
return (aa.q);
}
/*
* Return remainder after dividing two signed quads.
*
* XXX
* If -1/2 should produce -1 on this machine, this code is wrong.
*/
quad_t
__moddi3(a, b)
quad_t a, b;
{
u_quad_t ua, ub, ur;
int neg;
if (a < 0)
ua = -(u_quad_t)a, neg = 1;
else
ua = a, neg = 0;
if (b < 0)
ub = -(u_quad_t)b;
else
ub = b;
(void)__qdivrem(ua, ub, &ur);
return (neg ? -ur : ur);
}
/*
* Multiprecision divide. This algorithm is from Knuth vol. 2 (2nd ed),
* section 4.3.1, pp. 257--259.
*/
#define B (1 << HALF_BITS) /* digit base */
/* Combine two `digits' to make a single two-digit number. */
#define COMBINE(a, b) (((u_long)(a) << HALF_BITS) | (b))
/* select a type for digits in base B: use unsigned short if they fit */
#if ULONG_MAX == 0xffffffff && USHRT_MAX >= 0xffff
typedef unsigned short digit;
#else
typedef u_long digit;
#endif
/*
* Shift p[0]..p[len] left `sh' bits, ignoring any bits that
* `fall out' the left (there never will be any such anyway).
* We may assume len >= 0. NOTE THAT THIS WRITES len+1 DIGITS.
*/
static void
__shl(register digit *p, register int len, register int sh)
{
register int i;
for (i = 0; i < len; i++)
p[i] = LHALF(p[i] << sh) | (p[i + 1] >> (HALF_BITS - sh));
p[i] = LHALF(p[i] << sh);
}
/*
* __qdivrem(u, v, rem) returns u/v and, optionally, sets *rem to u%v.
*
* We do this in base 2-sup-HALF_BITS, so that all intermediate products
* fit within u_long. As a consequence, the maximum length dividend and
* divisor are 4 `digits' in this base (they are shorter if they have
* leading zeros).
*/
u_quad_t
__qdivrem(uq, vq, arq)
u_quad_t uq, vq, *arq;
{
union uu tmp;
digit *u, *v, *q;
register digit v1, v2;
u_long qhat, rhat, t;
int m, n, d, j, i;
digit uspace[5], vspace[5], qspace[5];
/*
* Take care of special cases: divide by zero, and u < v.
*/
if (vq == 0) {
/* divide by zero. */
static volatile const unsigned int zero = 0;
tmp.ul[H] = tmp.ul[L] = 1 / zero;
if (arq)
*arq = uq;
return (tmp.q);
}
if (uq < vq) {
if (arq)
*arq = uq;
return (0);
}
u = &uspace[0];
v = &vspace[0];
q = &qspace[0];
/*
* Break dividend and divisor into digits in base B, then
* count leading zeros to determine m and n. When done, we
* will have:
* u = (u[1]u[2]...u[m+n]) sub B
* v = (v[1]v[2]...v[n]) sub B
* v[1] != 0
* 1 < n <= 4 (if n = 1, we use a different division algorithm)
* m >= 0 (otherwise u < v, which we already checked)
* m + n = 4
* and thus
* m = 4 - n <= 2
*/
tmp.uq = uq;
u[0] = 0;
u[1] = HHALF(tmp.ul[H]);
u[2] = LHALF(tmp.ul[H]);
u[3] = HHALF(tmp.ul[L]);
u[4] = LHALF(tmp.ul[L]);
tmp.uq = vq;
v[1] = HHALF(tmp.ul[H]);
v[2] = LHALF(tmp.ul[H]);
v[3] = HHALF(tmp.ul[L]);
v[4] = LHALF(tmp.ul[L]);
for (n = 4; v[1] == 0; v++) {
if (--n == 1) {
u_long rbj; /* r*B+u[j] (not root boy jim) */
digit q1, q2, q3, q4;
/*
* Change of plan, per exercise 16.
* r = 0;
* for j = 1..4:
* q[j] = floor((r*B + u[j]) / v),
* r = (r*B + u[j]) % v;
* We unroll this completely here.
*/
t = v[2]; /* nonzero, by definition */
q1 = u[1] / t;
rbj = COMBINE(u[1] % t, u[2]);
q2 = rbj / t;
rbj = COMBINE(rbj % t, u[3]);
q3 = rbj / t;
rbj = COMBINE(rbj % t, u[4]);
q4 = rbj / t;
if (arq)
*arq = rbj % t;
tmp.ul[H] = COMBINE(q1, q2);
tmp.ul[L] = COMBINE(q3, q4);
return (tmp.q);
}
}
/*
* By adjusting q once we determine m, we can guarantee that
* there is a complete four-digit quotient at &qspace[1] when
* we finally stop.
*/
for (m = 4 - n; u[1] == 0; u++)
m--;
for (i = 4 - m; --i >= 0;)
q[i] = 0;
q += 4 - m;
/*
* Here we run Program D, translated from MIX to C and acquiring
* a few minor changes.
*
* D1: choose multiplier 1 << d to ensure v[1] >= B/2.
*/
d = 0;
for (t = v[1]; t < B / 2; t <<= 1)
d++;
if (d > 0) {
__shl(&u[0], m + n, d); /* u <<= d */
__shl(&v[1], n - 1, d); /* v <<= d */
}
/*
* D2: j = 0.
*/
j = 0;
v1 = v[1]; /* for D3 -- note that v[1..n] are constant */
v2 = v[2]; /* for D3 */
do {
register digit uj0, uj1, uj2;
/*
* D3: Calculate qhat (\^q, in TeX notation).
* Let qhat = min((u[j]*B + u[j+1])/v[1], B-1), and
* let rhat = (u[j]*B + u[j+1]) mod v[1].
* While rhat < B and v[2]*qhat > rhat*B+u[j+2],
* decrement qhat and increase rhat correspondingly.
* Note that if rhat >= B, v[2]*qhat < rhat*B.
*/
uj0 = u[j + 0]; /* for D3 only -- note that u[j+...] change */
uj1 = u[j + 1]; /* for D3 only */
uj2 = u[j + 2]; /* for D3 only */
if (uj0 == v1) {
qhat = B;
rhat = uj1;
goto qhat_too_big;
} else {
u_long nn = COMBINE(uj0, uj1);
qhat = nn / v1;
rhat = nn % v1;
}
while (v2 * qhat > COMBINE(rhat, uj2)) {
qhat_too_big:
qhat--;
if ((rhat += v1) >= B)
break;
}
/*
* D4: Multiply and subtract.
* The variable `t' holds any borrows across the loop.
* We split this up so that we do not require v[0] = 0,
* and to eliminate a final special case.
*/
for (t = 0, i = n; i > 0; i--) {
t = u[i + j] - v[i] * qhat - t;
u[i + j] = LHALF(t);
t = (B - HHALF(t)) & (B - 1);
}
t = u[j] - t;
u[j] = LHALF(t);
/*
* D5: test remainder.
* There is a borrow if and only if HHALF(t) is nonzero;
* in that (rare) case, qhat was too large (by exactly 1).
* Fix it by adding v[1..n] to u[j..j+n].
*/
if (HHALF(t)) {
qhat--;
for (t = 0, i = n; i > 0; i--) { /* D6: add back. */
t += u[i + j] + v[i];
u[i + j] = LHALF(t);
t = HHALF(t);
}
u[j] = LHALF(u[j] + t);
}
q[j] = qhat;
} while (++j <= m); /* D7: loop on j. */
/*
* If caller wants the remainder, we have to calculate it as
* u[m..m+n] >> d (this is at most n digits and thus fits in
* u[m+1..m+n], but we may need more source digits).
*/
if (arq) {
if (d) {
for (i = m + n; i > m; --i)
u[i] = (u[i] >> d) |
LHALF(u[i - 1] << (HALF_BITS - d));
u[i] = 0;
}
tmp.ul[H] = COMBINE(uspace[1], uspace[2]);
tmp.ul[L] = COMBINE(uspace[3], uspace[4]);
*arq = tmp.q;
}
tmp.ul[H] = COMBINE(qspace[1], qspace[2]);
tmp.ul[L] = COMBINE(qspace[3], qspace[4]);
return (tmp.q);
}
/*
* Return 0, 1, or 2 as a <, =, > b respectively.
* Neither a nor b are considered signed.
*/
int
__ucmpdi2(a, b)
u_quad_t a, b;
{
union uu aa, bb;
aa.uq = a;
bb.uq = b;
return (aa.ul[H] < bb.ul[H] ? 0 : aa.ul[H] > bb.ul[H] ? 2 :
aa.ul[L] < bb.ul[L] ? 0 : aa.ul[L] > bb.ul[L] ? 2 : 1);
}
/*
* Divide two unsigned quads.
*/
u_quad_t
__udivdi3(a, b)
u_quad_t a, b;
{
return (__qdivrem(a, b, (u_quad_t *)0));
}
/*
* Return remainder after dividing two unsigned quads.
*/
u_quad_t
__umoddi3(a, b)
u_quad_t a, b;
{
u_quad_t r;
(void)__qdivrem(a, b, &r);
return (r);
}
/*
* Divide two signed quads.
* This function is new in GCC 7.
*/
quad_t
__divmoddi4(a, b, rem)
quad_t a, b, *rem;
{
u_quad_t ua, ub, uq, ur;
int negq, negr;
if (a < 0)
ua = -(u_quad_t)a, negq = 1, negr = 1;
else
ua = a, negq = 0, negr = 0;
if (b < 0)
ub = -(u_quad_t)b, negq ^= 1;
else
ub = b;
uq = __qdivrem(ua, ub, &ur);
if (rem)
*rem = (negr ? -ur : ur);
return (negq ? -uq : uq);
}
u_quad_t
__udivmoddi4(u_quad_t a, u_quad_t b, u_quad_t *rem)
{
return __qdivrem(a, b, rem);
}
#else
static int __attribute__((unused)) dummy;
#endif /*deined (_X86_) && !defined (__x86_64__)*/

197
libs/winpthreads/src/misc.c Normal file
View File

@ -0,0 +1,197 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#include <windows.h>
#include "pthread.h"
#include "misc.h"
void (WINAPI *_pthread_get_system_time_best_as_file_time) (LPFILETIME) = NULL;
static ULONGLONG (WINAPI *_pthread_get_tick_count_64) (VOID);
#if defined(__GNUC__) || defined(__clang__)
__attribute__((constructor))
#endif
static void winpthreads_init(void)
{
HMODULE mod = GetModuleHandleA("kernel32.dll");
if (mod)
{
_pthread_get_tick_count_64 =
(ULONGLONG (WINAPI *)(VOID))(void*) GetProcAddress(mod, "GetTickCount64");
/* <1us precision on Windows 10 */
_pthread_get_system_time_best_as_file_time =
(void (WINAPI *)(LPFILETIME))(void*) GetProcAddress(mod, "GetSystemTimePreciseAsFileTime");
}
if (!_pthread_get_system_time_best_as_file_time)
/* >15ms precision on Windows 10 */
_pthread_get_system_time_best_as_file_time = GetSystemTimeAsFileTime;
}
#if defined(_MSC_VER) && !defined(__clang__)
/* Force a reference to __xc_t to prevent whole program optimization
* from discarding the variable. */
/* On x86, symbols are prefixed with an underscore. */
# if defined(_M_IX86)
# pragma comment(linker, "/include:___xc_t")
# else
# pragma comment(linker, "/include:__xc_t")
# endif
#pragma section(".CRT$XCT", long, read)
__declspec(allocate(".CRT$XCT"))
extern const _PVFV __xc_t;
const _PVFV __xc_t = winpthreads_init;
#endif
unsigned long long _pthread_time_in_ms(void)
{
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
return (((unsigned long long)ft.dwHighDateTime << 32) + ft.dwLowDateTime
- 0x19DB1DED53E8000ULL) / 10000ULL;
}
unsigned long long _pthread_time_in_ms_from_timespec(const struct timespec *ts)
{
unsigned long long t = (unsigned long long) ts->tv_sec * 1000LL;
/* The +999999 is here to ensure that the division always rounds up */
t += (unsigned long long) (ts->tv_nsec + 999999) / 1000000;
return t;
}
unsigned long long _pthread_rel_time_in_ms(const struct timespec *ts)
{
unsigned long long t1 = _pthread_time_in_ms_from_timespec(ts);
unsigned long long t2 = _pthread_time_in_ms();
/* Prevent underflow */
if (t1 < t2) return 0;
return t1 - t2;
}
static unsigned long long
_pthread_get_tick_count (long long *frequency)
{
if (_pthread_get_tick_count_64 != NULL)
return _pthread_get_tick_count_64 ();
LARGE_INTEGER freq, timestamp;
if (*frequency == 0)
{
if (QueryPerformanceFrequency (&freq))
*frequency = freq.QuadPart;
else
*frequency = -1;
}
if (*frequency > 0 && QueryPerformanceCounter (&timestamp))
return timestamp.QuadPart / (*frequency / 1000);
/* Fallback */
return GetTickCount ();
}
/* A wrapper around WaitForSingleObject() that ensures that
* the wait function does not time out before the time
* actually runs out. This is needed because WaitForSingleObject()
* might have poor accuracy, returning earlier than expected.
* On the other hand, returning a bit *later* than expected
* is acceptable in a preemptive multitasking environment.
*/
unsigned long
_pthread_wait_for_single_object (void *handle, unsigned long timeout)
{
DWORD result;
unsigned long long start_time, end_time;
unsigned long wait_time;
long long frequency = 0;
if (timeout == INFINITE || timeout == 0)
return WaitForSingleObject ((HANDLE) handle, (DWORD) timeout);
start_time = _pthread_get_tick_count (&frequency);
end_time = start_time + timeout;
wait_time = timeout;
do
{
unsigned long long current_time;
result = WaitForSingleObject ((HANDLE) handle, (DWORD) wait_time);
if (result != WAIT_TIMEOUT)
break;
current_time = _pthread_get_tick_count (&frequency);
if (current_time >= end_time)
break;
wait_time = (DWORD) (end_time - current_time);
} while (TRUE);
return result;
}
/* A wrapper around WaitForMultipleObjects() that ensures that
* the wait function does not time out before the time
* actually runs out. This is needed because WaitForMultipleObjects()
* might have poor accuracy, returning earlier than expected.
* On the other hand, returning a bit *later* than expected
* is acceptable in a preemptive multitasking environment.
*/
unsigned long
_pthread_wait_for_multiple_objects (unsigned long count, void **handles, unsigned int all, unsigned long timeout)
{
DWORD result;
unsigned long long start_time, end_time;
unsigned long wait_time;
long long frequency = 0;
if (timeout == INFINITE || timeout == 0)
return WaitForMultipleObjects ((DWORD) count, (HANDLE *) handles, all, (DWORD) timeout);
start_time = _pthread_get_tick_count (&frequency);
end_time = start_time + timeout;
wait_time = timeout;
do
{
unsigned long long current_time;
result = WaitForMultipleObjects ((DWORD) count, (HANDLE *) handles, all, (DWORD) wait_time);
if (result != WAIT_TIMEOUT)
break;
current_time = _pthread_get_tick_count (&frequency);
if (current_time >= end_time)
break;
wait_time = (DWORD) (end_time - current_time);
} while (TRUE);
return result;
}

126
libs/winpthreads/src/misc.h Normal file
View File

@ -0,0 +1,126 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#ifndef WIN_PTHREADS_MISC_H
#define WIN_PTHREADS_MISC_H
#include "pthread_compat.h"
#ifndef assert
#ifndef ASSERT_TRACE
# define ASSERT_TRACE 0
#else
# undef ASSERT_TRACE
# define ASSERT_TRACE 0
#endif
# define assert(e) \
((e) ? ((ASSERT_TRACE) ? fprintf(stderr, \
"Assertion succeeded: (%s), file %s, line %d\n", \
#e, __FILE__, (int) __LINE__), \
fflush(stderr) : \
0) : \
(fprintf(stderr, "Assertion failed: (%s), file %s, line %d\n", \
#e, __FILE__, (int) __LINE__), exit(1), 0))
# define fixme(e) \
((e) ? ((ASSERT_TRACE) ? fprintf(stderr, \
"Assertion succeeded: (%s), file %s, line %d\n", \
#e, __FILE__, (int) __LINE__), \
fflush(stderr) : \
0) : \
(fprintf(stderr, "FIXME: (%s), file %s, line %d\n", \
#e, __FILE__, (int) __LINE__), 0, 0))
#endif
#define PTR2INT(x) ((int)(uintptr_t)(x))
#if SIZE_MAX>UINT_MAX
typedef long long LONGBAG;
#else
typedef long LONGBAG;
#endif
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#undef GetHandleInformation
#define GetHandleInformation(h,f) (1)
#endif
#define CHECK_HANDLE(h) \
do { \
DWORD dwFlags; \
if (!(h) || ((h) == INVALID_HANDLE_VALUE) || !GetHandleInformation((h), &dwFlags)) \
return EINVAL; \
} while (0)
#define CHECK_PTR(p) do { if (!(p)) return EINVAL; } while (0)
#define UPD_RESULT(x,r) do { int _r = (x); (r) = (r) ? (r) : _r; } while (0)
#define CHECK_THREAD(t) \
do { \
CHECK_PTR(t); \
CHECK_HANDLE((t)->h); \
} while (0)
#define CHECK_OBJECT(o, e) \
do { \
DWORD dwFlags; \
if (!(o)) return e; \
if (!((o)->h) || (((o)->h) == INVALID_HANDLE_VALUE) || !GetHandleInformation(((o)->h), &dwFlags)) \
return e; \
} while (0)
#define VALID(x) if (!(p)) return EINVAL;
/* ms can be 64 bit, solve wrap-around issues: */
static WINPTHREADS_INLINE unsigned long dwMilliSecs(unsigned long long ms)
{
if (ms >= 0xffffffffULL) return 0xfffffffful;
return (unsigned long) ms;
}
unsigned long long _pthread_time_in_ms(void);
unsigned long long _pthread_time_in_ms_from_timespec(const struct timespec *ts);
unsigned long long _pthread_rel_time_in_ms(const struct timespec *ts);
unsigned long _pthread_wait_for_single_object (void *handle, unsigned long timeout);
unsigned long _pthread_wait_for_multiple_objects (unsigned long count, void **handles, unsigned int all, unsigned long timeout);
extern void (WINAPI *_pthread_get_system_time_best_as_file_time) (LPFILETIME);
#if defined(__GNUC__) || defined(__clang__)
#define likely(cond) __builtin_expect((cond) != 0, 1)
#define unlikely(cond) __builtin_expect((cond) != 0, 0)
#else
#define likely(cond) (cond)
#define unlikely(cond) (cond)
#endif
#if defined(__GNUC__) || defined(__clang__)
#define UNREACHABLE() __builtin_unreachable()
#elif defined(_MSC_VER)
#define UNREACHABLE() __assume(0)
#endif
#endif

View File

@ -0,0 +1,381 @@
/*
Copyright (c) 2011, 2014 mingw-w64 project
Copyright (c) 2015 Intel Corporation
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.
*/
#include <windows.h>
#include <stdio.h>
#include <malloc.h>
#include <stdbool.h>
#include "pthread.h"
#include "misc.h"
typedef enum {
Unlocked, /* Not locked. */
Locked, /* Locked but without waiters. */
Waiting, /* Locked, may have waiters. */
} mutex_state_t;
typedef enum {
Normal,
Errorcheck,
Recursive,
} mutex_type_t;
/* The heap-allocated part of a mutex. */
typedef struct {
mutex_state_t state;
mutex_type_t type;
HANDLE event; /* Auto-reset event, or NULL if not yet allocated. */
unsigned rec_lock; /* For recursive mutexes, the number of times the
mutex has been locked in excess by the same thread. */
volatile DWORD owner; /* For recursive and error-checking mutexes, the
ID of the owning thread if the mutex is locked. */
} mutex_impl_t;
/* Whether a mutex is still a static initializer (not a pointer to
a mutex_impl_t). */
static bool
is_static_initializer(pthread_mutex_t m)
{
/* Treat 0 as a static initializer as well (for normal mutexes),
to tolerate sloppy code in libgomp. (We should rather fix that code!) */
intptr_t v = (intptr_t)m;
return v >= -3 && v <= 0;
/* Should be simple:
return (uintptr_t)m >= (uintptr_t)-3; */
}
/* Create and return the implementation part of a mutex from a static
initialiser. Return NULL on out-of-memory error. */
static WINPTHREADS_ATTRIBUTE((noinline)) mutex_impl_t *
mutex_impl_init(pthread_mutex_t *m, mutex_impl_t *mi)
{
mutex_impl_t *new_mi = malloc(sizeof(mutex_impl_t));
if (new_mi == NULL)
return NULL;
new_mi->state = Unlocked;
new_mi->type = (mi == (void *)PTHREAD_RECURSIVE_MUTEX_INITIALIZER ? Recursive
: mi == (void *)PTHREAD_ERRORCHECK_MUTEX_INITIALIZER ? Errorcheck
: Normal);
new_mi->event = NULL;
new_mi->rec_lock = 0;
new_mi->owner = (DWORD)-1;
if (InterlockedCompareExchangePointer((PVOID volatile *)m, new_mi, mi) == mi) {
return new_mi;
} else {
/* Someone created the struct before us. */
free(new_mi);
return (mutex_impl_t *)*m;
}
}
/* Return the implementation part of a mutex, creating it if necessary.
Return NULL on out-of-memory error. */
static inline mutex_impl_t *
mutex_impl(pthread_mutex_t *m)
{
mutex_impl_t *mi = (mutex_impl_t *)*m;
if (is_static_initializer((pthread_mutex_t)mi)) {
return mutex_impl_init(m, mi);
} else {
/* mi cannot be null here; avoid a test in the fast path. */
if (mi == NULL)
UNREACHABLE();
return mi;
}
}
/* Lock a mutex. Give up after 'timeout' ms (with ETIMEDOUT),
or never if timeout=INFINITE. */
static inline int
pthread_mutex_lock_intern (pthread_mutex_t *m, DWORD timeout)
{
mutex_impl_t *mi = mutex_impl(m);
if (mi == NULL)
return ENOMEM;
mutex_state_t old_state = InterlockedExchange((long *)&mi->state, Locked);
if (unlikely(old_state != Unlocked)) {
/* The mutex is already locked. */
if (mi->type != Normal) {
/* Recursive or Errorcheck */
if (mi->owner == GetCurrentThreadId()) {
/* FIXME: A recursive mutex should not need two atomic ops when locking
recursively. We could rewrite by doing compare-and-swap instead of
test-and-set the first time, but it would lead to more code
duplication and add a conditional branch to the critical path. */
InterlockedCompareExchange((long *)&mi->state, old_state, Locked);
if (mi->type == Recursive) {
mi->rec_lock++;
return 0;
} else {
/* type == Errorcheck */
return EDEADLK;
}
}
}
/* Make sure there is an event object on which to wait. */
if (mi->event == NULL) {
/* Make an auto-reset event object. */
HANDLE ev = CreateEvent(NULL, false, false, NULL);
if (ev == NULL) {
switch (GetLastError()) {
case ERROR_ACCESS_DENIED:
return EPERM;
default:
return ENOMEM; /* Probably accurate enough. */
}
}
if (InterlockedCompareExchangePointer(&mi->event, ev, NULL) != NULL) {
/* Someone created the event before us. */
CloseHandle(ev);
}
}
/* At this point, mi->event is non-NULL. */
while (InterlockedExchange((long *)&mi->state, Waiting) != Unlocked) {
/* For timed locking attempts, it is possible (although unlikely)
that we are woken up but someone else grabs the lock before us,
and we have to go back to sleep again. In that case, the total
wait may be longer than expected. */
unsigned r = _pthread_wait_for_single_object(mi->event, timeout);
switch (r) {
case WAIT_TIMEOUT:
return ETIMEDOUT;
case WAIT_OBJECT_0:
break;
default:
return EINVAL;
}
}
}
if (mi->type != Normal)
mi->owner = GetCurrentThreadId();
return 0;
}
int
pthread_mutex_lock (pthread_mutex_t *m)
{
return pthread_mutex_lock_intern (m, INFINITE);
}
int pthread_mutex_timedlock(pthread_mutex_t *m, const struct timespec *ts)
{
unsigned long long patience;
if (ts != NULL) {
unsigned long long end = _pthread_time_in_ms_from_timespec(ts);
unsigned long long now = _pthread_time_in_ms();
patience = end > now ? end - now : 0;
if (patience > 0xffffffff)
patience = INFINITE;
} else {
patience = INFINITE;
}
return pthread_mutex_lock_intern(m, patience);
}
int pthread_mutex_unlock(pthread_mutex_t *m)
{
/* Here m might an initialiser of an error-checking or recursive mutex, in
which case the behaviour is well-defined, so we can't skip this check. */
mutex_impl_t *mi = mutex_impl(m);
if (mi == NULL)
return ENOMEM;
if (unlikely(mi->type != Normal)) {
if (mi->state == Unlocked)
return EINVAL;
if (mi->owner != GetCurrentThreadId())
return EPERM;
if (mi->rec_lock > 0) {
mi->rec_lock--;
return 0;
}
mi->owner = (DWORD)-1;
}
if (unlikely(InterlockedExchange((long *)&mi->state, Unlocked) == Waiting)) {
if (!SetEvent(mi->event))
return EPERM;
}
return 0;
}
int pthread_mutex_trylock(pthread_mutex_t *m)
{
mutex_impl_t *mi = mutex_impl(m);
if (mi == NULL)
return ENOMEM;
if (InterlockedCompareExchange((long *)&mi->state, Locked, Unlocked) == Unlocked) {
if (mi->type != Normal)
mi->owner = GetCurrentThreadId();
return 0;
} else {
if (mi->type == Recursive && mi->owner == GetCurrentThreadId()) {
mi->rec_lock++;
return 0;
}
return EBUSY;
}
}
int
pthread_mutex_init (pthread_mutex_t *m, const pthread_mutexattr_t *a)
{
pthread_mutex_t init = PTHREAD_MUTEX_INITIALIZER;
if (a != NULL) {
int pshared;
if (pthread_mutexattr_getpshared(a, &pshared) == 0
&& pshared == PTHREAD_PROCESS_SHARED)
return ENOSYS;
int type;
if (pthread_mutexattr_gettype(a, &type) == 0) {
switch (type) {
case PTHREAD_MUTEX_ERRORCHECK:
init = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER;
break;
case PTHREAD_MUTEX_RECURSIVE:
init = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
break;
default:
init = PTHREAD_MUTEX_INITIALIZER;
break;
}
}
}
*m = init;
return 0;
}
int pthread_mutex_destroy (pthread_mutex_t *m)
{
mutex_impl_t *mi = (mutex_impl_t *)*m;
if (!is_static_initializer((pthread_mutex_t)mi)) {
if (mi->event != NULL)
CloseHandle(mi->event);
free(mi);
/* Sabotage attempts to re-use the mutex before initialising it again. */
*m = (pthread_mutex_t)NULL;
}
return 0;
}
int pthread_mutexattr_init(pthread_mutexattr_t *a)
{
*a = PTHREAD_MUTEX_NORMAL | (PTHREAD_PROCESS_PRIVATE << 3);
return 0;
}
int pthread_mutexattr_destroy(pthread_mutexattr_t *a)
{
if (!a)
return EINVAL;
return 0;
}
int pthread_mutexattr_gettype(const pthread_mutexattr_t *a, int *type)
{
if (!a || !type)
return EINVAL;
*type = *a & 3;
return 0;
}
int pthread_mutexattr_settype(pthread_mutexattr_t *a, int type)
{
if (!a || (type != PTHREAD_MUTEX_NORMAL && type != PTHREAD_MUTEX_RECURSIVE && type != PTHREAD_MUTEX_ERRORCHECK))
return EINVAL;
*a &= ~3;
*a |= type;
return 0;
}
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *a, int *type)
{
if (!a || !type)
return EINVAL;
*type = (*a & 4 ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE);
return 0;
}
int pthread_mutexattr_setpshared(pthread_mutexattr_t * a, int type)
{
int r = 0;
if (!a || (type != PTHREAD_PROCESS_SHARED
&& type != PTHREAD_PROCESS_PRIVATE))
return EINVAL;
if (type == PTHREAD_PROCESS_SHARED)
{
type = PTHREAD_PROCESS_PRIVATE;
r = ENOSYS;
}
type = (type == PTHREAD_PROCESS_SHARED ? 4 : 0);
*a &= ~4;
*a |= type;
return r;
}
int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *a, int *type)
{
*type = *a & (8 + 16);
return 0;
}
int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int type)
{
if ((type & (8 + 16)) != 8 + 16) return EINVAL;
*a &= ~(8 + 16);
*a |= type;
return 0;
}
int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *a, int * prio)
{
*prio = *a / PTHREAD_PRIO_MULT;
return 0;
}
int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *a, int prio)
{
*a &= (PTHREAD_PRIO_MULT - 1);
*a += prio * PTHREAD_PRIO_MULT;
return 0;
}

View File

@ -0,0 +1,71 @@
/**
* This file has no copyright assigned and is placed in the Public Domain.
* This file is part of the w64 mingw-runtime package.
* No warranty is given; refer to the file DISCLAIMER.PD within this package.
*/
#include <errno.h>
#include <time.h>
#include <windows.h>
#include "pthread.h"
#include "pthread_time.h"
#include "winpthread_internal.h"
#define POW10_3 1000
#define POW10_4 10000
#define POW10_6 1000000
#define POW10_9 1000000000
#define MAX_SLEEP_IN_MS 4294967294UL
/**
* Sleep for the specified time.
* @param request The desired amount of time to sleep.
* @param remain The remain amount of time to sleep.
* @return If the function succeeds, the return value is 0.
* If the function fails, the return value is -1,
* with errno set to indicate the error.
*/
int nanosleep(const struct timespec *request, struct timespec *remain)
{
unsigned long ms, rc = 0;
unsigned __int64 u64, want, real;
union {
unsigned __int64 ns100;
FILETIME ft;
} _start, _end;
if (request->tv_sec < 0 || request->tv_nsec < 0 || request->tv_nsec >= POW10_9) {
errno = EINVAL;
return -1;
}
if (remain != NULL) GetSystemTimeAsFileTime(&_start.ft);
want = u64 = request->tv_sec * POW10_3 + request->tv_nsec / POW10_6;
while (u64 > 0 && rc == 0) {
if (u64 >= MAX_SLEEP_IN_MS) ms = MAX_SLEEP_IN_MS;
else ms = (unsigned long) u64;
u64 -= ms;
rc = pthread_delay_np_ms(ms);
}
if (rc != 0) { /* WAIT_IO_COMPLETION (192) */
if (remain != NULL) {
GetSystemTimeAsFileTime(&_end.ft);
real = (_end.ns100 - _start.ns100) / POW10_4;
if (real >= want) u64 = 0;
else u64 = want - real;
remain->tv_sec = u64 / POW10_3;
remain->tv_nsec = (long) (u64 % POW10_3) * POW10_6;
}
errno = EINTR;
return -1;
}
return 0;
}

View File

@ -0,0 +1,34 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#include <windows.h>
#include <winternl.h>
#include <stdio.h>
#include "pthread.h"
#include "semaphore.h"
#include "rwlock.h"
#include "cond.h"
#include "barrier.h"
#include "sem.h"
#include "ref.h"
#include "misc.h"

View File

@ -0,0 +1,29 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#ifndef WIN_PTHREADS_REF_H
#define WIN_PTHREADS_REF_H
#include "pthread.h"
#include "semaphore.h"
#endif

View File

@ -0,0 +1,537 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#include <windows.h>
#include <stdio.h>
#include <malloc.h>
#include "pthread.h"
#include "thread.h"
#include "ref.h"
#include "rwlock.h"
#include "misc.h"
static pthread_spinlock_t rwl_global = PTHREAD_SPINLOCK_INITIALIZER;
static WINPTHREADS_ATTRIBUTE((noinline)) int rwlock_static_init(pthread_rwlock_t *rw);
static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_unref(volatile pthread_rwlock_t *rwl, int res)
{
pthread_spin_lock(&rwl_global);
#ifdef WINPTHREAD_DBG
assert((((rwlock_t *)*rwl)->valid == LIFE_RWLOCK) && (((rwlock_t *)*rwl)->busy > 0));
#endif
((rwlock_t *)*rwl)->busy--;
pthread_spin_unlock(&rwl_global);
return res;
}
static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_ref(pthread_rwlock_t *rwl, int f )
{
int r = 0;
if (STATIC_RWL_INITIALIZER(*rwl)) {
r = rwlock_static_init(rwl);
if (r != 0 && r != EBUSY)
return r;
}
pthread_spin_lock(&rwl_global);
if (!rwl || !*rwl || ((rwlock_t *)*rwl)->valid != LIFE_RWLOCK) r = EINVAL;
else {
((rwlock_t *)*rwl)->busy ++;
}
pthread_spin_unlock(&rwl_global);
return r;
}
static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_ref_unlock(pthread_rwlock_t *rwl )
{
int r = 0;
pthread_spin_lock(&rwl_global);
if (!rwl || !*rwl || ((rwlock_t *)*rwl)->valid != LIFE_RWLOCK) r = EINVAL;
else if (STATIC_RWL_INITIALIZER(*rwl)) r= EPERM;
else {
((rwlock_t *)*rwl)->busy ++;
}
pthread_spin_unlock(&rwl_global);
return r;
}
static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_ref_destroy(pthread_rwlock_t *rwl, pthread_rwlock_t *rDestroy )
{
int r = 0;
*rDestroy = (pthread_rwlock_t)NULL;
pthread_spin_lock(&rwl_global);
if (!rwl || !*rwl) r = EINVAL;
else {
rwlock_t *r_ = (rwlock_t *)*rwl;
if (STATIC_RWL_INITIALIZER(*rwl)) *rwl = (pthread_rwlock_t)NULL;
else if (r_->valid != LIFE_RWLOCK) r = EINVAL;
else if (r_->busy) r = EBUSY;
else {
*rDestroy = *rwl;
*rwl = (pthread_rwlock_t)NULL;
}
}
pthread_spin_unlock(&rwl_global);
return r;
}
static int rwlock_gain_both_locks(rwlock_t *rwlock)
{
int ret;
ret = pthread_mutex_lock(&rwlock->mex);
if (ret != 0)
return ret;
ret = pthread_mutex_lock(&rwlock->mcomplete);
if (ret != 0)
pthread_mutex_unlock(&rwlock->mex);
return ret;
}
static int rwlock_free_both_locks(rwlock_t *rwlock, int last_fail)
{
int ret, ret2;
ret = pthread_mutex_unlock(&rwlock->mcomplete);
ret2 = pthread_mutex_unlock(&rwlock->mex);
if (last_fail && ret2 != 0)
ret = ret2;
else if (!last_fail && !ret)
ret = ret2;
return ret;
}
#ifdef WINPTHREAD_DBG
static int print_state = 0;
void rwl_print_set(int state)
{
print_state = state;
}
void rwl_print(volatile pthread_rwlock_t *rwl, char *txt)
{
if (!print_state) return;
rwlock_t *r = (rwlock_t *)*rwl;
if (r == NULL) {
printf("RWL%p %lu %s\n",(void *)*rwl,GetCurrentThreadId(),txt);
} else {
printf("RWL%p %lu V=%0X B=%d r=%ld w=%ld L=%p %s\n",
(void *)*rwl,
GetCurrentThreadId(),
(int)r->valid,
(int)r->busy,
0L,0L,NULL,txt);
}
}
#endif
static pthread_spinlock_t cond_locked = PTHREAD_SPINLOCK_INITIALIZER;
static WINPTHREADS_ATTRIBUTE((noinline)) int rwlock_static_init(pthread_rwlock_t *rw)
{
int r;
pthread_spin_lock(&cond_locked);
if (*rw != PTHREAD_RWLOCK_INITIALIZER)
{
pthread_spin_unlock(&cond_locked);
return EINVAL;
}
r = pthread_rwlock_init (rw, NULL);
pthread_spin_unlock(&cond_locked);
return r;
}
int pthread_rwlock_init (pthread_rwlock_t *rwlock_, const pthread_rwlockattr_t *attr)
{
rwlock_t *rwlock;
int r;
if(!rwlock_)
return EINVAL;
*rwlock_ = (pthread_rwlock_t)NULL;
if ((rwlock = calloc(1, sizeof(*rwlock))) == NULL)
return ENOMEM;
rwlock->valid = DEAD_RWLOCK;
rwlock->nex_count = rwlock->nsh_count = rwlock->ncomplete = 0;
if ((r = pthread_mutex_init (&rwlock->mex, NULL)) != 0)
{
free(rwlock);
return r;
}
if ((r = pthread_mutex_init (&rwlock->mcomplete, NULL)) != 0)
{
pthread_mutex_destroy(&rwlock->mex);
free(rwlock);
return r;
}
if ((r = pthread_cond_init (&rwlock->ccomplete, NULL)) != 0)
{
pthread_mutex_destroy(&rwlock->mex);
pthread_mutex_destroy (&rwlock->mcomplete);
free(rwlock);
return r;
}
rwlock->valid = LIFE_RWLOCK;
*rwlock_ = (pthread_rwlock_t)rwlock;
return r;
}
int pthread_rwlock_destroy (pthread_rwlock_t *rwlock_)
{
rwlock_t *rwlock;
pthread_rwlock_t rDestroy;
int r, r2;
pthread_spin_lock(&cond_locked);
r = rwl_ref_destroy(rwlock_,&rDestroy);
pthread_spin_unlock(&cond_locked);
if(r) return r;
if(!rDestroy) return 0; /* destroyed a (still) static initialized rwl */
rwlock = (rwlock_t *)rDestroy;
r = rwlock_gain_both_locks (rwlock);
if (r != 0)
{
*rwlock_ = rDestroy;
return r;
}
if (rwlock->nsh_count > rwlock->ncomplete || rwlock->nex_count > 0)
{
*rwlock_ = rDestroy;
r = rwlock_free_both_locks(rwlock, 1);
if (!r)
r = EBUSY;
return r;
}
rwlock->valid = DEAD_RWLOCK;
r = rwlock_free_both_locks(rwlock, 0);
if (r != 0) { *rwlock_ = rDestroy; return r; }
r = pthread_cond_destroy(&rwlock->ccomplete);
r2 = pthread_mutex_destroy(&rwlock->mex);
if (!r) r = r2;
r2 = pthread_mutex_destroy(&rwlock->mcomplete);
if (!r) r = r2;
rwlock->valid = DEAD_RWLOCK;
free((void *)rDestroy);
return 0;
}
int pthread_rwlock_rdlock (pthread_rwlock_t *rwlock_)
{
rwlock_t *rwlock;
int ret;
/* pthread_testcancel(); */
ret = rwl_ref(rwlock_,0);
if(ret != 0) return ret;
rwlock = (rwlock_t *)*rwlock_;
ret = pthread_mutex_lock(&rwlock->mex);
if (ret != 0) return rwl_unref(rwlock_, ret);
InterlockedIncrement((long*)&rwlock->nsh_count);
if (rwlock->nsh_count == INT_MAX)
{
ret = pthread_mutex_lock(&rwlock->mcomplete);
if (ret != 0)
{
pthread_mutex_unlock(&rwlock->mex);
return rwl_unref(rwlock_,ret);
}
rwlock->nsh_count -= rwlock->ncomplete;
rwlock->ncomplete = 0;
ret = rwlock_free_both_locks(rwlock, 0);
return rwl_unref(rwlock_, ret);
}
ret = pthread_mutex_unlock(&rwlock->mex);
return rwl_unref(rwlock_, ret);
}
int pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock_, const struct timespec *ts)
{
rwlock_t *rwlock;
int ret;
/* pthread_testcancel(); */
ret = rwl_ref(rwlock_,0);
if(ret != 0) return ret;
rwlock = (rwlock_t *)*rwlock_;
if ((ret = pthread_mutex_timedlock (&rwlock->mex, ts)) != 0)
return rwl_unref(rwlock_, ret);
InterlockedIncrement(&rwlock->nsh_count);
if (rwlock->nsh_count == INT_MAX)
{
ret = pthread_mutex_timedlock(&rwlock->mcomplete, ts);
if (ret != 0)
{
if (ret == ETIMEDOUT)
InterlockedIncrement(&rwlock->ncomplete);
pthread_mutex_unlock(&rwlock->mex);
return rwl_unref(rwlock_, ret);
}
rwlock->nsh_count -= rwlock->ncomplete;
rwlock->ncomplete = 0;
ret = rwlock_free_both_locks(rwlock, 0);
return rwl_unref(rwlock_, ret);
}
ret = pthread_mutex_unlock(&rwlock->mex);
return rwl_unref(rwlock_, ret);
}
int pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock_)
{
rwlock_t *rwlock;
int ret;
ret = rwl_ref(rwlock_,RWL_TRY);
if(ret != 0) return ret;
rwlock = (rwlock_t *)*rwlock_;
ret = pthread_mutex_trylock(&rwlock->mex);
if (ret != 0)
return rwl_unref(rwlock_, ret);
InterlockedIncrement(&rwlock->nsh_count);
if (rwlock->nsh_count == INT_MAX)
{
ret = pthread_mutex_lock(&rwlock->mcomplete);
if (ret != 0)
{
pthread_mutex_unlock(&rwlock->mex);
return rwl_unref(rwlock_, ret);
}
rwlock->nsh_count -= rwlock->ncomplete;
rwlock->ncomplete = 0;
ret = rwlock_free_both_locks(rwlock, 0);
return rwl_unref(rwlock_, ret);
}
ret = pthread_mutex_unlock(&rwlock->mex);
return rwl_unref(rwlock_,ret);
}
int pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock_)
{
rwlock_t *rwlock;
int ret;
ret = rwl_ref(rwlock_,RWL_TRY);
if(ret != 0) return ret;
rwlock = (rwlock_t *)*rwlock_;
ret = pthread_mutex_trylock (&rwlock->mex);
if (ret != 0)
return rwl_unref(rwlock_, ret);
ret = pthread_mutex_trylock(&rwlock->mcomplete);
if (ret != 0)
{
int r1 = pthread_mutex_unlock(&rwlock->mex);
if (r1 != 0)
ret = r1;
return rwl_unref(rwlock_, ret);
}
if (rwlock->nex_count != 0)
return rwl_unref(rwlock_, EBUSY);
if (rwlock->ncomplete > 0)
{
rwlock->nsh_count -= rwlock->ncomplete;
rwlock->ncomplete = 0;
}
if (rwlock->nsh_count > 0)
{
ret = rwlock_free_both_locks(rwlock, 0);
if (!ret)
ret = EBUSY;
return rwl_unref(rwlock_, ret);
}
rwlock->nex_count = 1;
return rwl_unref(rwlock_, 0);
}
int pthread_rwlock_unlock (pthread_rwlock_t *rwlock_)
{
rwlock_t *rwlock;
int ret;
ret = rwl_ref_unlock(rwlock_);
if(ret != 0) return ret;
rwlock = (rwlock_t *)*rwlock_;
if (rwlock->nex_count == 0)
{
ret = pthread_mutex_lock(&rwlock->mcomplete);
if (!ret)
{
int r1;
InterlockedIncrement(&rwlock->ncomplete);
if (rwlock->ncomplete == 0)
ret = pthread_cond_signal(&rwlock->ccomplete);
r1 = pthread_mutex_unlock(&rwlock->mcomplete);
if (!ret)
ret = r1;
}
}
else
{
InterlockedDecrement(&rwlock->nex_count);
ret = rwlock_free_both_locks(rwlock, 0);
}
return rwl_unref(rwlock_, ret);
}
static void st_cancelwrite (void *arg)
{
rwlock_t *rwlock = (rwlock_t *)arg;
rwlock->nsh_count = - rwlock->ncomplete;
rwlock->ncomplete = 0;
rwlock_free_both_locks(rwlock, 0);
}
int pthread_rwlock_wrlock (pthread_rwlock_t *rwlock_)
{
rwlock_t *rwlock;
int ret;
/* pthread_testcancel(); */
ret = rwl_ref(rwlock_,0);
if(ret != 0) return ret;
rwlock = (rwlock_t *)*rwlock_;
ret = rwlock_gain_both_locks(rwlock);
if (ret != 0)
return rwl_unref(rwlock_,ret);
if (rwlock->nex_count == 0)
{
if (rwlock->ncomplete > 0)
{
rwlock->nsh_count -= rwlock->ncomplete;
rwlock->ncomplete = 0;
}
if (rwlock->nsh_count > 0)
{
rwlock->ncomplete = -rwlock->nsh_count;
pthread_cleanup_push(st_cancelwrite, (void *) rwlock);
do {
ret = pthread_cond_wait(&rwlock->ccomplete, &rwlock->mcomplete);
} while (!ret && rwlock->ncomplete < 0);
pthread_cleanup_pop(!ret ? 0 : 1);
if (!ret)
rwlock->nsh_count = 0;
}
}
if(!ret)
InterlockedIncrement((long*)&rwlock->nex_count);
return rwl_unref(rwlock_,ret);
}
int pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock_, const struct timespec *ts)
{
int ret;
rwlock_t *rwlock;
/* pthread_testcancel(); */
if (!rwlock_ || !ts)
return EINVAL;
if ((ret = rwl_ref(rwlock_,0)) != 0)
return ret;
rwlock = (rwlock_t *)*rwlock_;
ret = pthread_mutex_timedlock(&rwlock->mex, ts);
if (ret != 0)
return rwl_unref(rwlock_,ret);
ret = pthread_mutex_timedlock (&rwlock->mcomplete, ts);
if (ret != 0)
{
pthread_mutex_unlock(&rwlock->mex);
return rwl_unref(rwlock_,ret);
}
if (rwlock->nex_count == 0)
{
if (rwlock->ncomplete > 0)
{
rwlock->nsh_count -= rwlock->ncomplete;
rwlock->ncomplete = 0;
}
if (rwlock->nsh_count > 0)
{
rwlock->ncomplete = -rwlock->nsh_count;
pthread_cleanup_push(st_cancelwrite, (void *) rwlock);
do {
ret = pthread_cond_timedwait(&rwlock->ccomplete, &rwlock->mcomplete, ts);
} while (rwlock->ncomplete < 0 && !ret);
pthread_cleanup_pop(!ret ? 0 : 1);
if (!ret)
rwlock->nsh_count = 0;
}
}
if(!ret)
InterlockedIncrement((long*)&rwlock->nex_count);
return rwl_unref(rwlock_,ret);
}
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *a)
{
if (!a)
return EINVAL;
return 0;
}
int pthread_rwlockattr_init(pthread_rwlockattr_t *a)
{
if (!a)
return EINVAL;
*a = PTHREAD_PROCESS_PRIVATE;
return 0;
}
int pthread_rwlockattr_getpshared(pthread_rwlockattr_t *a, int *s)
{
if (!a || !s)
return EINVAL;
*s = *a;
return 0;
}
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *a, int s)
{
if (!a || (s != PTHREAD_PROCESS_SHARED && s != PTHREAD_PROCESS_PRIVATE))
return EINVAL;
*a = s;
return 0;
}

View File

@ -0,0 +1,49 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#ifndef WIN_PTHREADS_RWLOCK_H
#define WIN_PTHREADS_RWLOCK_H
#define LIFE_RWLOCK 0xBAB1F0ED
#define DEAD_RWLOCK 0xDEADB0EF
#define STATIC_RWL_INITIALIZER(x) ((pthread_rwlock_t)(x) == ((pthread_rwlock_t)PTHREAD_RWLOCK_INITIALIZER))
typedef struct rwlock_t rwlock_t;
struct rwlock_t {
unsigned int valid;
int busy;
LONG nex_count; /* Exclusive access counter. */
LONG nsh_count; /* Shared access counter. */
LONG ncomplete; /* Shared completed counter. */
pthread_mutex_t mex; /* Exclusive access protection. */
pthread_mutex_t mcomplete; /* Shared completed protection. */
pthread_cond_t ccomplete; /* Shared access completed queue. */
};
#define RWL_SET 0x01
#define RWL_TRY 0x02
void rwl_print(volatile pthread_rwlock_t *rwl, char *txt);
void rwl_print_set(int state);
#endif

View File

@ -0,0 +1,218 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#include <windows.h>
#include <stdio.h>
#include "pthread.h"
#include "thread.h"
#include "misc.h"
int sched_get_priority_min(int pol)
{
if (pol < SCHED_MIN || pol > SCHED_MAX) {
errno = EINVAL;
return -1;
}
return THREAD_PRIORITY_IDLE;
}
int sched_get_priority_max(int pol)
{
if (pol < SCHED_MIN || pol > SCHED_MAX) {
errno = EINVAL;
return -1;
}
return THREAD_PRIORITY_TIME_CRITICAL;
}
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *p)
{
int r = 0;
if (attr == NULL || p == NULL) {
return EINVAL;
}
memcpy(&attr->param, p, sizeof (*p));
return r;
}
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *p)
{
int r = 0;
if (attr == NULL || p == NULL) {
return EINVAL;
}
memcpy(p, &attr->param, sizeof (*p));
return r;
}
int pthread_attr_setschedpolicy (pthread_attr_t *attr, int pol)
{
if (!attr || pol < SCHED_MIN || pol > SCHED_MAX)
return EINVAL;
if (pol != SCHED_OTHER)
return ENOTSUP;
return 0;
}
int pthread_attr_getschedpolicy (const pthread_attr_t *attr, int *pol)
{
if (!attr || !pol)
return EINVAL;
*pol = SCHED_OTHER;
return 0;
}
static int pthread_check(pthread_t t)
{
struct _pthread_v *pv;
if (!t)
return ESRCH;
pv = __pth_gpointer_locked (t);
if (pv->ended == 0)
return 0;
CHECK_OBJECT(pv, ESRCH);
return 0;
}
int pthread_getschedparam(pthread_t t, int *pol, struct sched_param *p)
{
int r;
//if (!t)
// t = pthread_self();
if ((r = pthread_check(t)) != 0)
{
return r;
}
if (!p || !pol)
{
return EINVAL;
}
*pol = __pth_gpointer_locked (t)->sched_pol;
p->sched_priority = __pth_gpointer_locked (t)->sched.sched_priority;
return 0;
}
int pthread_setschedparam(pthread_t t, int pol, const struct sched_param *p)
{
struct _pthread_v *pv;
int r, pr = 0;
//if (!t.p) t = pthread_self();
if ((r = pthread_check(t)) != 0)
return r;
if (pol < SCHED_MIN || pol > SCHED_MAX || p == NULL)
return EINVAL;
if (pol != SCHED_OTHER)
return ENOTSUP;
pr = p->sched_priority;
if (pr < sched_get_priority_min(pol) || pr > sched_get_priority_max(pol))
return EINVAL;
/* See msdn: there are actually 7 priorities:
THREAD_PRIORITY_IDLE - -15
THREAD_PRIORITY_LOWEST -2
THREAD_PRIORITY_BELOW_NORMAL -1
THREAD_PRIORITY_NORMAL 0
THREAD_PRIORITY_ABOVE_NORMAL 1
THREAD_PRIORITY_HIGHEST 2
THREAD_PRIORITY_TIME_CRITICAL 15
*/
if (pr <= THREAD_PRIORITY_IDLE) {
pr = THREAD_PRIORITY_IDLE;
} else if (pr <= THREAD_PRIORITY_LOWEST) {
pr = THREAD_PRIORITY_LOWEST;
} else if (pr >= THREAD_PRIORITY_TIME_CRITICAL) {
pr = THREAD_PRIORITY_TIME_CRITICAL;
} else if (pr >= THREAD_PRIORITY_HIGHEST) {
pr = THREAD_PRIORITY_HIGHEST;
}
pv = __pth_gpointer_locked (t);
if (SetThreadPriority(pv->h, pr)) {
pv->sched_pol = pol;
pv->sched.sched_priority = p->sched_priority;
} else
r = EINVAL;
return r;
}
int sched_getscheduler(pid_t pid)
{
if (pid != 0)
{
HANDLE h = NULL;
int selfPid = (int) GetCurrentProcessId ();
if (pid != (pid_t) selfPid && (h = OpenProcess (PROCESS_QUERY_INFORMATION, 0, (DWORD) pid)) == NULL)
{
errno = (GetLastError () == (0xFF & ERROR_ACCESS_DENIED)) ? EPERM : ESRCH;
return -1;
}
if (h)
CloseHandle (h);
}
return SCHED_OTHER;
}
int sched_setscheduler(pid_t pid, int pol, const struct sched_param *param)
{
if (!param)
{
errno = EINVAL;
return -1;
}
if (pid != 0)
{
HANDLE h = NULL;
int selfPid = (int) GetCurrentProcessId ();
if (pid != (pid_t) selfPid && (h = OpenProcess (PROCESS_SET_INFORMATION, 0, (DWORD) pid)) == NULL)
{
errno = (GetLastError () == (0xFF & ERROR_ACCESS_DENIED)) ? EPERM : ESRCH;
return -1;
}
if (h)
CloseHandle (h);
}
if (pol != SCHED_OTHER)
{
errno = ENOSYS;
return -1;
}
return SCHED_OTHER;
}
int sched_yield(void)
{
Sleep(0);
return 0;
}

354
libs/winpthreads/src/sem.c Normal file
View File

@ -0,0 +1,354 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#include <windows.h>
#include <stdio.h>
#include <malloc.h>
#include "pthread.h"
#include "thread.h"
#include "misc.h"
#include "semaphore.h"
#include "sem.h"
#include "ref.h"
int do_sema_b_wait_intern (HANDLE sema, int nointerrupt, DWORD timeout);
static int
sem_result (int res)
{
if (res != 0) {
errno = res;
return -1;
}
return 0;
}
int
sem_init (sem_t *sem, int pshared, unsigned int value)
{
_sem_t *sv;
if (!sem || value > (unsigned int)SEM_VALUE_MAX)
return sem_result (EINVAL);
if (pshared != PTHREAD_PROCESS_PRIVATE)
return sem_result (EPERM);
if ((sv = (sem_t) calloc (1,sizeof (*sv))) == NULL)
return sem_result (ENOMEM);
sv->value = value;
if (pthread_mutex_init (&sv->vlock, NULL) != 0)
{
free (sv);
return sem_result (ENOSPC);
}
if ((sv->s = CreateSemaphore (NULL, 0, SEM_VALUE_MAX, NULL)) == NULL)
{
pthread_mutex_destroy (&sv->vlock);
free (sv);
return sem_result (ENOSPC);
}
sv->valid = LIFE_SEM;
*sem = sv;
return 0;
}
int
sem_destroy (sem_t *sem)
{
int r;
_sem_t *sv = NULL;
if (!sem || (sv = *sem) == NULL)
return sem_result (EINVAL);
if ((r = pthread_mutex_lock (&sv->vlock)) != 0)
return sem_result (r);
#if 0
/* We don't wait for destroying a semaphore ...
or? */
if (sv->value < 0)
{
pthread_mutex_unlock (&sv->vlock);
return sem_result (EBUSY);
}
#endif
if (!CloseHandle (sv->s))
{
pthread_mutex_unlock (&sv->vlock);
return sem_result (EINVAL);
}
*sem = NULL;
sv->value = SEM_VALUE_MAX;
pthread_mutex_unlock(&sv->vlock);
Sleep (0);
while (pthread_mutex_destroy (&sv->vlock) == EBUSY)
Sleep (0);
sv->valid = DEAD_SEM;
free (sv);
return 0;
}
static int
sem_std_enter (sem_t *sem,_sem_t **svp, int do_test)
{
int r;
_sem_t *sv;
if (do_test)
pthread_testcancel ();
if (!sem)
return sem_result (EINVAL);
sv = *sem;
if (sv == NULL)
return sem_result (EINVAL);
if ((r = pthread_mutex_lock (&sv->vlock)) != 0)
return sem_result (r);
if (*sem == NULL)
{
pthread_mutex_unlock(&sv->vlock);
return sem_result (EINVAL);
}
*svp = sv;
return 0;
}
int
sem_trywait (sem_t *sem)
{
_sem_t *sv;
if (sem_std_enter (sem, &sv, 0) != 0)
return -1;
if (sv->value <= 0)
{
pthread_mutex_unlock (&sv->vlock);
return sem_result (EAGAIN);
}
sv->value--;
pthread_mutex_unlock (&sv->vlock);
return 0;
}
struct sSemTimedWait
{
sem_t *p;
int *ret;
};
static void
clean_wait_sem (void *s)
{
struct sSemTimedWait *p = (struct sSemTimedWait *) s;
_sem_t *sv = NULL;
if (sem_std_enter (p->p, &sv, 0) != 0)
return;
if (WaitForSingleObject (sv->s, 0) != WAIT_OBJECT_0)
InterlockedIncrement (&sv->value);
else if (p->ret)
p->ret[0] = 0;
pthread_mutex_unlock (&sv->vlock);
}
int
sem_wait (sem_t *sem)
{
long cur_v;
int ret = 0;
_sem_t *sv;
HANDLE semh;
struct sSemTimedWait arg;
if (sem_std_enter (sem, &sv, 1) != 0)
return -1;
arg.ret = &ret;
arg.p = sem;
InterlockedDecrement (&sv->value);
cur_v = sv->value;
semh = sv->s;
pthread_mutex_unlock (&sv->vlock);
if (cur_v >= 0)
return 0;
else
{
pthread_cleanup_push (clean_wait_sem, (void *) &arg);
ret = do_sema_b_wait_intern (semh, 2, INFINITE);
pthread_cleanup_pop (ret);
if (ret == EINVAL)
return 0;
}
if (!ret)
return 0;
return sem_result (ret);
}
int
sem_timedwait (sem_t *sem, const struct timespec *t)
{
int cur_v, ret = 0;
DWORD dwr;
HANDLE semh;
_sem_t *sv;
struct sSemTimedWait arg;
if (!t)
return sem_wait (sem);
dwr = dwMilliSecs(_pthread_rel_time_in_ms (t));
if (sem_std_enter (sem, &sv, 1) != 0)
return -1;
arg.ret = &ret;
arg.p = sem;
InterlockedDecrement (&sv->value);
cur_v = sv->value;
semh = sv->s;
pthread_mutex_unlock(&sv->vlock);
if (cur_v >= 0)
return 0;
else
{
pthread_cleanup_push (clean_wait_sem, (void *) &arg);
ret = do_sema_b_wait_intern (semh, 2, dwr);
pthread_cleanup_pop (ret);
if (ret == EINVAL)
return 0;
}
if (!ret)
return 0;
return sem_result (ret);
}
int
sem_post (sem_t *sem)
{
_sem_t *sv;
if (sem_std_enter (sem, &sv, 0) != 0)
return -1;
if (sv->value >= SEM_VALUE_MAX)
{
pthread_mutex_unlock (&sv->vlock);
return sem_result (ERANGE);
}
InterlockedIncrement (&sv->value);
if (sv->value > 0 || ReleaseSemaphore (sv->s, 1, NULL))
{
pthread_mutex_unlock (&sv->vlock);
return 0;
}
InterlockedDecrement (&sv->value);
pthread_mutex_unlock (&sv->vlock);
return sem_result (EINVAL);
}
int
sem_post_multiple (sem_t *sem, int count)
{
int waiters_count;
_sem_t *sv;
if (count <= 0)
return sem_result (EINVAL);
if (sem_std_enter (sem, &sv, 0) != 0)
return -1;
if (sv->value > (SEM_VALUE_MAX - count))
{
pthread_mutex_unlock (&sv->vlock);
return sem_result (ERANGE);
}
waiters_count = -sv->value;
sv->value += count;
/*InterlockedExchangeAdd((long*)&sv->value, (long) count);*/
if (waiters_count <= 0
|| ReleaseSemaphore (sv->s,
(waiters_count < count ? waiters_count
: count), NULL))
{
pthread_mutex_unlock(&sv->vlock);
return 0;
}
/*InterlockedExchangeAdd((long*)&sv->value, -((long) count));*/
sv->value -= count;
pthread_mutex_unlock(&sv->vlock);
return sem_result (EINVAL);
}
sem_t *
sem_open (const char *name, int oflag, mode_t mode, unsigned int value)
{
sem_result (ENOSYS);
return NULL;
}
int
sem_close (sem_t *sem)
{
return sem_result (ENOSYS);
}
int
sem_unlink (const char *name)
{
return sem_result (ENOSYS);
}
int
sem_getvalue (sem_t *sem, int *sval)
{
_sem_t *sv;
int r;
if (!sval)
return sem_result (EINVAL);
if (!sem || (sv = *sem) == NULL)
return sem_result (EINVAL);
if ((r = pthread_mutex_lock (&sv->vlock)) != 0)
return sem_result (r);
if (*sem == NULL)
{
pthread_mutex_unlock (&sv->vlock);
return sem_result (EINVAL);
}
*sval = (int) sv->value;
pthread_mutex_unlock (&sv->vlock);
return 0;
}

View File

@ -0,0 +1,40 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#ifndef WIN_SEM
#define WIN_SEM
#include <windows.h>
#define LIFE_SEM 0xBAB1F00D
#define DEAD_SEM 0xDEADBEEF
typedef struct _sem_t _sem_t;
struct _sem_t
{
unsigned int valid;
HANDLE s;
volatile long value;
pthread_mutex_t vlock;
};
#endif /* WIN_SEM */

View File

@ -0,0 +1,74 @@
/*
Copyright (c) 2013 mingw-w64 project
Copyright (c) 2015 Intel Corporation
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.
*/
#include <windows.h>
#include "pthread.h"
#include "misc.h"
/* We use the pthread_spinlock_t itself as a lock:
-1 is free, 0 is locked.
(This is dictated by PTHREAD_SPINLOCK_INITIALIZER, which we can't change
without breaking binary compatibility.) */
typedef intptr_t spinlock_word_t;
int
pthread_spin_init (pthread_spinlock_t *lock, int pshared)
{
spinlock_word_t *lk = (spinlock_word_t *)lock;
*lk = -1;
return 0;
}
int
pthread_spin_destroy (pthread_spinlock_t *lock)
{
return 0;
}
int
pthread_spin_lock (pthread_spinlock_t *lock)
{
volatile spinlock_word_t *lk = (volatile spinlock_word_t *)lock;
while (unlikely(InterlockedExchangePointer((PVOID volatile *)lk, 0) == 0))
do {
YieldProcessor();
} while (*lk == 0);
return 0;
}
int
pthread_spin_trylock (pthread_spinlock_t *lock)
{
spinlock_word_t *lk = (spinlock_word_t *)lock;
return InterlockedExchangePointer((PVOID volatile *)lk, 0) == 0 ? EBUSY : 0;
}
int
pthread_spin_unlock (pthread_spinlock_t *lock)
{
volatile spinlock_word_t *lk = (volatile spinlock_word_t *)lock;
*lk = -1;
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,79 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#ifndef WIN_PTHREAD_H
#define WIN_PTHREAD_H
#include <windows.h>
#include <setjmp.h>
#include "rwlock.h"
#define LIFE_THREAD 0xBAB1F00D
#define DEAD_THREAD 0xDEADBEEF
#define EXCEPTION_SET_THREAD_NAME ((DWORD) 0x406D1388)
typedef struct _pthread_v _pthread_v;
struct _pthread_v
{
unsigned int valid;
void *ret_arg;
void *(* func)(void *);
_pthread_cleanup *clean;
int nobreak;
HANDLE h;
HANDLE evStart;
pthread_mutex_t p_clock;
int cancelled : 2;
int in_cancel : 2;
int thread_noposix : 2;
unsigned int p_state;
unsigned int keymax;
void **keyval;
unsigned char *keyval_set;
char *thread_name;
pthread_spinlock_t spin_keys;
DWORD tid;
int rwlc;
pthread_rwlock_t rwlq[RWLS_PER_THREAD];
int sched_pol;
int ended;
struct sched_param sched;
jmp_buf jb;
struct _pthread_v *next;
pthread_t x; /* Internal posix handle. */
};
typedef struct __pthread_idlist {
struct _pthread_v *ptr;
pthread_t id;
} __pthread_idlist;
int _pthread_tryjoin(pthread_t t, void **res);
void _pthread_setnobreak(int);
#ifdef WINPTHREAD_DBG
void thread_print_set(int state);
void thread_print(volatile pthread_t t, char *txt);
#endif
int __pthread_shallcancel(void);
struct _pthread_v *WINPTHREAD_API __pth_gpointer_locked (pthread_t id);
#endif

View File

@ -0,0 +1,27 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#ifndef WINPTHREAD_INTERNAL_H
#define WINPTHREAD_INTERNAL_H
WINPTHREAD_API struct _pthread_v * __pth_gpointer_locked (pthread_t id);
int pthread_delay_np_ms (DWORD to);
#endif /*WINPTHREAD_INTERNAL_H*/

View File

@ -0,0 +1,29 @@
/*
Copyright (c) 2011-2016 mingw-w64 project
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.
*/
#ifndef __WPTHREADS_VERSION__
#define __WPTHREADS_VERSION__
#define WPTH_VERSION 1,0,0,0
#define WPTH_VERSION_STRING "1, 0, 0, 0\0"
#endif

59
src/backend_dx12.zig Normal file
View File

@ -0,0 +1,59 @@
pub const D3D12_CPU_DESCRIPTOR_HANDLE = extern struct {
ptr: c_ulonglong,
};
pub const D3D12_GPU_DESCRIPTOR_HANDLE = extern struct {
ptr: c_ulonglong,
};
pub fn init(
device: *const anyopaque, // ID3D12Device
num_frames_in_flight: u32,
rtv_format: c_uint, // DXGI_FORMAT
cbv_srv_heap: *const anyopaque, // ID3D12DescriptorHeap
font_srv_cpu_desc_handle: D3D12_CPU_DESCRIPTOR_HANDLE,
font_srv_gpu_desc_handle: D3D12_GPU_DESCRIPTOR_HANDLE,
) void {
if (!ImGui_ImplDX12_Init(
device,
num_frames_in_flight,
rtv_format,
cbv_srv_heap,
font_srv_cpu_desc_handle,
font_srv_gpu_desc_handle,
)) {
@panic("failed to init d3d12 for imgui");
}
}
pub fn deinit() void {
ImGui_ImplDX12_Shutdown();
}
pub fn newFrame() void {
ImGui_ImplDX12_NewFrame();
}
pub fn render(
draw_data: *const anyopaque, // *gui.DrawData
gfx_command_list: *const anyopaque, // *ID3D12GraphicsCommandList
) void {
ImGui_ImplDX12_RenderDrawData(draw_data, gfx_command_list);
}
// Those functions are defined in 'imgui_impl_dx12.cpp`
// (they include few custom changes).
extern fn ImGui_ImplDX12_Init(
device: *const anyopaque, // ID3D12Device
num_frames_in_flight: u32,
rtv_format: u32, // DXGI_FORMAT
cbv_srv_heap: *const anyopaque, // ID3D12DescriptorHeap
font_srv_cpu_desc_handle: D3D12_CPU_DESCRIPTOR_HANDLE,
font_srv_gpu_desc_handle: D3D12_GPU_DESCRIPTOR_HANDLE,
) bool;
extern fn ImGui_ImplDX12_Shutdown() void;
extern fn ImGui_ImplDX12_NewFrame() void;
extern fn ImGui_ImplDX12_RenderDrawData(
draw_data: *const anyopaque, // *ImDrawData
graphics_command_list: *const anyopaque, // *ID3D12GraphicsCommandList
) void;

35
src/backend_glfw.zig Normal file
View File

@ -0,0 +1,35 @@
const gui = @import("gui.zig");
// This call will install GLFW callbacks to handle GUI interactions.
// Those callbacks will chain-call user's previously installed callbacks, if any.
// This means that custom user's callbacks need to be installed *before* calling zgpu.gui.init().
pub fn init(
window: *const anyopaque, // zglfw.Window
) void {
if (!ImGui_ImplGlfw_InitForOther(window, true)) {
unreachable;
}
}
pub fn initOpenGL(
window: *const anyopaque, // zglfw.Window
) void {
if (!ImGui_ImplGlfw_InitForOpenGL(window, true)) {
unreachable;
}
}
pub fn deinit() void {
ImGui_ImplGlfw_Shutdown();
}
pub fn newFrame() void {
ImGui_ImplGlfw_NewFrame();
}
// Those functions are defined in `imgui_impl_glfw.cpp`
// (they include few custom changes).
extern fn ImGui_ImplGlfw_InitForOther(window: *const anyopaque, install_callbacks: bool) bool;
extern fn ImGui_ImplGlfw_InitForOpenGL(window: *const anyopaque, install_callbacks: bool) bool;
extern fn ImGui_ImplGlfw_NewFrame() void;
extern fn ImGui_ImplGlfw_Shutdown() void;

View File

@ -1,4 +1,6 @@
const gui = @import("gui.zig"); const gui = @import("gui.zig");
const backend_glfw = @import("backend_glfw.zig");
const backend_dx12 = @import("backend_dx12.zig");
pub fn init( pub fn init(
window: *const anyopaque, // zglfw.Window window: *const anyopaque, // zglfw.Window
@ -6,33 +8,28 @@ pub fn init(
num_frames_in_flight: u32, num_frames_in_flight: u32,
rtv_format: c_uint, // DXGI_FORMAT rtv_format: c_uint, // DXGI_FORMAT
cbv_srv_heap: *const anyopaque, // ID3D12DescriptorHeap cbv_srv_heap: *const anyopaque, // ID3D12DescriptorHeap
font_srv_cpu_desc_handle: D3D12_CPU_DESCRIPTOR_HANDLE, font_srv_cpu_desc_handle: backend_dx12.D3D12_CPU_DESCRIPTOR_HANDLE,
font_srv_gpu_desc_handle: D3D12_GPU_DESCRIPTOR_HANDLE, font_srv_gpu_desc_handle: backend_dx12.D3D12_GPU_DESCRIPTOR_HANDLE,
) void { ) void {
if (!ImGui_ImplGlfw_InitForOther(window, true)) { backend_glfw.init(window);
@panic("failed to init glfw for imgui"); backend_dx12.init(
}
if (!ImGui_ImplDX12_Init(
device, device,
num_frames_in_flight, num_frames_in_flight,
rtv_format, rtv_format,
cbv_srv_heap, cbv_srv_heap,
font_srv_cpu_desc_handle, font_srv_cpu_desc_handle,
font_srv_gpu_desc_handle, font_srv_gpu_desc_handle,
)) { );
@panic("failed to init d3d12 for imgui");
}
} }
pub fn deinit() void { pub fn deinit() void {
ImGui_ImplGlfw_Shutdown(); backend_dx12.deinit();
ImGui_ImplDX12_Shutdown(); backend_glfw.deinit();
} }
pub fn newFrame(fb_width: u32, fb_height: u32) void { pub fn newFrame(fb_width: u32, fb_height: u32) void {
ImGui_ImplGlfw_NewFrame(); backend_glfw.newFrame();
ImGui_ImplDX12_NewFrame(); backend_dx12.newFrame();
gui.io.setDisplaySize(@as(f32, @floatFromInt(fb_width)), @as(f32, @floatFromInt(fb_height))); gui.io.setDisplaySize(@as(f32, @floatFromInt(fb_width)), @as(f32, @floatFromInt(fb_height)));
gui.io.setDisplayFramebufferScale(1.0, 1.0); gui.io.setDisplayFramebufferScale(1.0, 1.0);
@ -44,31 +41,5 @@ pub fn draw(
graphics_command_list: *const anyopaque, // *ID3D12GraphicsCommandList graphics_command_list: *const anyopaque, // *ID3D12GraphicsCommandList
) void { ) void {
gui.render(); gui.render();
ImGui_ImplDX12_RenderDrawData(gui.getDrawData(), graphics_command_list); backend_dx12.render(gui.getDrawData(), graphics_command_list);
} }
pub const D3D12_CPU_DESCRIPTOR_HANDLE = extern struct {
ptr: c_ulonglong,
};
pub const D3D12_GPU_DESCRIPTOR_HANDLE = extern struct {
ptr: c_ulonglong,
};
extern fn ImGui_ImplGlfw_InitForOther(window: *const anyopaque, install_callbacks: bool) bool;
extern fn ImGui_ImplGlfw_NewFrame() void;
extern fn ImGui_ImplGlfw_Shutdown() void;
extern fn ImGui_ImplDX12_Init(
device: *const anyopaque, // ID3D12Device
num_frames_in_flight: u32,
rtv_format: u32, // DXGI_FORMAT
cbv_srv_heap: *const anyopaque, // ID3D12DescriptorHeap
font_srv_cpu_desc_handle: D3D12_CPU_DESCRIPTOR_HANDLE,
font_srv_gpu_desc_handle: D3D12_GPU_DESCRIPTOR_HANDLE,
) bool;
extern fn ImGui_ImplDX12_Shutdown() void;
extern fn ImGui_ImplDX12_NewFrame() void;
extern fn ImGui_ImplDX12_RenderDrawData(
draw_data: *const anyopaque, // *ImDrawData
graphics_command_list: *const anyopaque, // *ID3D12GraphicsCommandList
) void;

View File

@ -1,12 +1,11 @@
const gui = @import("gui.zig"); const gui = @import("gui.zig");
const backend_glfw = @import("backend_glfw.zig");
pub fn initWithGlSlVersion( pub fn initWithGlSlVersion(
window: *const anyopaque, // zglfw.Window window: *const anyopaque, // zglfw.Window
glsl_version: ?[:0]const u8, // e.g. "#version 130" glsl_version: ?[:0]const u8, // e.g. "#version 130"
) void { ) void {
if (!ImGui_ImplGlfw_InitForOpenGL(window, true)) { backend_glfw.initOpenGL(window);
unreachable;
}
ImGui_ImplOpenGL3_Init(@ptrCast(glsl_version)); ImGui_ImplOpenGL3_Init(@ptrCast(glsl_version));
} }
@ -18,12 +17,12 @@ pub fn init(
} }
pub fn deinit() void { pub fn deinit() void {
ImGui_ImplGlfw_Shutdown();
ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplOpenGL3_Shutdown();
backend_glfw.deinit();
} }
pub fn newFrame(fb_width: u32, fb_height: u32) void { pub fn newFrame(fb_width: u32, fb_height: u32) void {
ImGui_ImplGlfw_NewFrame(); backend_glfw.newFrame();
ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplOpenGL3_NewFrame();
gui.io.setDisplaySize(@as(f32, @floatFromInt(fb_width)), @as(f32, @floatFromInt(fb_height))); gui.io.setDisplaySize(@as(f32, @floatFromInt(fb_width)), @as(f32, @floatFromInt(fb_height)));
@ -37,9 +36,8 @@ pub fn draw() void {
ImGui_ImplOpenGL3_RenderDrawData(gui.getDrawData()); ImGui_ImplOpenGL3_RenderDrawData(gui.getDrawData());
} }
extern fn ImGui_ImplGlfw_InitForOpenGL(window: *const anyopaque, install_callbacks: bool) bool; // Those functions are defined in 'imgui_impl_opengl3.cpp`
extern fn ImGui_ImplGlfw_NewFrame() void; // (they include few custom changes).
extern fn ImGui_ImplGlfw_Shutdown() void;
extern fn ImGui_ImplOpenGL3_Init(glsl_version: [*c]const u8) void; extern fn ImGui_ImplOpenGL3_Init(glsl_version: [*c]const u8) void;
extern fn ImGui_ImplOpenGL3_Shutdown() void; extern fn ImGui_ImplOpenGL3_Shutdown() void;
extern fn ImGui_ImplOpenGL3_NewFrame() void; extern fn ImGui_ImplOpenGL3_NewFrame() void;

View File

@ -1,33 +1,40 @@
const gui = @import("gui.zig"); const gui = @import("gui.zig");
const backend_glfw = @import("backend_glfw.zig");
// This call will install GLFW callbacks to handle GUI interactions. // This call will install GLFW callbacks to handle GUI interactions.
// Those callbacks will chain-call user's previously installed callbacks, if any. // Those callbacks will chain-call user's previously installed callbacks, if any.
// This means that custom user's callbacks need to be installed *before* calling zgpu.gui.init(). // This means that custom user's callbacks need to be installed *before* calling zgpu.gui.init().
pub fn init( pub fn init(
window: *const anyopaque, // zglfw.Window window: *const anyopaque, // zglfw.Window
wgpu_device: *const anyopaque, // WGPUDevice wgpu_device: *const anyopaque, // wgpu.Device
wgpu_swap_chain_format: u32, // WGPUTextureFormat wgpu_swap_chain_format: u32, // wgpu.TextureFormat
wgpu_depth_format: u32, // WGPUTextureFormat wgpu_depth_format: u32, // wgpu.TextureFormat
) void { ) void {
if (!ImGui_ImplGlfw_InitForOther(window, true)) { backend_glfw.init(window);
unreachable;
}
if (!ImGui_ImplWGPU_Init(wgpu_device, 1, wgpu_swap_chain_format, wgpu_depth_format)) { var info = ImGui_ImplWGPU_InitInfo{
.device = wgpu_device,
.num_frames_in_flight = 1,
.rt_format = wgpu_swap_chain_format,
.depth_format = wgpu_depth_format,
.pipeline_multisample_state = .{},
};
if (!ImGui_ImplWGPU_Init(&info)) {
unreachable; unreachable;
} }
} }
pub fn deinit() void { pub fn deinit() void {
ImGui_ImplWGPU_Shutdown(); ImGui_ImplWGPU_Shutdown();
ImGui_ImplGlfw_Shutdown(); backend_glfw.deinit();
} }
pub fn newFrame(fb_width: u32, fb_height: u32) void { pub fn newFrame(fb_width: u32, fb_height: u32) void {
ImGui_ImplWGPU_NewFrame(); ImGui_ImplWGPU_NewFrame();
ImGui_ImplGlfw_NewFrame(); backend_glfw.newFrame();
gui.io.setDisplaySize(@as(f32, @floatFromInt(fb_width)), @as(f32, @floatFromInt(fb_height))); gui.io.setDisplaySize(@floatFromInt(fb_width), @floatFromInt(fb_height));
gui.io.setDisplayFramebufferScale(1.0, 1.0); gui.io.setDisplayFramebufferScale(1.0, 1.0);
gui.newFrame(); gui.newFrame();
@ -38,17 +45,23 @@ pub fn draw(wgpu_render_pass: *const anyopaque) void {
ImGui_ImplWGPU_RenderDrawData(gui.getDrawData(), wgpu_render_pass); ImGui_ImplWGPU_RenderDrawData(gui.getDrawData(), wgpu_render_pass);
} }
// Those functions are defined in `imgui_impl_glfw.cpp` and 'imgui_impl_wgpu.cpp` pub const ImGui_ImplWGPU_InitInfo = extern struct {
device: *const anyopaque,
num_frames_in_flight: u32 = 1,
rt_format: u32,
depth_format: u32,
pipeline_multisample_state: extern struct {
next_in_chain: ?*const anyopaque = null,
count: u32 = 1,
mask: u32 = @bitCast(@as(i32, -1)),
alpha_to_coverage_enabled: bool = false,
},
};
// Those functions are defined in 'imgui_impl_wgpu.cpp`
// (they include few custom changes). // (they include few custom changes).
extern fn ImGui_ImplGlfw_InitForOther(window: *const anyopaque, install_callbacks: bool) bool; extern fn ImGui_ImplWGPU_Init(init_info: *ImGui_ImplWGPU_InitInfo) bool;
extern fn ImGui_ImplGlfw_NewFrame() void;
extern fn ImGui_ImplGlfw_Shutdown() void;
extern fn ImGui_ImplWGPU_Init(
device: *const anyopaque, // WGPUDevice
num_frames_in_flight: u32,
rt_format: u32, // WGPUTextureFormat
wgpu_depth_format: u32, // WGPUTextureFormat
) bool;
extern fn ImGui_ImplWGPU_NewFrame() void; extern fn ImGui_ImplWGPU_NewFrame() void;
extern fn ImGui_ImplWGPU_RenderDrawData(draw_data: *const anyopaque, pass_encoder: *const anyopaque) void; extern fn ImGui_ImplWGPU_RenderDrawData(draw_data: *const anyopaque, pass_encoder: *const anyopaque) void;
extern fn ImGui_ImplWGPU_Shutdown() void; extern fn ImGui_ImplWGPU_Shutdown() void;

View File

@ -0,0 +1,48 @@
const std = @import("std");
const gui = @import("gui.zig");
const backend_dx12 = @import("backend_dx12.zig");
pub fn init(
hwnd: *const anyopaque, // HWND
d3d12_device: *const anyopaque, // ID3D12Device*
num_frames_in_flight: u16,
rtv_format: u32, // DXGI_FORMAT
cbv_srv_heap: *const anyopaque, // ID3D12DescriptorHeap*
font_srv_cpu_desc_handle: backend_dx12.D3D12_CPU_DESCRIPTOR_HANDLE,
font_srv_gpu_desc_handle: backend_dx12.D3D12_GPU_DESCRIPTOR_HANDLE,
) void {
std.debug.assert(ImGui_ImplWin32_Init(hwnd));
backend_dx12.init(
d3d12_device,
num_frames_in_flight,
rtv_format,
cbv_srv_heap,
font_srv_cpu_desc_handle,
font_srv_gpu_desc_handle,
);
}
pub fn deinit() void {
backend_dx12.deinit();
ImGui_ImplWin32_Shutdown();
}
pub fn newFrame(fb_width: u32, fb_height: u32) void {
ImGui_ImplWin32_NewFrame();
backend_dx12.newFrame();
gui.io.setDisplaySize(@as(f32, @floatFromInt(fb_width)), @as(f32, @floatFromInt(fb_height)));
gui.io.setDisplayFramebufferScale(1.0, 1.0);
gui.newFrame();
}
pub fn draw(graphics_command_list: *const anyopaque) void {
gui.render();
backend_dx12.render(gui.getDrawData(), graphics_command_list);
}
extern fn ImGui_ImplWin32_Init(hwnd: *const anyopaque) bool;
extern fn ImGui_ImplWin32_Shutdown() void;
extern fn ImGui_ImplWin32_NewFrame() void;

View File

@ -5,13 +5,16 @@
// //
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
pub const plot = @import("plot.zig"); pub const plot = @import("plot.zig");
pub const te = @import("te.zig");
pub const backend = switch (@import("zgui_options").backend) { pub const backend = switch (@import("zgui_options").backend) {
.glfw_wgpu => @import("backend_glfw_wgpu.zig"), .glfw_wgpu => @import("backend_glfw_wgpu.zig"),
.glfw_opengl3 => @import("backend_glfw_opengl.zig"), .glfw_opengl3 => @import("backend_glfw_opengl.zig"),
.glfw_dx12 => @import("backend_glfw_dx12.zig"), .glfw_dx12 => @import("backend_glfw_dx12.zig"),
.win32_dx12 => .{}, // TODO: .glfw => @import("backend_glfw.zig"),
.win32_dx12 => @import("backend_win32_dx12.zig"),
.no_backend => .{}, .no_backend => .{},
}; };
const te_enabled = @import("zgui_options").with_te;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
const std = @import("std"); const std = @import("std");
const assert = std.debug.assert; const assert = std.debug.assert;
@ -26,6 +29,7 @@ pub const DrawVert = extern struct {
color: u32, color: u32,
}; };
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
pub fn init(allocator: std.mem.Allocator) void { pub fn init(allocator: std.mem.Allocator) void {
if (zguiGetCurrentContext() == null) { if (zguiGetCurrentContext() == null) {
mem_allocator = allocator; mem_allocator = allocator;
@ -37,6 +41,10 @@ pub fn init(allocator: std.mem.Allocator) void {
temp_buffer = std.ArrayList(u8).init(allocator); temp_buffer = std.ArrayList(u8).init(allocator);
temp_buffer.?.resize(3 * 1024 + 1) catch unreachable; temp_buffer.?.resize(3 * 1024 + 1) catch unreachable;
if (te_enabled) {
te.init();
}
} }
} }
pub fn deinit() void { pub fn deinit() void {
@ -44,6 +52,12 @@ pub fn deinit() void {
temp_buffer.?.deinit(); temp_buffer.?.deinit();
zguiDestroyContext(null); zguiDestroyContext(null);
// Must be after destroy imgui context.
// And before allocation check
if (te_enabled) {
te.deinit();
}
if (mem_allocations.?.count() > 0) { if (mem_allocations.?.count() > 0) {
var it = mem_allocations.?.iterator(); var it = mem_allocations.?.iterator();
while (it.next()) |kv| { while (it.next()) |kv| {
@ -124,7 +138,13 @@ pub const ConfigFlags = packed struct(c_int) {
nav_no_capture_keyboard: bool = false, nav_no_capture_keyboard: bool = false,
no_mouse: bool = false, no_mouse: bool = false,
no_mouse_cursor_change: bool = false, no_mouse_cursor_change: bool = false,
user_storage: u14 = 0, dock_enable: bool = false,
_pading0: u3 = 0,
viewport_enable: bool = false,
_pading1: u3 = 0,
dpi_enable_scale_viewport: bool = false,
dpi_enable_scale_fonts: bool = false,
user_storage: u4 = 0,
is_srgb: bool = false, is_srgb: bool = false,
is_touch_screen: bool = false, is_touch_screen: bool = false,
_padding: u10 = 0, _padding: u10 = 0,
@ -147,6 +167,7 @@ pub const FontConfig = extern struct {
merge_mode: bool, merge_mode: bool,
font_builder_flags: c_uint, font_builder_flags: c_uint,
rasterizer_multiply: f32, rasterizer_multiply: f32,
rasterizer_density: f32,
ellipsis_char: Wchar, ellipsis_char: Wchar,
name: [40]u8, name: [40]u8,
dst_font: *Font, dst_font: *Font,
@ -326,7 +347,7 @@ pub const DrawData = *extern struct {
cmd_lists_count: c_int, cmd_lists_count: c_int,
total_idx_count: c_int, total_idx_count: c_int,
total_vtx_count: c_int, total_vtx_count: c_int,
cmd_lists: [*]DrawList, cmd_lists: Vector(DrawList),
display_pos: [2]f32, display_pos: [2]f32,
display_size: [2]f32, display_size: [2]f32,
framebuffer_scale: [2]f32, framebuffer_scale: [2]f32,
@ -334,7 +355,7 @@ pub const DrawData = *extern struct {
pub const Font = *opaque {}; pub const Font = *opaque {};
pub const Ident = u32; pub const Ident = u32;
pub const TextureIdent = *anyopaque; pub const TextureIdent = *anyopaque;
pub const Wchar = u16; pub const Wchar = if (@import("zgui_options").use_wchar32) u32 else u16;
pub const Key = enum(c_int) { pub const Key = enum(c_int) {
none = 0, none = 0,
tab = 512, tab = 512,
@ -409,6 +430,18 @@ pub const Key = enum(c_int) {
f10, f10,
f11, f11,
f12, f12,
f13,
f14,
f15,
f16,
f17,
f18,
f19,
f20,
f21,
f22,
f23,
f24,
apostrophe, apostrophe,
comma, comma,
minus, minus,
@ -443,6 +476,9 @@ pub const Key = enum(c_int) {
keypad_enter, keypad_enter,
keypad_equal, keypad_equal,
app_back,
app_forward,
gamepad_start, gamepad_start,
gamepad_back, gamepad_back,
gamepad_faceleft, gamepad_faceleft,
@ -483,6 +519,7 @@ pub const Key = enum(c_int) {
mod_super = 1 << 15, mod_super = 1 << 15,
mod_mask_ = 0xf000, mod_mask_ = 0xf000,
}; };
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
pub const WindowFlags = packed struct(c_int) { pub const WindowFlags = packed struct(c_int) {
no_title_bar: bool = false, no_title_bar: bool = false,
@ -501,12 +538,11 @@ pub const WindowFlags = packed struct(c_int) {
no_bring_to_front_on_focus: bool = false, no_bring_to_front_on_focus: bool = false,
always_vertical_scrollbar: bool = false, always_vertical_scrollbar: bool = false,
always_horizontal_scrollbar: bool = false, always_horizontal_scrollbar: bool = false,
always_use_window_padding: bool = false,
_removed: u1 = 0,
no_nav_inputs: bool = false, no_nav_inputs: bool = false,
no_nav_focus: bool = false, no_nav_focus: bool = false,
unsaved_document: bool = false, unsaved_document: bool = false,
_padding: u11 = 0, no_docking: bool = false,
_padding: u12 = 0,
pub const no_nav = WindowFlags{ .no_nav_inputs = true, .no_nav_focus = true }; pub const no_nav = WindowFlags{ .no_nav_inputs = true, .no_nav_focus = true };
pub const no_decoration = WindowFlags{ pub const no_decoration = WindowFlags{
@ -521,6 +557,20 @@ pub const WindowFlags = packed struct(c_int) {
.no_nav_focus = true, .no_nav_focus = true,
}; };
}; };
pub const ChildFlags = packed struct(c_int) {
border: bool = false,
no_move: bool = false,
always_use_window_padding: bool = false,
resize_x: bool = false,
resize_y: bool = false,
auto_resize_x: bool = false,
auto_resize_y: bool = false,
always_auto_resize: bool = false,
frame_style: bool = false,
_padding: u23 = 0,
};
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
pub const SliderFlags = packed struct(c_int) { pub const SliderFlags = packed struct(c_int) {
_reserved0: bool = false, _reserved0: bool = false,
@ -629,7 +679,12 @@ pub fn setNextWindowBgAlpha(args: SetNextWindowBgAlpha) void {
zguiSetNextWindowBgAlpha(args.alpha); zguiSetNextWindowBgAlpha(args.alpha);
} }
extern fn zguiSetNextWindowBgAlpha(alpha: f32) void; extern fn zguiSetNextWindowBgAlpha(alpha: f32) void;
//--------------------------------------------------------------------------------------------------
pub fn setWindowFocus(name: ?[:0]const u8) void {
zguiSetWindowFocus(name orelse null);
}
extern fn zguiSetWindowFocus(name: ?[*:0]const u8) void;
//-------------------------------------------------------------------------------------------------
pub fn setKeyboardFocusHere(offset: i32) void { pub fn setKeyboardFocusHere(offset: i32) void {
zguiSetKeyboardFocusHere(offset); zguiSetKeyboardFocusHere(offset);
} }
@ -651,19 +706,19 @@ extern fn zguiEnd() void;
const BeginChild = struct { const BeginChild = struct {
w: f32 = 0.0, w: f32 = 0.0,
h: f32 = 0.0, h: f32 = 0.0,
border: bool = false, child_flags: ChildFlags = .{},
flags: WindowFlags = .{}, window_flags: WindowFlags = .{},
}; };
pub fn beginChild(str_id: [:0]const u8, args: BeginChild) bool { pub fn beginChild(str_id: [:0]const u8, args: BeginChild) bool {
return zguiBeginChild(str_id, args.w, args.h, args.border, args.flags); return zguiBeginChild(str_id, args.w, args.h, args.child_flags, args.window_flags);
} }
pub fn beginChildId(id: Ident, args: BeginChild) bool { pub fn beginChildId(id: Ident, args: BeginChild) bool {
return zguiBeginChildId(id, args.w, args.h, args.border, args.flags); return zguiBeginChildId(id, args.w, args.h, args.child_flags, args.window_flags);
} }
/// `pub fn endChild() void` /// `pub fn endChild() void`
pub const endChild = zguiEndChild; pub const endChild = zguiEndChild;
extern fn zguiBeginChild(str_id: [*:0]const u8, w: f32, h: f32, border: bool, flags: WindowFlags) bool; extern fn zguiBeginChild(str_id: [*:0]const u8, w: f32, h: f32, flags: ChildFlags, window_flags: WindowFlags) bool;
extern fn zguiBeginChildId(id: Ident, w: f32, h: f32, border: bool, flags: WindowFlags) bool; extern fn zguiBeginChildId(id: Ident, w: f32, h: f32, flags: ChildFlags, window_flags: WindowFlags) bool;
extern fn zguiEndChild() void; extern fn zguiEndChild() void;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// `pub fn zguiGetScrollX() f32` /// `pub fn zguiGetScrollX() f32`
@ -720,7 +775,8 @@ pub const FocusedFlags = packed struct(c_int) {
root_window: bool = false, root_window: bool = false,
any_window: bool = false, any_window: bool = false,
no_popup_hierarchy: bool = false, no_popup_hierarchy: bool = false,
_padding: u28 = 0, dock_hierarchy: bool = false,
_padding: u27 = 0,
pub const root_and_child_windows = FocusedFlags{ .root_window = true, .child_windows = true }; pub const root_and_child_windows = FocusedFlags{ .root_window = true, .child_windows = true };
}; };
@ -730,22 +786,27 @@ pub const HoveredFlags = packed struct(c_int) {
root_window: bool = false, root_window: bool = false,
any_window: bool = false, any_window: bool = false,
no_popup_hierarchy: bool = false, no_popup_hierarchy: bool = false,
_reserved0: bool = false, dock_hierarchy: bool = false,
allow_when_blocked_by_popup: bool = false, allow_when_blocked_by_popup: bool = false,
_reserved1: bool = false, _reserved1: bool = false,
allow_when_blocked_by_active_item: bool = false, allow_when_blocked_by_active_item: bool = false,
allow_when_overlapped: bool = false, allow_when_overlapped_by_item: bool = false,
allow_when_overlapped_by_window: bool = false,
allow_when_disabled: bool = false, allow_when_disabled: bool = false,
no_nav_override: bool = false, no_nav_override: bool = false,
for_tooltip: bool = false,
stationary: bool = false,
delay_none: bool = false,
delay_normal: bool = false, delay_normal: bool = false,
delay_short: bool = false, delay_short: bool = false,
no_shared_delay: bool = false, no_shared_delay: bool = false,
_padding: u18 = 0, _padding: u14 = 0,
pub const rect_only = HoveredFlags{ pub const rect_only = HoveredFlags{
.allow_when_blocked_by_popup = true, .allow_when_blocked_by_popup = true,
.allow_when_blocked_by_active_item = true, .allow_when_blocked_by_active_item = true,
.allow_when_overlapped = true, .allow_when_overlapped_by_item = true,
.allow_when_overlapped_by_window = true,
}; };
pub const root_and_child_windows = HoveredFlags{ .root_window = true, .child_windows = true }; pub const root_and_child_windows = HoveredFlags{ .root_window = true, .child_windows = true };
}; };
@ -812,6 +873,79 @@ extern fn zguiGetContentRegionAvail(size: *[2]f32) void;
extern fn zguiGetContentRegionMax(size: *[2]f32) void; extern fn zguiGetContentRegionMax(size: *[2]f32) void;
extern fn zguiGetWindowContentRegionMin(size: *[2]f32) void; extern fn zguiGetWindowContentRegionMin(size: *[2]f32) void;
extern fn zguiGetWindowContentRegionMax(size: *[2]f32) void; extern fn zguiGetWindowContentRegionMax(size: *[2]f32) void;
//--------------------------------------------------------------------------------------------------
//
// Docking
//
//--------------------------------------------------------------------------------------------------
pub const DockNodeFlags = packed struct(c_int) {
keep_alive_only: bool = false,
_reserved: u1 = 0,
no_docking_over_central_node: bool = false,
passthru_central_node: bool = false,
no_docking_split: bool = false,
no_resize: bool = false,
auto_hide_tab_bar: bool = false,
no_undocking: bool = false,
_padding_0: u2 = 0,
// Extended enum entries from imgui_internal (unstable, subject to change, use at own risk)
dock_space: bool = false,
central_node: bool = false,
no_tab_bar: bool = false,
hidden_tab_bar: bool = false,
no_window_menu_button: bool = false,
no_close_button: bool = false,
no_resize_x: bool = false,
no_resize_y: bool = false,
docked_windows_in_focus_route: bool = false,
no_docking_split_other: bool = false,
no_docking_over_me: bool = false,
no_docking_over_other: bool = false,
no_docking_over_empty: bool = false,
_padding_1: u9 = 0,
};
extern fn zguiDockSpace(str_id: [*:0]const u8, size: *const [2]f32, flags: DockNodeFlags) Ident;
pub fn DockSpace(str_id: [:0]const u8, size: [2]f32, flags: DockNodeFlags) Ident {
return zguiDockSpace(str_id.ptr, &size, flags);
}
extern fn zguiDockSpaceOverViewport(viewport: Viewport, flags: DockNodeFlags) Ident;
pub const DockSpaceOverViewport = zguiDockSpaceOverViewport;
//--------------------------------------------------------------------------------------------------
//
// DockBuilder (Unstable internal imgui API, subject to change, use at own risk)
//
//--------------------------------------------------------------------------------------------------
pub fn dockBuilderDockWindow(window_name: [:0]const u8, node_id: Ident) void {
zguiDockBuilderDockWindow(window_name.ptr, node_id);
}
pub const dockBuilderAddNode = zguiDockBuilderAddNode;
pub const dockBuilderRemoveNode = zguiDockBuilderRemoveNode;
pub fn dockBuilderSetNodePos(node_id: Ident, pos: [2]f32) void {
zguiDockBuilderSetNodePos(node_id, &pos);
}
pub fn dockBuilderSetNodeSize(node_id: Ident, size: [2]f32) void {
zguiDockBuilderSetNodeSize(node_id, &size);
}
pub const dockBuilderSplitNode = zguiDockBuilderSplitNode;
pub const dockBuilderFinish = zguiDockBuilderFinish;
extern fn zguiDockBuilderDockWindow(window_name: [*:0]const u8, node_id: Ident) void;
extern fn zguiDockBuilderAddNode(node_id: Ident, flags: DockNodeFlags) Ident;
extern fn zguiDockBuilderRemoveNode(node_id: Ident) void;
extern fn zguiDockBuilderSetNodePos(node_id: Ident, pos: *const [2]f32) void;
extern fn zguiDockBuilderSetNodeSize(node_id: Ident, size: *const [2]f32) void;
extern fn zguiDockBuilderSplitNode(
node_id: Ident,
split_dir: Direction,
size_ratio_for_node_at_dir: f32,
out_id_at_dir: ?*Ident,
out_id_at_opposite_dir: ?*Ident,
) Ident;
extern fn zguiDockBuilderFinish(node_id: Ident) void;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// //
// Style // Style
@ -847,6 +981,8 @@ pub const Style = extern struct {
tab_rounding: f32, tab_rounding: f32,
tab_border_size: f32, tab_border_size: f32,
tab_min_width_for_close_button: f32, tab_min_width_for_close_button: f32,
tab_bar_border_size: f32,
table_angled_header_angle: f32,
color_button_position: Direction, color_button_position: Direction,
button_text_align: [2]f32, button_text_align: [2]f32,
selectable_text_align: [2]f32, selectable_text_align: [2]f32,
@ -855,6 +991,7 @@ pub const Style = extern struct {
separator_text_padding: [2]f32, separator_text_padding: [2]f32,
display_window_padding: [2]f32, display_window_padding: [2]f32,
display_safe_area_padding: [2]f32, display_safe_area_padding: [2]f32,
docking_separator_size: f32,
mouse_cursor_scale: f32, mouse_cursor_scale: f32,
anti_aliased_lines: bool, anti_aliased_lines: bool,
anti_aliased_lines_use_tex: bool, anti_aliased_lines_use_tex: bool,
@ -864,6 +1001,13 @@ pub const Style = extern struct {
colors: [@typeInfo(StyleCol).Enum.fields.len][4]f32, colors: [@typeInfo(StyleCol).Enum.fields.len][4]f32,
hover_stationary_delay: f32,
hover_delay_short: f32,
hover_delay_normal: f32,
hover_flags_for_tooltip_mouse: HoveredFlags,
hover_flags_for_tooltip_nav: HoveredFlags,
/// `pub fn init() Style` /// `pub fn init() Style`
pub const init = zguiStyle_Init; pub const init = zguiStyle_Init;
extern fn zguiStyle_Init() Style; extern fn zguiStyle_Init() Style;
@ -922,6 +1066,8 @@ pub const StyleCol = enum(c_int) {
tab_active, tab_active,
tab_unfocused, tab_unfocused,
tab_unfocused_active, tab_unfocused_active,
docking_preview,
docking_empty_bg,
plot_lines, plot_lines,
plot_lines_hovered, plot_lines_hovered,
plot_histogram, plot_histogram,
@ -996,11 +1142,13 @@ pub const StyleVar = enum(c_int) {
grab_min_size, // 1f grab_min_size, // 1f
grab_rounding, // 1f grab_rounding, // 1f
tab_rounding, // 1f tab_rounding, // 1f
tab_bar_border_size, // 1f
button_text_align, // 2f button_text_align, // 2f
selectable_text_align, // 2f selectable_text_align, // 2f
separator_text_border_size, // 1f separator_text_border_size, // 1f
separator_text_align, // 2f separator_text_align, // 2f
separator_text_padding, // 2f separator_text_padding, // 2f
docking_separator_size, // 1f
}; };
pub fn pushStyleVar1f(args: struct { pub fn pushStyleVar1f(args: struct {
@ -1227,8 +1375,14 @@ pub fn getItemRectMin() [2]f32 {
zguiGetItemRectMin(&rect); zguiGetItemRectMin(&rect);
return rect; return rect;
} }
pub fn getItemRectSize() [2]f32 {
var rect: [2]f32 = undefined;
zguiGetItemRectSize(&rect);
return rect;
}
extern fn zguiGetItemRectMax(rect: *[2]f32) void; extern fn zguiGetItemRectMax(rect: *[2]f32) void;
extern fn zguiGetItemRectMin(rect: *[2]f32) void; extern fn zguiGetItemRectMin(rect: *[2]f32) void;
extern fn zguiGetItemRectSize(rect: *[2]f32) void;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// //
// ID stack/scopes // ID stack/scopes
@ -1502,31 +1656,35 @@ pub fn comboFromEnum(
/// i32 (the underlying imgui restriction) /// i32 (the underlying imgui restriction)
current_item: anytype, current_item: anytype,
) bool { ) bool {
const item_names = comptime lbl: { const EnumType = @TypeOf(current_item.*);
const item_type = @typeInfo(@TypeOf(current_item.*)); const enum_type_info = switch (@typeInfo(EnumType)) {
switch (item_type) { .Enum => |enum_type_info| enum_type_info,
.Enum => |e| { else => @compileError("Error: current_item must be a pointer-to-an-enum, not a " ++ @TypeOf(current_item)),
var str: [:0]const u8 = "";
for (e.fields) |f| {
str = str ++ f.name ++ "\x00";
}
break :lbl str;
},
else => {
@compileError("Error: current_item must be a pointer-to-an-enum, not a " ++ @TypeOf(current_item));
},
}
}; };
var item: i32 = @intCast(@intFromEnum(current_item.*)); const FieldNameIndex = std.meta.Tuple(&.{ []const u8, i32 });
comptime var item_names: [:0]const u8 = "";
comptime var field_name_to_index_list: [enum_type_info.fields.len]FieldNameIndex = undefined;
comptime var index_to_enum: [enum_type_info.fields.len]EnumType = undefined;
comptime {
for (enum_type_info.fields, 0..) |f, i| {
item_names = item_names ++ f.name ++ "\x00";
const e: EnumType = @enumFromInt(f.value);
field_name_to_index_list[i] = .{ f.name, @intCast(i) };
index_to_enum[i] = e;
}
}
const field_name_to_index = std.StaticStringMap(i32).initComptime(&field_name_to_index_list);
var item: i32 = field_name_to_index.get(@tagName(current_item.*)).?;
const result = combo(label, .{ const result = combo(label, .{
.items_separated_by_zeros = item_names, .items_separated_by_zeros = item_names,
.current_item = &item, .current_item = &item,
}); });
current_item.* = @enumFromInt(item); current_item.* = index_to_enum[@intCast(item)];
return result; return result;
} }
@ -1545,7 +1703,8 @@ pub const ComboFlags = packed struct(c_int) {
height_largest: bool = false, height_largest: bool = false,
no_arrow_button: bool = false, no_arrow_button: bool = false,
no_preview: bool = false, no_preview: bool = false,
_padding: u25 = 0, width_fit_preview: bool = false,
_padding: u24 = 0,
}; };
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
const BeginCombo = struct { const BeginCombo = struct {
@ -2235,7 +2394,7 @@ pub const InputTextCallbackData = extern struct {
pub const InputTextCallback = *const fn (data: *InputTextCallbackData) i32; pub const InputTextCallback = *const fn (data: *InputTextCallbackData) i32;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
pub fn inputText(label: [:0]const u8, args: struct { pub fn inputText(label: [:0]const u8, args: struct {
buf: []u8, buf: [:0]u8,
flags: InputTextFlags = .{}, flags: InputTextFlags = .{},
callback: ?InputTextCallback = null, callback: ?InputTextCallback = null,
user_data: ?*anyopaque = null, user_data: ?*anyopaque = null,
@ -2243,7 +2402,7 @@ pub fn inputText(label: [:0]const u8, args: struct {
return zguiInputText( return zguiInputText(
label, label,
args.buf.ptr, args.buf.ptr,
args.buf.len, args.buf.len + 1, // + 1 for sentinel
args.flags, args.flags,
if (args.callback) |cb| cb else null, if (args.callback) |cb| cb else null,
args.user_data, args.user_data,
@ -2259,7 +2418,7 @@ extern fn zguiInputText(
) bool; ) bool;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
pub fn inputTextMultiline(label: [:0]const u8, args: struct { pub fn inputTextMultiline(label: [:0]const u8, args: struct {
buf: []u8, buf: [:0]u8,
w: f32 = 0.0, w: f32 = 0.0,
h: f32 = 0.0, h: f32 = 0.0,
flags: InputTextFlags = .{}, flags: InputTextFlags = .{},
@ -2269,7 +2428,7 @@ pub fn inputTextMultiline(label: [:0]const u8, args: struct {
return zguiInputTextMultiline( return zguiInputTextMultiline(
label, label,
args.buf.ptr, args.buf.ptr,
args.buf.len, args.buf.len + 1, // + 1 for sentinel
args.w, args.w,
args.h, args.h,
args.flags, args.flags,
@ -2290,7 +2449,7 @@ extern fn zguiInputTextMultiline(
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
pub fn inputTextWithHint(label: [:0]const u8, args: struct { pub fn inputTextWithHint(label: [:0]const u8, args: struct {
hint: [:0]const u8, hint: [:0]const u8,
buf: []u8, buf: [:0]u8,
flags: InputTextFlags = .{}, flags: InputTextFlags = .{},
callback: ?InputTextCallback = null, callback: ?InputTextCallback = null,
user_data: ?*anyopaque = null, user_data: ?*anyopaque = null,
@ -2299,7 +2458,7 @@ pub fn inputTextWithHint(label: [:0]const u8, args: struct {
label, label,
args.hint, args.hint,
args.buf.ptr, args.buf.ptr,
args.buf.len, args.buf.len + 1, // + 1 for sentinel
args.flags, args.flags,
if (args.callback) |cb| cb else null, if (args.callback) |cb| cb else null,
args.user_data, args.user_data,
@ -2620,7 +2779,7 @@ extern fn zguiColorButton(
pub const TreeNodeFlags = packed struct(c_int) { pub const TreeNodeFlags = packed struct(c_int) {
selected: bool = false, selected: bool = false,
framed: bool = false, framed: bool = false,
allow_item_overlap: bool = false, allow_overlap: bool = false,
no_tree_push_on_open: bool = false, no_tree_push_on_open: bool = false,
no_auto_open_on_log: bool = false, no_auto_open_on_log: bool = false,
default_open: bool = false, default_open: bool = false,
@ -2631,8 +2790,9 @@ pub const TreeNodeFlags = packed struct(c_int) {
frame_padding: bool = false, frame_padding: bool = false,
span_avail_width: bool = false, span_avail_width: bool = false,
span_full_width: bool = false, span_full_width: bool = false,
span_all_columns: bool = false,
nav_left_jumps_back_here: bool = false, nav_left_jumps_back_here: bool = false,
_padding: u18 = 0, _padding: u17 = 0,
pub const collapsing_header = TreeNodeFlags{ pub const collapsing_header = TreeNodeFlags{
.framed = true, .framed = true,
@ -2732,7 +2892,7 @@ pub const SelectableFlags = packed struct(c_int) {
span_all_columns: bool = false, span_all_columns: bool = false,
allow_double_click: bool = false, allow_double_click: bool = false,
disabled: bool = false, disabled: bool = false,
allow_item_overlap: bool = false, allow_overlap: bool = false,
_padding: u27 = 0, _padding: u27 = 0,
}; };
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -3245,11 +3405,13 @@ pub const PopupFlags = packed struct(c_int) {
_reserved0: bool = false, _reserved0: bool = false,
_reserved1: bool = false, _reserved1: bool = false,
no_reopen: bool = false,
_reserved2: bool = false,
no_open_over_existing_popup: bool = false, no_open_over_existing_popup: bool = false,
no_open_over_items: bool = false, no_open_over_items: bool = false,
any_popup_id: bool = false, any_popup_id: bool = false,
any_popup_level: bool = false, any_popup_level: bool = false,
_padding: u23 = 0, _padding: u21 = 0,
pub const any_popup = PopupFlags{ .any_popup_id = true, .any_popup_level = true }; pub const any_popup = PopupFlags{ .any_popup_id = true, .any_popup_level = true };
}; };
@ -3297,7 +3459,8 @@ pub const TabItemFlags = packed struct(c_int) {
no_reorder: bool = false, no_reorder: bool = false,
leading: bool = false, leading: bool = false,
trailing: bool = false, trailing: bool = false,
_padding: u24 = 0, no_assumed_closure: bool = false,
_padding: u23 = 0,
}; };
pub fn beginTabBar(label: [:0]const u8, flags: TabBarFlags) bool { pub fn beginTabBar(label: [:0]const u8, flags: TabBarFlags) bool {
return zguiBeginTabBar(label, flags); return zguiBeginTabBar(label, flags);
@ -4380,6 +4543,14 @@ pub const DrawList = *opaque {
extern fn zguiDrawList_AddResetRenderStateCallback(draw_list: DrawList) void; extern fn zguiDrawList_AddResetRenderStateCallback(draw_list: DrawList) void;
}; };
fn Vector(comptime T: type) type {
return extern struct {
len: c_int,
capacity: c_int,
items: [*]T,
};
}
test { test {
const testing = std.testing; const testing = std.testing;

255
src/te.zig Normal file
View File

@ -0,0 +1,255 @@
const std = @import("std");
const zgui = @import("gui.zig");
const te_enabled = @import("zgui_options").with_te;
pub const Actions = enum(c_int) {
unknown = 0,
/// Move mouse
hover,
/// Move mouse and click
click,
/// Move mouse and double-click
double_click,
/// Check item if unchecked (Checkbox, MenuItem or any widget reporting ImGuiItemStatusFlags_Checkable)
check,
/// Uncheck item if checked
uncheck,
/// Open item if closed (TreeNode, BeginMenu or any widget reporting ImGuiItemStatusFlags_Openable)
open,
/// Close item if opened
close,
/// Start text inputing into a field (e.g. CTRL+Click on Drags/Slider, click on InputText etc.)
input,
/// Activate item with navigation
nav_activate,
};
pub const TestRunFlags = packed struct(c_int) {
/// Used internally to temporarily disable the GUI func (at the end of a test, etc)
gui_func_disable: bool = false,
/// Set when user selects "Run GUI func"
gui_func_only: bool = false,
no_success_mgs: bool = false,
no_stop_on_error: bool = false,
no_break_on_error: bool = false,
/// Disable input submission to let test submission raw input event (in order to test e.g. IO queue)
enable_raw_inputs: bool = false,
manual_run: bool = false,
command_line: bool = false,
_padding: u24 = 0,
};
pub const TestOpFlags = packed struct(c_int) {
// Don't check for HoveredId after aiming for a widget. A few situations may want this: while e.g. dragging or another items prevents hovering, or for items that don't use ItemHoverable()
no_check_hovered_id: bool = false,
/// Don't abort/error e.g. if the item cannot be found or the operation doesn't succeed.
no_error: bool = false,
/// Don't focus window when aiming at an item
no_focus_window: bool = false,
/// Disable automatically uncollapsing windows (useful when specifically testing Collapsing behaviors)
no_auto_uncollapse: bool = false,
/// Disable automatically opening intermediaries (e.g. ItemClick("Hello/OK") will automatically first open "Hello" if "OK" isn't found. Only works if ref is a string path.
no_auto_open_full_path: bool = false,
/// Used by recursing functions to indicate a second attempt
is_second_attempt: bool = false,
move_to_edge_l: bool = false, // Simple Dumb aiming helpers to test widget that care about clicking position. May need to replace will better functionalities.
move_to_edge_r: bool = false,
move_to_edge_u: bool = false,
move_to_edge_d: bool = false,
_padding: u22 = 0,
};
pub const CheckFlags = packed struct(c_int) {
silent_success: bool = false,
_padding: u31 = 0,
};
pub const RunSpeed = enum(c_int) {
/// Run tests as fast as possible (teleport mouse, skip delays, etc.)
fast = 0,
/// Run tests at human watchable speed (for debugging)
normal = 1,
/// Run tests with pauses between actions (for e.g. tutorials)
cinematic = 2,
};
pub const TestGroup = enum(c_int) {
unknown = -1,
tests = 0,
perfs = 1,
};
pub const Test = anyopaque;
pub const TestEngine = opaque {
pub fn registerTest(
engine: *TestEngine,
category: [:0]const u8,
name: [:0]const u8,
src: std.builtin.SourceLocation,
comptime Callbacks: type,
) *Test {
return zguiTe_RegisterTest(
engine,
category.ptr,
name.ptr,
src.file.ptr,
@intCast(src.line),
if (std.meta.hasFn(Callbacks, "gui"))
struct {
fn f(context: *TestContext) callconv(.C) void {
Callbacks.gui(context) catch undefined;
}
}.f
else
null,
if (std.meta.hasFn(Callbacks, "run"))
struct {
fn f(context: *TestContext) callconv(.C) void {
Callbacks.run(context) catch undefined;
}
}.f
else
null,
);
}
pub const showTestEngineWindows = zguiTe_ShowTestEngineWindows;
extern fn zguiTe_ShowTestEngineWindows(engine: *TestEngine, p_open: ?*bool) void;
pub const setRunSpeed = zguiTe_EngineSetRunSpeed;
extern fn zguiTe_EngineSetRunSpeed(engine: *TestEngine, speed: RunSpeed) void;
pub const stop = zguiTe_Stop;
extern fn zguiTe_Stop(engine: *TestEngine) void;
pub const tryAbortEngine = zguiTe_TryAbortEngine;
extern fn zguiTe_TryAbortEngine(engine: *TestEngine) void;
pub const postSwap = zguiTe_PostSwap;
extern fn zguiTe_PostSwap(engine: *TestEngine) void;
pub const isTestQueueEmpty = zguiTe_IsTestQueueEmpty;
extern fn zguiTe_IsTestQueueEmpty(engine: *TestEngine) bool;
pub const getResult = zguiTe_GetResult;
extern fn zguiTe_GetResult(engine: *TestEngine, count_tested: *c_int, count_success: *c_int) void;
pub const printResultSummary = zguiTe_PrintResultSummary;
extern fn zguiTe_PrintResultSummary(engine: *TestEngine) void;
pub fn queueTests(engine: *TestEngine, group: TestGroup, filter_str: [:0]const u8, run_flags: TestRunFlags) void {
zguiTe_QueueTests(engine, group, filter_str.ptr, run_flags);
}
extern fn zguiTe_QueueTests(engine: *TestEngine, group: TestGroup, filter_str: [*]const u8, run_flags: TestRunFlags) void;
pub fn exportJunitResult(engine: *TestEngine, filename: [:0]const u8) void {
zguiTe_EngineExportJunitResult(engine, filename.ptr);
}
extern fn zguiTe_EngineExportJunitResult(engine: *TestEngine, filename: [*]const u8) void;
};
pub const TestContext = opaque {
pub fn setRef(ctx: *TestContext, ref: [:0]const u8) void {
return zguiTe_ContextSetRef(ctx, ref.ptr);
}
pub fn windowFocus(ctx: *TestContext, ref: [:0]const u8) void {
return zguiTe_ContextWindowFocus(ctx, ref.ptr);
}
pub fn yield(ctx: *TestContext, frame_count: i32) void {
return zguiTe_ContextYield(ctx, frame_count);
}
pub fn itemAction(ctx: *TestContext, action: Actions, ref: [:0]const u8, flags: TestOpFlags, action_arg: ?*anyopaque) void {
return zguiTe_ContextItemAction(ctx, action, ref.ptr, flags, action_arg);
}
pub fn itemInputStrValue(ctx: *TestContext, ref: [:0]const u8, value: [:0]const u8) void {
return zguiTe_ContextItemInputStrValue(ctx, ref.ptr, value.ptr);
}
pub fn itemInputIntValue(ctx: *TestContext, ref: [:0]const u8, value: i32) void {
return zguiTe_ContextItemInputIntValue(ctx, ref.ptr, value);
}
pub fn itemInputFloatValue(ctx: *TestContext, ref: [:0]const u8, value: f32) void {
return zguiTe_ContextItemInputFloatValue(ctx, ref.ptr, value);
}
pub fn menuAction(ctx: *TestContext, action: Actions, ref: [*]const u8) void {
return zguiTe_ContextMenuAction(ctx, action, ref);
}
pub fn dragAndDrop(ctx: *TestContext, ref_src: [:0]const u8, ref_dst: [:0]const u8, button: zgui.MouseButton) void {
return zguiTe_ContextDragAndDrop(ctx, ref_src.ptr, ref_dst.ptr, button);
}
pub fn keyDown(ctx: *TestContext, key_chord: c_int) void {
return zguiTe_ContextKeyDown(ctx, key_chord);
}
pub fn keyUp(ctx: *TestContext, key_chord: c_int) void {
return zguiTe_ContextKeyUp(ctx, key_chord);
}
extern fn zguiTe_ContextSetRef(ctx: *TestContext, ref: [*]const u8) void;
extern fn zguiTe_ContextWindowFocus(ctx: *TestContext, ref: [*]const u8) void;
extern fn zguiTe_ContextYield(ctx: *TestContext, frame_count: c_int) void;
extern fn zguiTe_ContextMenuAction(ctx: *TestContext, action: Actions, ref: [*]const u8) void;
extern fn zguiTe_ContextItemAction(ctx: *TestContext, action: Actions, ref: [*]const u8, flags: TestOpFlags, action_arg: ?*anyopaque) void;
extern fn zguiTe_ContextItemInputStrValue(ctx: *TestContext, ref: [*]const u8, value: [*]const u8) void;
extern fn zguiTe_ContextItemInputIntValue(ctx: *TestContext, ref: [*]const u8, value: i32) void;
extern fn zguiTe_ContextItemInputFloatValue(ctx: *TestContext, ref: [*]const u8, value: f32) void;
extern fn zguiTe_ContextDragAndDrop(ctx: *TestContext, ref_src: [*]const u8, ref_dst: [*]const u8, button: zgui.MouseButton) void;
extern fn zguiTe_ContextKeyDown(ctx: *TestContext, key_chord: c_int) void;
extern fn zguiTe_ContextKeyUp(ctx: *TestContext, key_chord: c_int) void;
};
const ImGuiTestGuiFunc = fn (context: *TestContext) callconv(.C) void;
const ImGuiTestTestFunc = fn (context: *TestContext) callconv(.C) void;
pub const createContext = zguiTe_CreateContext;
extern fn zguiTe_CreateContext() *TestEngine;
pub const destroyContext = zguiTe_DestroyContext;
extern fn zguiTe_DestroyContext(engine: *TestEngine) void;
extern fn zguiTe_Check(filename: [*]const u8, func: [*]const u8, line: u32, flags: CheckFlags, resul: bool, expr: [*]const u8) bool;
pub fn check(src: std.builtin.SourceLocation, flags: CheckFlags, resul: bool, expr: [:0]const u8) bool {
return zguiTe_Check(src.file.ptr, src.fn_name.ptr, src.line, flags, resul, expr.ptr);
}
pub extern fn zguiTe_RegisterTest(
engine: *TestEngine,
category: [*]const u8,
name: [*]const u8,
src: [*]const u8,
src_line: c_int,
gui_fce: ?*const ImGuiTestGuiFunc,
gui_test_fce: ?*const ImGuiTestTestFunc,
) *Test;
pub fn checkTestError(
src: std.builtin.SourceLocation,
err: anyerror,
) void {
var buff: [128:0]u8 = undefined;
const msg = std.fmt.bufPrintZ(&buff, "Assert error: {}", .{err}) catch undefined;
_ = zguiTe_Check(src.file.ptr, src.fn_name.ptr, src.line, .{}, false, msg.ptr);
}
var _te_engine: ?*TestEngine = null;
pub fn getTestEngine() ?*TestEngine {
return _te_engine;
}
pub fn init() void {
_te_engine = createContext();
}
pub fn deinit() void {
destroyContext(_te_engine.?);
}

View File

@ -4,6 +4,16 @@
#include "implot.h" #include "implot.h"
#endif #endif
#if ZGUI_TE
#include "imgui_te_engine.h"
#include "imgui_te_context.h"
#include "imgui_te_ui.h"
#include "imgui_te_utils.h"
#include "imgui_te_exporters.h"
#endif
#include "imgui_internal.h"
#ifndef ZGUI_API #ifndef ZGUI_API
#define ZGUI_API #define ZGUI_API
#endif #endif
@ -51,6 +61,10 @@ ZGUI_API void zguiSetNextWindowBgAlpha(float alpha) {
ImGui::SetNextWindowBgAlpha(alpha); ImGui::SetNextWindowBgAlpha(alpha);
} }
ZGUI_API void zguiSetWindowFocus(const char* name) {
ImGui::SetWindowFocus(name);
}
ZGUI_API void zguiSetKeyboardFocusHere(int offset) { ZGUI_API void zguiSetKeyboardFocusHere(int offset) {
ImGui::SetKeyboardFocusHere(offset); ImGui::SetKeyboardFocusHere(offset);
} }
@ -63,12 +77,12 @@ ZGUI_API void zguiEnd(void) {
ImGui::End(); ImGui::End();
} }
ZGUI_API bool zguiBeginChild(const char* str_id, float w, float h, bool border, ImGuiWindowFlags flags) { ZGUI_API bool zguiBeginChild(const char* str_id, float w, float h, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) {
return ImGui::BeginChild(str_id, { w, h }, border, flags); return ImGui::BeginChild(str_id, { w, h }, child_flags, window_flags);
} }
ZGUI_API bool zguiBeginChildId(ImGuiID id, float w, float h, bool border, ImGuiWindowFlags flags) { ZGUI_API bool zguiBeginChildId(ImGuiID id, float w, float h, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) {
return ImGui::BeginChild(id, { w, h }, border, flags); return ImGui::BeginChild(id, { w, h }, child_flags, window_flags);
} }
ZGUI_API void zguiEndChild(void) { ZGUI_API void zguiEndChild(void) {
@ -213,6 +227,12 @@ ZGUI_API void zguiGetItemRectMin(float rect[2]) {
rect[1] = r.y; rect[1] = r.y;
} }
ZGUI_API void zguiGetItemRectSize(float rect[2]) {
const ImVec2 r = ImGui::GetItemRectSize();
rect[0] = r.x;
rect[1] = r.y;
}
ZGUI_API void zguiGetCursorPos(float pos[2]) { ZGUI_API void zguiGetCursorPos(float pos[2]) {
const ImVec2 p = ImGui::GetCursorPos(); const ImVec2 p = ImGui::GetCursorPos();
pos[0] = p.x; pos[0] = p.x;
@ -2208,6 +2228,66 @@ ZGUI_API void zguiViewport_GetWorkSize(ImGuiViewport* viewport, float p[2]) {
p[1] = sz.y; p[1] = sz.y;
} }
//--------------------------------------------------------------------------------------------------
//
// Docking
//
//--------------------------------------------------------------------------------------------------
ZGUI_API ImGuiID zguiDockSpace(const char* str_id, float size[2], ImGuiDockNodeFlags flags) {
return ImGui::DockSpace(ImGui::GetID(str_id), {size[0], size[1]}, flags);
}
ZGUI_API ImGuiID zguiDockSpaceOverViewport(const ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags) {
return ImGui::DockSpaceOverViewport(viewport, dockspace_flags);
}
//--------------------------------------------------------------------------------------------------
//
// DockBuilder (Unstable internal imgui API, subject to change, use at own risk)
//
//--------------------------------------------------------------------------------------------------
ZGUI_API void zguiDockBuilderDockWindow(const char* window_name, ImGuiID node_id) {
ImGui::DockBuilderDockWindow(window_name, node_id);
}
ZGUI_API ImGuiID zguiDockBuilderAddNode(ImGuiID node_id, ImGuiDockNodeFlags flags) {
return ImGui::DockBuilderAddNode(node_id, flags);
}
ZGUI_API void zguiDockBuilderRemoveNode(ImGuiID node_id) {
ImGui::DockBuilderRemoveNode(node_id);
}
ZGUI_API void zguiDockBuilderSetNodePos(ImGuiID node_id, float pos[2]) {
ImGui::DockBuilderSetNodePos(node_id, {pos[0], pos[1]});
}
ZGUI_API void zguiDockBuilderSetNodeSize(ImGuiID node_id, float size[2]) {
ImGui::DockBuilderSetNodeSize(node_id, {size[0], size[1]});
}
ZGUI_API ImGuiID zguiDockBuilderSplitNode(
ImGuiID node_id,
ImGuiDir split_dir,
float size_ratio_for_node_at_dir,
ImGuiID* out_id_at_dir,
ImGuiID* out_id_at_opposite_dir
) {
return ImGui::DockBuilderSplitNode(
node_id,
split_dir,
size_ratio_for_node_at_dir,
out_id_at_dir,
out_id_at_opposite_dir
);
}
ZGUI_API void zguiDockBuilderFinish(ImGuiID node_id) {
ImGui::DockBuilderFinish(node_id);
}
#if ZGUI_IMPLOT #if ZGUI_IMPLOT
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// //
@ -2473,3 +2553,156 @@ ZGUI_API void zguiPlot_PlotText(
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
} /* extern "C" */ } /* extern "C" */
#if ZGUI_TE
//--------------------------------------------------------------------------------------------------
//
// ImGUI Test Engine
//
//--------------------------------------------------------------------------------------------------
extern "C"
{
ZGUI_API void *zguiTe_CreateContext(void)
{
ImGuiTestEngine *e = ImGuiTestEngine_CreateContext();
ImGuiTestEngine_Start(e, ImGui::GetCurrentContext());
ImGuiTestEngine_InstallDefaultCrashHandler();
return e;
}
ZGUI_API void zguiTe_DestroyContext(ImGuiTestEngine *engine)
{
ImGuiTestEngine_DestroyContext(engine);
}
ZGUI_API void zguiTe_EngineSetRunSpeed(ImGuiTestEngine *engine, ImGuiTestRunSpeed speed)
{
ImGuiTestEngine_GetIO(engine).ConfigRunSpeed = speed;
}
ZGUI_API void zguiTe_EngineExportJunitResult(ImGuiTestEngine *engine, const char *filename)
{
ImGuiTestEngine_GetIO(engine).ExportResultsFilename = filename;
ImGuiTestEngine_GetIO(engine).ExportResultsFormat = ImGuiTestEngineExportFormat_JUnitXml;
}
ZGUI_API void zguiTe_TryAbortEngine(ImGuiTestEngine *engine)
{
ImGuiTestEngine_TryAbortEngine(engine);
}
ZGUI_API void zguiTe_Stop(ImGuiTestEngine *engine)
{
ImGuiTestEngine_Stop(engine);
}
ZGUI_API void zguiTe_PostSwap(ImGuiTestEngine *engine)
{
ImGuiTestEngine_PostSwap(engine);
}
ZGUI_API bool zguiTe_IsTestQueueEmpty(ImGuiTestEngine *engine)
{
return ImGuiTestEngine_IsTestQueueEmpty(engine);
}
ZGUI_API void zguiTe_GetResult(ImGuiTestEngine *engine, int *count_tested, int *count_success)
{
int ct = 0;
int cs = 0;
ImGuiTestEngine_GetResult(engine, ct, cs);
*count_tested = ct;
*count_success = cs;
}
ZGUI_API void zguiTe_PrintResultSummary(ImGuiTestEngine *engine)
{
ImGuiTestEngine_PrintResultSummary(engine);
}
ZGUI_API void zguiTe_QueueTests(ImGuiTestEngine *engine, ImGuiTestGroup group, const char *filter_str, ImGuiTestRunFlags run_flags)
{
ImGuiTestEngine_QueueTests(engine, group, filter_str, run_flags);
}
ZGUI_API void zguiTe_ShowTestEngineWindows(ImGuiTestEngine *engine, bool *p_open)
{
ImGuiTestEngine_ShowTestEngineWindows(engine, p_open);
}
ZGUI_API void *zguiTe_RegisterTest(ImGuiTestEngine *engine, const char *category, const char *name, const char *src_file, int src_line, ImGuiTestGuiFunc *gui_fce, ImGuiTestTestFunc *gui_test_fce)
{
auto t = ImGuiTestEngine_RegisterTest(engine, category, name, src_file, src_line);
t->GuiFunc = gui_fce;
t->TestFunc = gui_test_fce;
return t;
}
ZGUI_API bool zguiTe_Check(const char *file, const char *func, int line, ImGuiTestCheckFlags flags, bool result, const char *expr)
{
return ImGuiTestEngine_Check(file, func, line, flags, result, expr);
}
// CONTEXT
ZGUI_API void zguiTe_ContextSetRef(ImGuiTestContext *ctx, const char *ref)
{
ctx->SetRef(ref);
}
ZGUI_API void zguiTe_ContextWindowFocus(ImGuiTestContext *ctx, const char *ref)
{
ctx->WindowFocus(ref);
}
ZGUI_API void zguiTe_ContextItemAction(ImGuiTestContext *ctx, ImGuiTestAction action, const char *ref, ImGuiTestOpFlags flags = 0, void *action_arg = NULL)
{
ctx->ItemAction(action, ref, flags, action_arg);
}
ZGUI_API void zguiTe_ContextItemInputStrValue(ImGuiTestContext *ctx, const char *ref, const char *value)
{
ctx->ItemInputValue(ref, value);
}
ZGUI_API void zguiTe_ContextItemInputIntValue(ImGuiTestContext *ctx, const char *ref, int value)
{
ctx->ItemInputValue(ref, value);
}
ZGUI_API void zguiTe_ContextItemInputFloatValue(ImGuiTestContext *ctx, const char *ref, float value)
{
ctx->ItemInputValue(ref, value);
}
ZGUI_API void zguiTe_ContextYield(ImGuiTestContext *ctx, int frame_count)
{
ctx->Yield(frame_count);
}
ZGUI_API void zguiTe_ContextMenuAction(ImGuiTestContext *ctx, ImGuiTestAction action, const char *ref)
{
ctx->MenuAction(action, ref);
}
ZGUI_API void zguiTe_ContextDragAndDrop(ImGuiTestContext *ctx, const char *ref_src, const char *ref_dst, ImGuiMouseButton button)
{
ctx->ItemDragAndDrop(ref_src, ref_dst, button);
}
ZGUI_API void zguiTe_ContextKeyDown(ImGuiTestContext *ctx, ImGuiKeyChord key_chord)
{
ctx->KeyDown(key_chord);
}
ZGUI_API void zguiTe_ContextKeyUp(ImGuiTestContext *ctx, ImGuiKeyChord key_chord)
{
ctx->KeyUp(key_chord);
}
} /* extern "C" */
#endif