chore: update to 2a0400ade5535001bfc09e0e7531515e232be6e7
This commit is contained in:
parent
e009f01d38
commit
c461e29eaa
|
@ -1,2 +1,3 @@
|
|||
/zig-cache
|
||||
/zig-out
|
||||
.zig-cache/
|
||||
zig-cache/
|
||||
zig-out/
|
||||
|
|
|
@ -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.
|
60
README.md
60
README.md
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
* [DrawList API](#drawlist-api) for vector graphics, text rendering and custom widgets
|
||||
* [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
|
||||
|
||||
|
@ -37,11 +44,11 @@ pub fn build(b: *std.Build) void {
|
|||
exe.linkLibrary(zglfw.artifact("glfw"));
|
||||
|
||||
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", .{});
|
||||
exe.root_module.addImport("zgpu", zglfw.module("root"));
|
||||
exe.linkLibrary(zglfw.artifact("wgpu"));
|
||||
exe.root_module.addImport("zgpu", zgpu.module("root"));
|
||||
exe.linkLibrary(zgpu.artifact("zdawn"));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -147,3 +154,48 @@ if (zgui.plot.beginPlot("Line Plot", .{ .h = -1.0 })) {
|
|||
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
112
build.zig
|
@ -6,6 +6,7 @@ pub const Backend = enum {
|
|||
glfw_opengl3,
|
||||
glfw_dx12,
|
||||
win32_dx12,
|
||||
glfw,
|
||||
};
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
|
@ -24,6 +25,16 @@ pub fn build(b: *std.Build) void {
|
|||
"with_implot",
|
||||
"Build with bundled implot source",
|
||||
) 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();
|
||||
|
@ -34,7 +45,7 @@ pub fn build(b: *std.Build) void {
|
|||
const options_module = options_step.createModule();
|
||||
|
||||
_ = b.addModule("root", .{
|
||||
.root_source_file = .{ .path = "src/gui.zig" },
|
||||
.root_source_file = b.path("src/gui.zig"),
|
||||
.imports = &.{
|
||||
.{ .name = "zgui_options", .module = options_module },
|
||||
},
|
||||
|
@ -49,7 +60,6 @@ pub fn build(b: *std.Build) void {
|
|||
.optimize = optimize,
|
||||
});
|
||||
|
||||
b.installArtifact(lib);
|
||||
if (target.result.os.tag == .windows) {
|
||||
lib.defineCMacro("IMGUI_API", "__declspec(dllexport)");
|
||||
lib.defineCMacro("IMPLOT_API", "__declspec(dllexport)");
|
||||
|
@ -69,15 +79,15 @@ pub fn build(b: *std.Build) void {
|
|||
|
||||
b.installArtifact(imgui);
|
||||
|
||||
imgui.addIncludePath(.{ .path = "libs" });
|
||||
imgui.addIncludePath(.{ .path = "libs/imgui" });
|
||||
imgui.addIncludePath(b.path("libs"));
|
||||
imgui.addIncludePath(b.path("libs/imgui"));
|
||||
|
||||
imgui.linkLibC();
|
||||
if (target.result.abi != .msvc)
|
||||
imgui.linkLibCpp();
|
||||
|
||||
imgui.addCSourceFile(.{
|
||||
.file = .{ .path = "src/zgui.cpp" },
|
||||
.file = b.path("src/zgui.cpp"),
|
||||
.flags = cflags,
|
||||
});
|
||||
|
||||
|
@ -106,12 +116,79 @@ pub fn build(b: *std.Build) void {
|
|||
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) {
|
||||
.glfw_wgpu => {
|
||||
const zglfw = b.dependency("zglfw", .{});
|
||||
const zgpu = b.dependency("zgpu", .{});
|
||||
imgui.addIncludePath(.{ .path = zglfw.path("libs/glfw/include").getPath(b) });
|
||||
imgui.addIncludePath(.{ .path = zgpu.path("libs/dawn/include").getPath(b) });
|
||||
imgui.addIncludePath(zglfw.path("libs/glfw/include"));
|
||||
imgui.addIncludePath(zgpu.path("libs/dawn/include"));
|
||||
imgui.addCSourceFiles(.{
|
||||
.files = &.{
|
||||
"libs/imgui/backends/imgui_impl_glfw.cpp",
|
||||
|
@ -122,7 +199,7 @@ pub fn build(b: *std.Build) void {
|
|||
},
|
||||
.glfw_opengl3 => {
|
||||
const zglfw = b.dependency("zglfw", .{});
|
||||
imgui.addIncludePath(.{ .path = zglfw.path("libs/glfw/include").getPath(b) });
|
||||
imgui.addIncludePath(zglfw.path("libs/glfw/include"));
|
||||
imgui.addCSourceFiles(.{
|
||||
.files = &.{
|
||||
"libs/imgui/backends/imgui_impl_glfw.cpp",
|
||||
|
@ -133,7 +210,7 @@ pub fn build(b: *std.Build) void {
|
|||
},
|
||||
.glfw_dx12 => {
|
||||
const zglfw = b.dependency("zglfw", .{});
|
||||
imgui.addIncludePath(.{ .path = zglfw.path("libs/glfw/include").getPath(b) });
|
||||
imgui.addIncludePath(zglfw.path("libs/glfw/include"));
|
||||
imgui.addCSourceFiles(.{
|
||||
.files = &.{
|
||||
"libs/imgui/backends/imgui_impl_glfw.cpp",
|
||||
|
@ -153,6 +230,21 @@ pub fn build(b: *std.Build) void {
|
|||
});
|
||||
imgui.linkSystemLibrary("d3dcompiler_47");
|
||||
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 => {},
|
||||
}
|
||||
|
@ -161,7 +253,7 @@ pub fn build(b: *std.Build) void {
|
|||
|
||||
const tests = b.addTest(.{
|
||||
.name = "zgui-tests",
|
||||
.root_source_file = .{ .path = "src/gui.zig" },
|
||||
.root_source_file = b.path("src/gui.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.{
|
||||
.name = "zgui",
|
||||
.version = "0.1.0",
|
||||
.version = "0.2.0",
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
// Implemented features:
|
||||
// [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'.
|
||||
// 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.
|
||||
// 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.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
// 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
|
||||
|
||||
// CHANGELOG
|
||||
// (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.
|
||||
// 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)
|
||||
|
@ -39,6 +45,7 @@
|
|||
// 2018-02-22: Merged into master with all Win32 code synchronized to other examples.
|
||||
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DISABLE
|
||||
#include "imgui_impl_dx12.h"
|
||||
|
||||
// DirectX
|
||||
|
@ -47,43 +54,22 @@
|
|||
#include <d3dcompiler.h>
|
||||
#ifdef _MSC_VER
|
||||
#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
|
||||
|
||||
// DirectX data
|
||||
struct ImGui_ImplDX12_RenderBuffers
|
||||
{
|
||||
ID3D12Resource* IndexBuffer;
|
||||
ID3D12Resource* VertexBuffer;
|
||||
int IndexBufferSize;
|
||||
int VertexBufferSize;
|
||||
};
|
||||
|
||||
struct ImGui_ImplDX12_Data
|
||||
{
|
||||
ID3D12Device* pd3dDevice;
|
||||
ID3D12RootSignature* pRootSignature;
|
||||
ID3D12PipelineState* pPipelineState;
|
||||
DXGI_FORMAT RTVFormat;
|
||||
ID3D12Resource* pFontTextureResource;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE hFontSrvCpuDescHandle;
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE hFontSrvGpuDescHandle;
|
||||
ID3D12Device* pd3dDevice;
|
||||
ID3D12RootSignature* pRootSignature;
|
||||
ID3D12PipelineState* pPipelineState;
|
||||
DXGI_FORMAT RTVFormat;
|
||||
ID3D12Resource* pFontTextureResource;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE hFontSrvCpuDescHandle;
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE hFontSrvGpuDescHandle;
|
||||
ID3D12DescriptorHeap* pd3dSrvDescHeap;
|
||||
UINT numFramesInFlight;
|
||||
|
||||
ImGui_ImplDX12_RenderBuffers* pFrameResources;
|
||||
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];
|
||||
ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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
|
||||
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)
|
||||
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();
|
||||
bd->frameIndex = bd->frameIndex + 1;
|
||||
ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[bd->frameIndex % bd->numFramesInFlight];
|
||||
ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)draw_data->OwnerViewport->RendererUserData;
|
||||
vd->FrameIndex++;
|
||||
ImGui_ImplDX12_RenderBuffers* fr = &vd->FrameRenderBuffers[vd->FrameIndex % bd->numFramesInFlight];
|
||||
|
||||
// Create and grow vertex/index buffers if needed
|
||||
if (fr->VertexBuffer == nullptr || fr->VertexBufferSize < draw_data->TotalVtxCount)
|
||||
|
@ -522,6 +598,19 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
|
|||
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");
|
||||
if (D3D12SerializeRootSignatureFn == nullptr)
|
||||
return false;
|
||||
|
@ -529,6 +618,7 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
|
|||
ID3DBlob* blob = nullptr;
|
||||
if (D3D12SerializeRootSignatureFn(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, nullptr) != S_OK)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
bd->pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignature));
|
||||
blob->Release();
|
||||
|
@ -591,9 +681,9 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
|
|||
// Create the input 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 },
|
||||
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_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 },
|
||||
{ "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)offsetof(ImDrawVert, uv), 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 };
|
||||
}
|
||||
|
@ -677,24 +767,24 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
|
|||
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()
|
||||
{
|
||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||
if (!bd || !bd->pd3dDevice)
|
||||
return;
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
SafeRelease(bd->pRootSignature);
|
||||
SafeRelease(bd->pPipelineState);
|
||||
SafeRelease(bd->pFontTextureResource);
|
||||
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,
|
||||
|
@ -708,25 +798,21 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO
|
|||
io.BackendRendererUserData = (void*)bd;
|
||||
io.BackendRendererName = "imgui_impl_dx12";
|
||||
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->RTVFormat = rtv_format;
|
||||
bd->hFontSrvCpuDescHandle = font_srv_cpu_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->frameIndex = UINT_MAX;
|
||||
IM_UNUSED(cbv_srv_heap); // Unused in master branch (will be used by multi-viewports)
|
||||
bd->pd3dSrvDescHeap = cbv_srv_heap;
|
||||
|
||||
// Create buffers with a default size (they will later be grown as needed)
|
||||
for (int i = 0; i < num_frames_in_flight; i++)
|
||||
{
|
||||
ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[i];
|
||||
fr->IndexBuffer = nullptr;
|
||||
fr->VertexBuffer = nullptr;
|
||||
fr->IndexBufferSize = 10000;
|
||||
fr->VertexBufferSize = 5000;
|
||||
}
|
||||
// Create a dummy ImGui_ImplDX12_ViewportData holder for the main viewport,
|
||||
// Since this is created and managed by the application, we will only use the ->Resources[] fields.
|
||||
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
|
||||
main_viewport->RendererUserData = IM_NEW(ImGui_ImplDX12_ViewportData)(bd->numFramesInFlight);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -737,10 +823,24 @@ void ImGui_ImplDX12_Shutdown()
|
|||
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
|
||||
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();
|
||||
delete[] bd->pFrameResources;
|
||||
|
||||
io.BackendRendererName = nullptr;
|
||||
io.BackendRendererUserData = nullptr;
|
||||
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasViewports);
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
|
@ -752,3 +852,247 @@ void ImGui_ImplDX12_NewFrame()
|
|||
if (!bd->pPipelineState)
|
||||
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
|
||||
|
|
|
@ -3,18 +3,23 @@
|
|||
|
||||
// Implemented features:
|
||||
// [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'.
|
||||
// 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.
|
||||
// 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.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
// 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 <dxgiformat.h> // DXGI_FORMAT
|
||||
|
||||
struct ID3D12Device;
|
||||
|
@ -23,7 +28,8 @@ struct ID3D12GraphicsCommandList;
|
|||
struct D3D12_CPU_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.
|
||||
// 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();
|
||||
|
||||
}
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -14,11 +14,17 @@
|
|||
|
||||
// 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.
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
// 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
|
||||
|
||||
// CHANGELOG
|
||||
// (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-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)
|
||||
|
@ -103,9 +109,8 @@
|
|||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DISABLE
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#include <stdio.h>
|
||||
#include <stdint.h> // intptr_t
|
||||
|
@ -175,9 +180,20 @@ extern "C" {
|
|||
#define GL_VERTEX_ARRAY_BINDING GL_VERTEX_ARRAY_BINDING_OES
|
||||
#endif
|
||||
|
||||
// Desktop GL 2.0+ has glPolygonMode() which GL ES and WebGL don't have.
|
||||
#ifdef GL_POLYGON_MODE
|
||||
#define IMGUI_IMPL_HAS_POLYGON_MODE
|
||||
// Desktop GL 2.0+ has extension and glPolygonMode() which GL ES and WebGL don't have..
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
#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
|
||||
|
||||
// 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
|
||||
#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]
|
||||
//#define 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!");
|
||||
|
||||
// Initialize our loader
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
|
||||
if (imgl3wInit() != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to initialize OpenGL loader!\n");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
|
||||
if (imgl3wInit() != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to initialize OpenGL loader!\n");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Setup backend capabilities flags
|
||||
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
|
||||
bd->HasClipOrigin = (bd->GlVersion >= 450);
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS
|
||||
#ifdef IMGUI_IMPL_OPENGL_HAS_EXTENSIONS
|
||||
GLint num_extensions = 0;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
|
||||
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)
|
||||
glDisable(GL_PRIMITIVE_RESTART);
|
||||
#endif
|
||||
#ifdef IMGUI_IMPL_HAS_POLYGON_MODE
|
||||
#ifdef IMGUI_IMPL_OPENGL_HAS_POLYGON_MODE
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
#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->AttribLocationVtxUV));
|
||||
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->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_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->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*)offsetof(ImDrawVert, uv)));
|
||||
GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, col)));
|
||||
}
|
||||
|
||||
// OpenGL3 Render function.
|
||||
|
@ -499,7 +505,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
|||
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||
GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object);
|
||||
#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);
|
||||
#endif
|
||||
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); }
|
||||
#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
|
||||
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]);
|
||||
}
|
||||
#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]);
|
||||
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;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
||||
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
|
||||
GLint last_vertex_array;
|
||||
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
|
||||
|
@ -919,6 +929,9 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
|
|||
// Restore modified GL state
|
||||
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||
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
|
||||
glBindVertexArray(last_vertex_array);
|
||||
#endif
|
||||
|
|
|
@ -14,8 +14,11 @@
|
|||
|
||||
// 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.
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
// 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
|
||||
|
||||
// About GLSL version:
|
||||
// The 'glsl_version' initialization parameter should be nullptr (default) or a "#version XXX" string.
|
||||
|
@ -26,20 +29,19 @@
|
|||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
// FIX(zig-gamedev)
|
||||
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
|
||||
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);
|
||||
|
||||
// (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();
|
||||
|
||||
// (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
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// zig-gamedev changes marked with `FIX(zig-gamedev)`
|
||||
|
||||
// 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.)
|
||||
|
@ -7,14 +5,24 @@
|
|||
// 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.
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
// 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
|
||||
|
||||
// CHANGELOG
|
||||
// (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: 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)
|
||||
|
@ -30,6 +38,11 @@
|
|||
// 2021-01-28: Initial version.
|
||||
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
// FIX(zig-gamedev):
|
||||
//#include "imgui_impl_wgpu.h"
|
||||
|
||||
#include <limits.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.
|
||||
extern "C" {
|
||||
|
||||
IMGUI_IMPL_API bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format, WGPUTextureFormat depth_format = WGPUTextureFormat_Undefined);
|
||||
IMGUI_IMPL_API void ImGui_ImplWGPU_Shutdown(void);
|
||||
IMGUI_IMPL_API void ImGui_ImplWGPU_NewFrame(void);
|
||||
// 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(void);
|
||||
IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects(void);
|
||||
IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects();
|
||||
IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects();
|
||||
|
||||
} // extern "C"
|
||||
|
||||
|
@ -82,16 +112,17 @@ struct Uniforms
|
|||
|
||||
struct ImGui_ImplWGPU_Data
|
||||
{
|
||||
WGPUDevice wgpuDevice = nullptr;
|
||||
WGPUQueue defaultQueue = nullptr;
|
||||
WGPUTextureFormat renderTargetFormat = WGPUTextureFormat_Undefined;
|
||||
WGPUTextureFormat depthStencilFormat = WGPUTextureFormat_Undefined;
|
||||
WGPURenderPipeline pipelineState = nullptr;
|
||||
ImGui_ImplWGPU_InitInfo initInfo;
|
||||
WGPUDevice wgpuDevice = nullptr;
|
||||
WGPUQueue defaultQueue = nullptr;
|
||||
WGPUTextureFormat renderTargetFormat = WGPUTextureFormat_Undefined;
|
||||
WGPUTextureFormat depthStencilFormat = WGPUTextureFormat_Undefined;
|
||||
WGPURenderPipeline pipelineState = nullptr;
|
||||
|
||||
RenderResources renderResources;
|
||||
FrameResources* pFrameResources = nullptr;
|
||||
unsigned int numFramesInFlight = 0;
|
||||
unsigned int frameIndex = UINT_MAX;
|
||||
RenderResources renderResources;
|
||||
FrameResources* pFrameResources = nullptr;
|
||||
unsigned int numFramesInFlight = 0;
|
||||
unsigned int frameIndex = UINT_MAX;
|
||||
};
|
||||
|
||||
// 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);
|
||||
res = nullptr;
|
||||
}
|
||||
// FIX(zig-gamedev): https://github.com/ocornut/imgui/commit/9266c0d2d1390e50d2d8070896932c2564594407
|
||||
static void SafeRelease(WGPUPipelineLayout& res)
|
||||
{
|
||||
if (res)
|
||||
wgpuPipelineLayoutRelease(res);
|
||||
res = nullptr;
|
||||
}
|
||||
{
|
||||
if (res)
|
||||
wgpuPipelineLayoutRelease(res);
|
||||
res = nullptr;
|
||||
}
|
||||
static void SafeRelease(WGPURenderPipeline& res)
|
||||
{
|
||||
if (res)
|
||||
|
@ -252,7 +282,6 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const c
|
|||
|
||||
WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
|
||||
wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
|
||||
// FiX(zig-gamedev): `.source` renamed to `.code`
|
||||
wgsl_desc.code = wgsl_source;
|
||||
|
||||
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)
|
||||
{
|
||||
// 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;
|
||||
|
||||
// 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
|
||||
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);
|
||||
|
||||
// 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)
|
||||
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
|
||||
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);
|
||||
|
@ -540,7 +571,6 @@ static void ImGui_ImplWGPU_CreateFontsTexture()
|
|||
WGPUSamplerDescriptor sampler_desc = {};
|
||||
sampler_desc.minFilter = WGPUFilterMode_Linear;
|
||||
sampler_desc.magFilter = WGPUFilterMode_Linear;
|
||||
// FIX(zig-gamedev): WGPUFilterMode_Linear should be WGPUMipmapFilterMode_Linear
|
||||
sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Linear;
|
||||
sampler_desc.addressModeU = 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.frontFace = WGPUFrontFace_CW;
|
||||
graphics_pipeline_desc.primitive.cullMode = WGPUCullMode_None;
|
||||
graphics_pipeline_desc.multisample.count = 1;
|
||||
graphics_pipeline_desc.multisample.mask = UINT_MAX;
|
||||
graphics_pipeline_desc.multisample.alphaToCoverageEnabled = false;
|
||||
graphics_pipeline_desc.multisample = bd->initInfo.PipelineMultisampleState;
|
||||
|
||||
// Bind group layouts
|
||||
WGPUBindGroupLayoutEntry common_bg_layout_entries[2] = {};
|
||||
|
@ -626,9 +654,9 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
|
|||
// Vertex input configuration
|
||||
WGPUVertexAttribute attribute_desc[] =
|
||||
{
|
||||
{ WGPUVertexFormat_Float32x2, (uint64_t)IM_OFFSETOF(ImDrawVert, pos), 0 },
|
||||
{ WGPUVertexFormat_Float32x2, (uint64_t)IM_OFFSETOF(ImDrawVert, uv), 1 },
|
||||
{ WGPUVertexFormat_Unorm8x4, (uint64_t)IM_OFFSETOF(ImDrawVert, col), 2 },
|
||||
{ WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, pos), 0 },
|
||||
{ WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, uv), 1 },
|
||||
{ WGPUVertexFormat_Unorm8x4, (uint64_t)offsetof(ImDrawVert, col), 2 },
|
||||
};
|
||||
|
||||
WGPUVertexBufferLayout buffer_layouts[1];
|
||||
|
@ -671,12 +699,10 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
|
|||
depth_stencil_state.depthWriteEnabled = false;
|
||||
depth_stencil_state.depthCompare = 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.depthFailOp = WGPUStencilOperation_Keep;
|
||||
depth_stencil_state.stencilFront.passOp = WGPUStencilOperation_Keep;
|
||||
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.depthFailOp = WGPUStencilOperation_Keep;
|
||||
depth_stencil_state.stencilBack.passOp = WGPUStencilOperation_Keep;
|
||||
|
@ -709,7 +735,6 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
|
|||
|
||||
SafeRelease(vertex_shader_desc.module);
|
||||
SafeRelease(pixel_shader_desc.module);
|
||||
// FIX(zig-gamedev): https://github.com/ocornut/imgui/commit/9266c0d2d1390e50d2d8070896932c2564594407
|
||||
SafeRelease(graphics_pipeline_desc.layout);
|
||||
SafeRelease(bg_layouts[0]);
|
||||
|
||||
|
@ -732,7 +757,7 @@ void ImGui_ImplWGPU_InvalidateDeviceObjects()
|
|||
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();
|
||||
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.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->renderTargetFormat = rt_format;
|
||||
bd->depthStencilFormat = depth_format;
|
||||
bd->numFramesInFlight = num_frames_in_flight;
|
||||
bd->renderTargetFormat = init_info->RenderTargetFormat;
|
||||
bd->depthStencilFormat = init_info->DepthStencilFormat;
|
||||
bd->numFramesInFlight = init_info->NumFramesInFlight;
|
||||
bd->frameIndex = UINT_MAX;
|
||||
|
||||
bd->renderResources.FontTexture = nullptr;
|
||||
|
@ -760,8 +786,8 @@ bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextur
|
|||
bd->renderResources.ImageBindGroupLayout = nullptr;
|
||||
|
||||
// Create buffers with a default size (they will later be grown as needed)
|
||||
bd->pFrameResources = new FrameResources[num_frames_in_flight];
|
||||
for (int i = 0; i < num_frames_in_flight; i++)
|
||||
bd->pFrameResources = new FrameResources[bd->numFramesInFlight];
|
||||
for (int i = 0; i < bd->numFramesInFlight; i++)
|
||||
{
|
||||
FrameResources* fr = &bd->pFrameResources[i];
|
||||
fr->IndexBuffer = nullptr;
|
||||
|
@ -801,3 +827,7 @@ void ImGui_ImplWGPU_NewFrame()
|
|||
if (!bd->pipelineState)
|
||||
ImGui_ImplWGPU_CreateDeviceObjects();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
|
|
|
@ -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
|
|
@ -3,17 +3,24 @@
|
|||
|
||||
// Implemented features:
|
||||
// [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: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||
// [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.
|
||||
// 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.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
// 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
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_win32.h"
|
||||
#ifndef IMGUI_DISABLE
|
||||
// FIX(zig-gamedev):
|
||||
// #include "imgui_impl_win32.h"
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
@ -32,8 +39,48 @@ typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILIT
|
|||
typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*);
|
||||
#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
|
||||
// (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-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).
|
||||
|
@ -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.
|
||||
// 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
|
||||
{
|
||||
HWND hWnd;
|
||||
HWND MouseHwnd;
|
||||
bool MouseTracked;
|
||||
int MouseTrackedArea; // 0: not tracked, 1: client are, 2: non-client area
|
||||
int MouseButtonsDown;
|
||||
INT64 Time;
|
||||
INT64 TicksPerSecond;
|
||||
ImGuiMouseCursor LastMouseCursor;
|
||||
UINT32 KeyboardCodePage;
|
||||
bool WantUpdateMonitors;
|
||||
|
||||
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||
bool HasGamepad;
|
||||
|
@ -110,7 +164,17 @@ static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData()
|
|||
}
|
||||
|
||||
// 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();
|
||||
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.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_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->WantUpdateMonitors = true;
|
||||
bd->TicksPerSecond = perf_frequency;
|
||||
bd->Time = perf_counter;
|
||||
bd->LastMouseCursor = ImGuiMouseCursor_COUNT;
|
||||
ImGui_ImplWin32_UpdateKeyboardCodePage();
|
||||
|
||||
// Set platform dependent data in viewport
|
||||
ImGui::GetMainViewport()->PlatformHandleRaw = (void*)hwnd;
|
||||
// Our mouse update function expect PlatformHandle to be filled for the main viewport
|
||||
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
|
||||
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||
|
@ -160,12 +231,25 @@ bool ImGui_ImplWin32_Init(void* hwnd)
|
|||
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()
|
||||
{
|
||||
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
ImGui_ImplWin32_ShutdownPlatformInterface();
|
||||
|
||||
// Unload XInput library
|
||||
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||
if (bd->XInputDLL)
|
||||
|
@ -174,6 +258,7 @@ void ImGui_ImplWin32_Shutdown()
|
|||
|
||||
io.BackendPlatformName = nullptr;
|
||||
io.BackendPlatformUserData = nullptr;
|
||||
io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_PlatformHasViewports | ImGuiBackendFlags_HasMouseHoveredViewport);
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
|
@ -247,31 +332,59 @@ static void ImGui_ImplWin32_UpdateKeyModifiers()
|
|||
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()
|
||||
{
|
||||
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
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)
|
||||
{
|
||||
// (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)
|
||||
{
|
||||
POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
|
||||
if (::ClientToScreen(bd->hWnd, &pos))
|
||||
::SetCursorPos(pos.x, pos.y);
|
||||
if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0)
|
||||
::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)
|
||||
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;
|
||||
if (::GetCursorPos(&pos) && ::ScreenToClient(bd->hWnd, &pos))
|
||||
io.AddMousePosEvent((float)pos.x, (float)pos.y);
|
||||
// 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)
|
||||
// (This is the position you can get with ::GetCursorPos() + ::ScreenToClient() or WM_MOUSEMOVE.)
|
||||
// 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
|
||||
|
@ -331,6 +444,35 @@ static void ImGui_ImplWin32_UpdateGamepads()
|
|||
#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()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
@ -341,6 +483,8 @@ void ImGui_ImplWin32_NewFrame()
|
|||
RECT rect = { 0, 0, 0, 0 };
|
||||
::GetClientRect(bd->hWnd, &rect);
|
||||
io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));
|
||||
if (bd->WantUpdateMonitors)
|
||||
ImGui_ImplWin32_UpdateMonitors();
|
||||
|
||||
// Setup time step
|
||||
INT64 current_time = 0;
|
||||
|
@ -478,6 +622,20 @@ static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam)
|
|||
case VK_F10: return ImGuiKey_F10;
|
||||
case VK_F11: return ImGuiKey_F11;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -502,6 +660,19 @@ static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam)
|
|||
// 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);
|
||||
#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)
|
||||
{
|
||||
if (ImGui::GetCurrentContext() == nullptr)
|
||||
|
@ -513,27 +684,50 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
|
|||
switch (msg)
|
||||
{
|
||||
case WM_MOUSEMOVE:
|
||||
case WM_NCMOUSEMOVE:
|
||||
{
|
||||
// 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;
|
||||
if (!bd->MouseTracked)
|
||||
if (bd->MouseTrackedArea != area)
|
||||
{
|
||||
TRACKMOUSEEVENT tme = { sizeof(tme), TME_LEAVE, hwnd, 0 };
|
||||
::TrackMouseEvent(&tme);
|
||||
bd->MouseTracked = true;
|
||||
TRACKMOUSEEVENT tme_cancel = { sizeof(tme_cancel), TME_CANCEL, hwnd, 0 };
|
||||
TRACKMOUSEEVENT tme_track = { sizeof(tme_track), (DWORD)((area == 2) ? (TME_LEAVE | TME_NONCLIENT) : TME_LEAVE), hwnd, 0 };
|
||||
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;
|
||||
}
|
||||
case WM_MOUSELEAVE:
|
||||
if (bd->MouseHwnd == hwnd)
|
||||
bd->MouseHwnd = nullptr;
|
||||
bd->MouseTracked = false;
|
||||
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
|
||||
case WM_NCMOUSELEAVE:
|
||||
{
|
||||
const int area = (msg == WM_MOUSELEAVE) ? 1 : 2;
|
||||
if (bd->MouseTrackedArea == area)
|
||||
{
|
||||
if (bd->MouseHwnd == hwnd)
|
||||
bd->MouseHwnd = nullptr;
|
||||
bd->MouseTrackedArea = 0;
|
||||
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
|
||||
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
|
||||
case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
|
||||
case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
|
||||
{
|
||||
ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
|
||||
int button = 0;
|
||||
if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; }
|
||||
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)
|
||||
::SetCapture(hwnd);
|
||||
bd->MouseButtonsDown |= 1 << button;
|
||||
io.AddMouseSourceEvent(mouse_source);
|
||||
io.AddMouseButtonEvent(button, true);
|
||||
return 0;
|
||||
}
|
||||
|
@ -550,6 +745,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
|
|||
case WM_MBUTTONUP:
|
||||
case WM_XBUTTONUP:
|
||||
{
|
||||
ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
|
||||
int button = 0;
|
||||
if (msg == WM_LBUTTONUP) { button = 0; }
|
||||
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);
|
||||
if (bd->MouseButtonsDown == 0 && ::GetCapture() == hwnd)
|
||||
::ReleaseCapture();
|
||||
io.AddMouseSourceEvent(mouse_source);
|
||||
io.AddMouseButtonEvent(button, false);
|
||||
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);
|
||||
return 0;
|
||||
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;
|
||||
case WM_KEYDOWN:
|
||||
case WM_KEYUP:
|
||||
|
@ -583,10 +780,14 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
|
|||
int vk = (int)wParam;
|
||||
if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED))
|
||||
vk = IM_VK_KEYPAD_ENTER;
|
||||
|
||||
// Submit key event
|
||||
const ImGuiKey key = ImGui_ImplWin32_VirtualKeyToImGuiKey(vk);
|
||||
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)
|
||||
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:
|
||||
io.AddFocusEvent(msg == WM_SETFOCUS);
|
||||
return 0;
|
||||
case WM_INPUTLANGCHANGE:
|
||||
ImGui_ImplWin32_UpdateKeyboardCodePage();
|
||||
return 0;
|
||||
case WM_CHAR:
|
||||
if (::IsWindowUnicode(hwnd))
|
||||
{
|
||||
|
@ -624,7 +828,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
|
|||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
return 0;
|
||||
|
@ -639,6 +843,9 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
|
|||
bd->WantUpdateHasGamepad = true;
|
||||
#endif
|
||||
return 0;
|
||||
case WM_DISPLAYCHANGE:
|
||||
bd->WantUpdateMonitors = true;
|
||||
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
|
||||
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())
|
||||
{
|
||||
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
|
||||
|
|
|
@ -3,21 +3,26 @@
|
|||
|
||||
// Implemented features:
|
||||
// [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: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||
// [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.
|
||||
// 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.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
// 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
|
||||
|
||||
extern "C" { // mziulek
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
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();
|
||||
|
||||
|
@ -45,4 +50,4 @@ IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); //
|
|||
// - Use together with e.g. clearing your framebuffer with zero-alpha.
|
||||
IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd
|
||||
|
||||
}
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
|
|
|
@ -153,6 +153,8 @@ typedef khronos_intptr_t GLintptr;
|
|||
#define GL_ARRAY_BUFFER_BINDING 0x8894
|
||||
#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
|
||||
#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 PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
|
||||
typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
|
||||
|
|
|
@ -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.
|
||||
// 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
|
||||
// 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.
|
||||
// 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
|
||||
|
@ -26,21 +26,21 @@
|
|||
//#define IMGUI_API __declspec( dllexport )
|
||||
//#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.
|
||||
#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.
|
||||
//---- 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_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.
|
||||
// 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_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.
|
||||
//#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_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_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.
|
||||
|
@ -50,21 +50,24 @@
|
|||
//#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
|
||||
// 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_USER_H_FILENAME "my_folder/my_imgui_user.h"
|
||||
|
||||
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
||||
//#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
|
||||
|
||||
//---- 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.
|
||||
//#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_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_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)
|
||||
// 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'.
|
||||
//#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)
|
||||
// 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
|
||||
|
@ -105,7 +114,7 @@
|
|||
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
|
||||
//#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.)
|
||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||
//#define IM_DEBUG_BREAK __debugbreak()
|
||||
|
@ -113,10 +122,10 @@
|
|||
//---- Debug Tools: Enable slower asserts
|
||||
//#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
|
||||
{
|
||||
void MyFunction(const char* name, const MyMatrix44& v);
|
||||
void MyFunction(const char* name, MyMatrix44* mtx);
|
||||
}
|
||||
*/
|
||||
|
|
9213
libs/imgui/imgui.cpp
9213
libs/imgui/imgui.cpp
File diff suppressed because it is too large
Load Diff
1138
libs/imgui/imgui.h
1138
libs/imgui/imgui.h
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,4 @@
|
|||
// dear imgui, v1.89.6
|
||||
// dear imgui, v1.90.4
|
||||
// (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 "-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 "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter
|
||||
#elif defined(__GNUC__)
|
||||
#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
|
||||
|
@ -134,7 +135,7 @@ namespace IMGUI_STB_NAMESPACE
|
|||
#define STBTT_sqrt(x) ImSqrt(x)
|
||||
#define STBTT_pow(x,y) ImPow(x,y)
|
||||
#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_STATIC
|
||||
#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_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||||
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_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 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_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||||
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_PlotLinesHovered] = 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_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||||
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_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 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()
|
||||
{
|
||||
// Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory.
|
||||
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0);
|
||||
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4));
|
||||
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID));
|
||||
IM_STATIC_ASSERT(offsetof(ImDrawCmd, ClipRect) == 0);
|
||||
IM_STATIC_ASSERT(offsetof(ImDrawCmd, TextureId) == sizeof(ImVec4));
|
||||
IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID));
|
||||
if (_Splitter._Count > 1)
|
||||
_Splitter.Merge(this);
|
||||
|
||||
|
@ -474,7 +481,7 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
|
|||
}
|
||||
|
||||
// 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_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)
|
||||
|
@ -560,7 +567,7 @@ int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const
|
|||
{
|
||||
// Automatic segment count
|
||||
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
|
||||
else
|
||||
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;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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_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_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloorSigned(a_max_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)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 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
/*
|
||||
IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4));
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
// Obsoleted in 1.82 (from February 2021)
|
||||
// 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;
|
||||
|
||||
// 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);
|
||||
|
||||
// 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)
|
||||
if (flags == ~0) { return ImDrawFlags_RoundCornersAll; }
|
||||
// - Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations). Read details in older version of this code.
|
||||
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'
|
||||
#endif
|
||||
|
||||
// If this 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...
|
||||
*/
|
||||
// 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. 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!");
|
||||
|
||||
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)
|
||||
{
|
||||
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 = 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)
|
||||
{
|
||||
PathLineTo(a);
|
||||
|
@ -1544,6 +1563,35 @@ void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, in
|
|||
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
|
||||
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
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
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!
|
||||
void ImDrawData::DeIndexAllBuffers()
|
||||
{
|
||||
|
@ -1832,15 +1937,9 @@ void ImDrawData::DeIndexAllBuffers()
|
|||
// or if there is a difference between your window resolution and framebuffer resolution.
|
||||
void ImDrawData::ScaleClipRects(const ImVec2& fb_scale)
|
||||
{
|
||||
for (int i = 0; i < CmdListsCount; i++)
|
||||
{
|
||||
ImDrawList* cmd_list = CmdLists[i];
|
||||
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);
|
||||
}
|
||||
}
|
||||
for (ImDrawList* draw_list : CmdLists)
|
||||
for (ImDrawCmd& cmd : draw_list->CmdBuffer)
|
||||
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
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -1904,10 +2011,11 @@ ImFontConfig::ImFontConfig()
|
|||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
FontDataOwnedByAtlas = true;
|
||||
OversampleH = 3; // FIXME: 2 may be a better default?
|
||||
OversampleH = 2;
|
||||
OversampleV = 1;
|
||||
GlyphMaxAdvanceX = FLT_MAX;
|
||||
RasterizerMultiply = 1.0f;
|
||||
RasterizerDensity = 1.0f;
|
||||
EllipsisChar = (ImWchar)-1;
|
||||
}
|
||||
|
||||
|
@ -1981,19 +2089,19 @@ ImFontAtlas::~ImFontAtlas()
|
|||
void ImFontAtlas::ClearInputData()
|
||||
{
|
||||
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
|
||||
for (int i = 0; i < ConfigData.Size; i++)
|
||||
if (ConfigData[i].FontData && ConfigData[i].FontDataOwnedByAtlas)
|
||||
for (ImFontConfig& font_cfg : ConfigData)
|
||||
if (font_cfg.FontData && font_cfg.FontDataOwnedByAtlas)
|
||||
{
|
||||
IM_FREE(ConfigData[i].FontData);
|
||||
ConfigData[i].FontData = NULL;
|
||||
IM_FREE(font_cfg.FontData);
|
||||
font_cfg.FontData = NULL;
|
||||
}
|
||||
|
||||
// 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++)
|
||||
if (Fonts[i]->ConfigData >= ConfigData.Data && Fonts[i]->ConfigData < ConfigData.Data + ConfigData.Size)
|
||||
for (ImFont* font : Fonts)
|
||||
if (font->ConfigData >= ConfigData.Data && font->ConfigData < ConfigData.Data + ConfigData.Size)
|
||||
{
|
||||
Fonts[i]->ConfigData = NULL;
|
||||
Fonts[i]->ConfigDataCount = 0;
|
||||
font->ConfigData = NULL;
|
||||
font->ConfigDataCount = 0;
|
||||
}
|
||||
ConfigData.clear();
|
||||
CustomRects.clear();
|
||||
|
@ -2090,6 +2198,8 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
|
|||
if (new_font_cfg.DstFont->EllipsisChar == (ImWchar)-1)
|
||||
new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar;
|
||||
|
||||
ImFontAtlasUpdateConfigDataPointers(this);
|
||||
|
||||
// Invalidate texture
|
||||
TexReady = false;
|
||||
ClearTexData();
|
||||
|
@ -2126,7 +2236,7 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
|
|||
if (font_cfg.Name[0] == '\0')
|
||||
ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf, %dpx", (int)font_cfg.SizePixels);
|
||||
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 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().
|
||||
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()!");
|
||||
ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
|
||||
IM_ASSERT(font_cfg.FontData == NULL);
|
||||
font_cfg.FontData = ttf_data;
|
||||
font_cfg.FontDataSize = ttf_size;
|
||||
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.FontData = font_data;
|
||||
font_cfg.FontDataSize = font_data_size;
|
||||
font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels;
|
||||
if (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);
|
||||
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))
|
||||
{
|
||||
IM_ASSERT(0 && "stbtt_InitFont(): failed to parse FontData. It is correct and complete? Check FontDataSize.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Measure highest codepoints
|
||||
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
|
||||
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.array_of_unicode_codepoints = src_tmp.GlyphsList.Data;
|
||||
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;
|
||||
|
||||
// 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;
|
||||
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;
|
||||
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 descent = ImFloor(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1));
|
||||
const float ascent = ImTrunc(unscaled_ascent * font_scale + ((unscaled_ascent > 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);
|
||||
const float font_off_x = cfg.GlyphOffset.x;
|
||||
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++)
|
||||
{
|
||||
// Register glyph
|
||||
|
@ -2578,7 +2694,11 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
|
|||
stbtt_aligned_quad q;
|
||||
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);
|
||||
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
|
||||
|
||||
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)
|
||||
{
|
||||
if (!font_config->MergeMode)
|
||||
{
|
||||
font->ClearOutputData();
|
||||
font->FontSize = font_config->SizePixels;
|
||||
font->ConfigData = font_config;
|
||||
font->ConfigDataCount = 0;
|
||||
IM_ASSERT(font->ConfigData == font_config);
|
||||
font->ContainerAtlas = atlas;
|
||||
font->Ascent = ascent;
|
||||
font->Descent = descent;
|
||||
}
|
||||
font->ConfigDataCount++;
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
if (atlas->PackIdMouseCursors < 0)
|
||||
{
|
||||
|
@ -2798,9 +2937,9 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas)
|
|||
}
|
||||
|
||||
// Build all fonts lookup tables
|
||||
for (int i = 0; i < atlas->Fonts.Size; i++)
|
||||
if (atlas->Fonts[i]->DirtyLookupTables)
|
||||
atlas->Fonts[i]->BuildLookupTable();
|
||||
for (ImFont* font : atlas->Fonts)
|
||||
if (font->DirtyLookupTables)
|
||||
font->BuildLookupTable();
|
||||
|
||||
atlas->TexReady = true;
|
||||
}
|
||||
|
@ -3165,6 +3304,7 @@ void ImFont::BuildLookupTable()
|
|||
max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint);
|
||||
|
||||
// Build lookup table
|
||||
IM_ASSERT(Glyphs.Size > 0 && "Font has not loaded glyph!");
|
||||
IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved
|
||||
IndexAdvanceX.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);
|
||||
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;
|
||||
x1 += char_off_x;
|
||||
}
|
||||
|
@ -3549,8 +3689,8 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im
|
|||
if (glyph->Colored)
|
||||
col |= ~IM_COL32_A_MASK;
|
||||
float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f;
|
||||
float x = IM_FLOOR(pos.x);
|
||||
float y = IM_FLOOR(pos.y);
|
||||
float x = IM_TRUNC(pos.x);
|
||||
float y = IM_TRUNC(pos.y);
|
||||
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);
|
||||
}
|
||||
|
@ -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.
|
||||
|
||||
// Align to be pixel perfect
|
||||
float x = IM_FLOOR(pos.x);
|
||||
float y = IM_FLOOR(pos.y);
|
||||
float x = IM_TRUNC(pos.x);
|
||||
float y = IM_TRUNC(pos.y);
|
||||
if (y > clip_rect.w)
|
||||
return;
|
||||
|
||||
|
@ -3747,6 +3887,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
|
|||
// - RenderArrow()
|
||||
// - RenderBullet()
|
||||
// - RenderCheckMark()
|
||||
// - RenderArrowDockMenu()
|
||||
// - RenderArrowPointingAt()
|
||||
// - RenderRectFilledRangeH()
|
||||
// - 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)
|
||||
{
|
||||
if (x <= 0.0f) return IM_PI * 0.5f;
|
||||
|
@ -3863,8 +4012,8 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im
|
|||
}
|
||||
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, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e, 3); // TR
|
||||
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); // TR
|
||||
}
|
||||
if (p1.x > rect.Min.x + rounding)
|
||||
{
|
||||
|
@ -3883,8 +4032,8 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im
|
|||
}
|
||||
else
|
||||
{
|
||||
draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b, 3); // TR
|
||||
draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e, 3); // BR
|
||||
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); // BR
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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()
|
||||
// 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.
|
||||
|
@ -4071,8 +4231,8 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i
|
|||
//-----------------------------------------------------------------------------
|
||||
// ProggyClean.ttf
|
||||
// Copyright (c) 2004, 2005 Tristan Grimmer
|
||||
// MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip)
|
||||
// Download and more information at http://upperbounds.net
|
||||
// MIT license (see License.txt in http://www.proggyfonts.net/index.php?menu=download)
|
||||
// Download and more information at http://www.proggyfonts.net or http://upperboundsinteractive.com/fonts.php
|
||||
//-----------------------------------------------------------------------------
|
||||
// File: 'ProggyClean.ttf' (41208 bytes)
|
||||
// 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
|
@ -1,6 +1,6 @@
|
|||
// 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
|
||||
// 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
|
||||
// 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.
|
||||
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/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
|
||||
|
@ -133,6 +136,11 @@ You can read releases logs https://github.com/epezent/implot/releases for more d
|
|||
#define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All
|
||||
#endif
|
||||
|
||||
// Support for pre-1.89.7 versions.
|
||||
#if (IMGUI_VERSION_NUM < 18966)
|
||||
#define ImGuiButtonFlags_AllowOverlap ImGuiButtonFlags_AllowItemOverlap
|
||||
#endif
|
||||
|
||||
// Visual Studio warnings
|
||||
#ifdef _MSC_VER
|
||||
#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[] =
|
||||
{
|
||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, LineWeight) }, // ImPlotStyleVar_LineWeight
|
||||
{ ImGuiDataType_S32, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker
|
||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerSize) }, // ImPlotStyleVar_MarkerSize
|
||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight
|
||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, FillAlpha) }, // ImPlotStyleVar_FillAlpha
|
||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize
|
||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarWeight) }, // ImPlotStyleVar_ErrorBarWeight
|
||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitHeight) }, // ImPlotStyleVar_DigitalBitHeight
|
||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitGap) }, // ImPlotStyleVar_DigitalBitGap
|
||||
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, LineWeight) }, // ImPlotStyleVar_LineWeight
|
||||
{ ImGuiDataType_S32, 1, (ImU32)offsetof(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker
|
||||
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MarkerSize) }, // ImPlotStyleVar_MarkerSize
|
||||
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight
|
||||
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, FillAlpha) }, // ImPlotStyleVar_FillAlpha
|
||||
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize
|
||||
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, ErrorBarWeight) }, // ImPlotStyleVar_ErrorBarWeight
|
||||
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, DigitalBitHeight) }, // ImPlotStyleVar_DigitalBitHeight
|
||||
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, DigitalBitGap) }, // ImPlotStyleVar_DigitalBitGap
|
||||
|
||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotBorderSize) }, // ImPlotStyleVar_PlotBorderSize
|
||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorAlpha) }, // ImPlotStyleVar_MinorAlpha
|
||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorTickLen) }, // ImPlotStyleVar_MajorTickLen
|
||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorTickLen) }, // ImPlotStyleVar_MinorTickLen
|
||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorTickSize) }, // ImPlotStyleVar_MajorTickSize
|
||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorTickSize) }, // ImPlotStyleVar_MinorTickSize
|
||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorGridSize) }, // ImPlotStyleVar_MajorGridSize
|
||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorGridSize) }, // ImPlotStyleVar_MinorGridSize
|
||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotPadding) }, // ImPlotStyleVar_PlotPadding
|
||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LabelPadding) }, // ImPlotStyleVar_LabelPaddine
|
||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendPadding) }, // ImPlotStyleVar_LegendPadding
|
||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendInnerPadding) }, // ImPlotStyleVar_LegendInnerPadding
|
||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendSpacing) }, // ImPlotStyleVar_LegendSpacing
|
||||
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, PlotBorderSize) }, // ImPlotStyleVar_PlotBorderSize
|
||||
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MinorAlpha) }, // ImPlotStyleVar_MinorAlpha
|
||||
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorTickLen) }, // ImPlotStyleVar_MajorTickLen
|
||||
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorTickLen) }, // ImPlotStyleVar_MinorTickLen
|
||||
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorTickSize) }, // ImPlotStyleVar_MajorTickSize
|
||||
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorTickSize) }, // ImPlotStyleVar_MinorTickSize
|
||||
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorGridSize) }, // ImPlotStyleVar_MajorGridSize
|
||||
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorGridSize) }, // ImPlotStyleVar_MinorGridSize
|
||||
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotPadding) }, // ImPlotStyleVar_PlotPadding
|
||||
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LabelPadding) }, // ImPlotStyleVar_LabelPaddine
|
||||
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendPadding) }, // ImPlotStyleVar_LegendPadding
|
||||
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendInnerPadding) }, // ImPlotStyleVar_LegendInnerPadding
|
||||
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendSpacing) }, // ImPlotStyleVar_LegendSpacing
|
||||
|
||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MousePosPadding) }, // ImPlotStyleVar_MousePosPadding
|
||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, AnnotationPadding) }, // ImPlotStyleVar_AnnotationPadding
|
||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, FitPadding) }, // ImPlotStyleVar_FitPadding
|
||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotDefaultSize) }, // ImPlotStyleVar_PlotDefaultSize
|
||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotMinSize) } // ImPlotStyleVar_PlotMinSize
|
||||
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MousePosPadding) }, // ImPlotStyleVar_MousePosPadding
|
||||
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, AnnotationPadding) }, // ImPlotStyleVar_AnnotationPadding
|
||||
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, FitPadding) }, // ImPlotStyleVar_FitPadding
|
||||
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotDefaultSize) }, // ImPlotStyleVar_PlotDefaultSize
|
||||
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotMinSize) } // ImPlotStyleVar_PlotMinSize
|
||||
};
|
||||
|
||||
static const ImPlotStyleVarInfo* GetPlotStyleVarInfo(ImPlotStyleVar idx) {
|
||||
|
@ -333,8 +341,8 @@ void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char *te
|
|||
ImGuiContext& g = *GImGui;
|
||||
ImFont* font = g.Font;
|
||||
// Align to be pixel perfect
|
||||
pos.x = IM_FLOOR(pos.x);
|
||||
pos.y = IM_FLOOR(pos.y);
|
||||
pos.x = ImFloor(pos.x);
|
||||
pos.y = ImFloor(pos.y);
|
||||
const float scale = g.FontSize / font->FontSize;
|
||||
const char* s = text_begin;
|
||||
int chars_exp = (int)(text_end - s);
|
||||
|
@ -485,10 +493,6 @@ void Initialize(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
|
||||
ctx->NextPlotData.Reset();
|
||||
ctx->NextItemData.Reset();
|
||||
|
@ -582,6 +586,28 @@ ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& s
|
|||
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) {
|
||||
ImPlotItemGroup* items = GImPlot->SortItems;
|
||||
const int a = *(const int*)_a;
|
||||
|
@ -1296,12 +1322,12 @@ bool DragFloat(const char*, F*, float, F, F) {
|
|||
|
||||
template <>
|
||||
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 <>
|
||||
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) {
|
||||
|
@ -1548,7 +1574,7 @@ void ShowPlotContextMenu(ImPlotPlot& plot) {
|
|||
ImFlipFlag(plot.Flags, ImPlotFlags_Crosshairs);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (gp.CurrentSubplot != nullptr && !ImHasFlag(gp.CurrentPlot->Flags, ImPlotSubplotFlags_NoMenus)) {
|
||||
if (gp.CurrentSubplot != nullptr && !ImHasFlag(gp.CurrentSubplot->Flags, ImPlotSubplotFlags_NoMenus)) {
|
||||
ImGui::Separator();
|
||||
if ((ImGui::BeginMenu("Subplots"))) {
|
||||
ShowSubplotsContextMenu(*gp.CurrentSubplot);
|
||||
|
@ -1808,7 +1834,7 @@ bool UpdateInput(ImPlotPlot& plot) {
|
|||
|
||||
// BUTTON STATE -----------------------------------------------------------
|
||||
|
||||
const ImGuiButtonFlags plot_button_flags = ImGuiButtonFlags_AllowItemOverlap
|
||||
const ImGuiButtonFlags plot_button_flags = ImGuiButtonFlags_AllowOverlap
|
||||
| ImGuiButtonFlags_PressedOnClick
|
||||
| ImGuiButtonFlags_PressedOnDoubleClick
|
||||
| ImGuiButtonFlags_MouseButtonLeft
|
||||
|
@ -1818,7 +1844,9 @@ bool UpdateInput(ImPlotPlot& plot) {
|
|||
| 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 (!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 -----------------------------------------------------------
|
||||
|
||||
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;
|
||||
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));
|
||||
ImVec2 rect_size = plot.PlotRect.GetSize();
|
||||
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_locked = (equal_zoom != false) && x_axis.OrthoAxis->IsInputLocked();
|
||||
if (x_hov[i] && !x_axis.IsInputLocked() && !equal_locked) {
|
||||
float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f;
|
||||
const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - rect_size.x * tx * zoom_rate * correction);
|
||||
const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x + rect_size.x * (1 - tx) * zoom_rate * correction);
|
||||
x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l);
|
||||
x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r);
|
||||
if (axis_equal && x_axis.OrthoAxis != nullptr)
|
||||
x_axis.OrthoAxis->SetAspect(x_axis.GetAspect());
|
||||
changed = true;
|
||||
ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID);
|
||||
if (zoom_rate != 0.0f) {
|
||||
float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f;
|
||||
const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - rect_size.x * tx * zoom_rate * correction);
|
||||
const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x + rect_size.x * (1 - tx) * zoom_rate * correction);
|
||||
x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l);
|
||||
x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r);
|
||||
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++) {
|
||||
|
@ -1980,14 +2013,17 @@ bool UpdateInput(ImPlotPlot& plot) {
|
|||
const bool equal_zoom = axis_equal && y_axis.OrthoAxis != nullptr;
|
||||
const bool equal_locked = equal_zoom && y_axis.OrthoAxis->IsInputLocked();
|
||||
if (y_hov[i] && !y_axis.IsInputLocked() && !equal_locked) {
|
||||
float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f;
|
||||
const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - rect_size.y * ty * zoom_rate * correction);
|
||||
const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y + rect_size.y * (1 - ty) * zoom_rate * correction);
|
||||
y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b);
|
||||
y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t);
|
||||
if (axis_equal && y_axis.OrthoAxis != nullptr)
|
||||
y_axis.OrthoAxis->SetAspect(y_axis.GetAspect());
|
||||
changed = true;
|
||||
ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID);
|
||||
if (zoom_rate != 0.0f) {
|
||||
float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f;
|
||||
const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - rect_size.y * ty * zoom_rate * correction);
|
||||
const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y + rect_size.y * (1 - ty) * zoom_rate * correction);
|
||||
y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b);
|
||||
y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t);
|
||||
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) {
|
||||
ImPlotContext& gp = *GImPlot;
|
||||
IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,
|
||||
"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!");
|
||||
IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr,
|
||||
"SetupLegend() needs to be called within an itemized context!");
|
||||
ImPlotLegend& legend = gp.CurrentItems->Legend;
|
||||
// check and set location
|
||||
if (location != legend.PreviousLocation)
|
||||
legend.Location = location;
|
||||
legend.PreviousLocation = location;
|
||||
// check and set flags
|
||||
if (flags != legend.PreviousFlags)
|
||||
legend.Flags = flags;
|
||||
legend.PreviousFlags = flags;
|
||||
IM_ASSERT_USER_ERROR((gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked) || (gp.CurrentSubplot != nullptr && gp.CurrentPlot == nullptr),
|
||||
"Setup needs to be called after BeginPlot or BeginSubplots and before any setup locking functions (e.g. PlotX)!");
|
||||
if (gp.CurrentItems) {
|
||||
ImPlotLegend& legend = gp.CurrentItems->Legend;
|
||||
// check and set location
|
||||
if (location != legend.PreviousLocation)
|
||||
legend.Location = location;
|
||||
legend.PreviousLocation = location;
|
||||
// check and set flags
|
||||
if (flags != legend.PreviousFlags)
|
||||
legend.Flags = flags;
|
||||
legend.PreviousFlags = 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)
|
||||
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
|
||||
plot.ClearTextBuffer();
|
||||
plot.SetTitle(title_id);
|
||||
|
@ -2550,7 +2570,7 @@ void SetupFinish() {
|
|||
// (2) get y tick labels (needed for left/right pad)
|
||||
for (int i = 0; i < IMPLOT_NUM_Y_AXES; 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);
|
||||
}
|
||||
}
|
||||
|
@ -2563,7 +2583,7 @@ void SetupFinish() {
|
|||
// (4) get x ticks
|
||||
for (int i = 0; i < IMPLOT_NUM_X_AXES; 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);
|
||||
}
|
||||
}
|
||||
|
@ -2758,7 +2778,7 @@ void EndPlot() {
|
|||
|
||||
// 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_y_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES);
|
||||
|
||||
|
@ -3023,24 +3043,57 @@ void EndPlot() {
|
|||
legend.Location,
|
||||
legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding);
|
||||
legend.Rect = ImRect(legend_pos, legend_pos + legend_size);
|
||||
// test hover
|
||||
legend.Hovered = ImGui::IsWindowHovered() && legend.Rect.Contains(IO.MousePos);
|
||||
legend.RectClamped = legend.Rect;
|
||||
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)
|
||||
ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true);
|
||||
else
|
||||
PushPlotClipRect();
|
||||
ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg);
|
||||
ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder);
|
||||
DrawList.AddRectFilled(legend.Rect.Min, legend.Rect.Max, col_bg);
|
||||
DrawList.AddRect(legend.Rect.Min, legend.Rect.Max, col_bd);
|
||||
if (legend_scrollable) {
|
||||
if (legend.Hovered) {
|
||||
ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.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(plot.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();
|
||||
|
||||
// main ctx menu
|
||||
if (gp.OpenContextThisFrame && legend_contextable && !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus))
|
||||
ImGui::OpenPopup("##LegendContext");
|
||||
ImGui::PopClipRect();
|
||||
|
||||
if (ImGui::BeginPopup("##LegendContext")) {
|
||||
ImGui::Text("Legend"); ImGui::Separator();
|
||||
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.GridRect.Min = subplot.FrameRect.Min + half_pad + ImVec2(0,pad_top);
|
||||
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)
|
||||
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);
|
||||
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;
|
||||
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_clk && ImGui::IsMouseDoubleClicked(0)) {
|
||||
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);
|
||||
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;
|
||||
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_clk && ImGui::IsMouseDoubleClicked(0)) {
|
||||
float p = (subplot.ColRatios[c] + subplot.ColRatios[c+1])/2;
|
||||
|
@ -3488,6 +3541,7 @@ void EndSubplots() {
|
|||
ImPlotContext& gp = *GImPlot;
|
||||
IM_ASSERT_USER_ERROR(gp.CurrentSubplot != nullptr, "Mismatched BeginSubplots()/EndSubplots()!");
|
||||
ImPlotSubplot& subplot = *gp.CurrentSubplot;
|
||||
const ImGuiIO& IO = ImGui::GetIO();
|
||||
// set alignments
|
||||
for (int r = 0; r < subplot.Rows; ++r)
|
||||
subplot.RowAlignmentData[r].End();
|
||||
|
@ -3506,24 +3560,60 @@ void EndSubplots() {
|
|||
const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems);
|
||||
ImDrawList& DrawList = *ImGui::GetWindowDrawList();
|
||||
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_pos = GetLocationPos(subplot.FrameRect, legend_size, subplot.Items.Legend.Location, gp.Style.PlotPadding);
|
||||
subplot.Items.Legend.Rect = ImRect(legend_pos, legend_pos + legend_size);
|
||||
subplot.Items.Legend.Hovered = subplot.FrameHovered && subplot.Items.Legend.Rect.Contains(ImGui::GetIO().MousePos);
|
||||
ImGui::PushClipRect(subplot.FrameRect.Min, subplot.FrameRect.Max, true);
|
||||
ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg);
|
||||
ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder);
|
||||
DrawList.AddRectFilled(subplot.Items.Legend.Rect.Min, subplot.Items.Legend.Rect.Max, col_bg);
|
||||
DrawList.AddRect(subplot.Items.Legend.Rect.Min, subplot.Items.Legend.Rect.Max, col_bd);
|
||||
bool legend_contextable = ShowLegendEntries(subplot.Items, subplot.Items.Legend.Rect, subplot.Items.Legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList)
|
||||
&& !ImHasFlag(subplot.Items.Legend.Flags, ImPlotLegendFlags_NoMenus);
|
||||
const ImVec2 legend_pos = GetLocationPos(subplot.FrameRect, legend_size, legend.Location, gp.Style.PlotPadding);
|
||||
legend.Rect = ImRect(legend_pos, legend_pos + legend_size);
|
||||
legend.RectClamped = legend.Rect;
|
||||
const bool legend_scrollable = ClampLegendRect(legend.RectClamped,subplot.FrameRect, gp.Style.PlotPadding);
|
||||
const ImGuiButtonFlags legend_button_flags = ImGuiButtonFlags_AllowOverlap
|
||||
| ImGuiButtonFlags_PressedOnClick
|
||||
| ImGuiButtonFlags_PressedOnDoubleClick
|
||||
| ImGuiButtonFlags_MouseButtonLeft
|
||||
| ImGuiButtonFlags_MouseButtonRight
|
||||
| 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])
|
||||
ImGui::OpenPopup("##LegendContext");
|
||||
ImGui::PopClipRect();
|
||||
if (ImGui::BeginPopup("##LegendContext")) {
|
||||
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);
|
||||
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;
|
||||
|
||||
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");
|
||||
IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragPoint() needs to be called between BeginPlot() and EndPlot()!");
|
||||
SetupLock();
|
||||
|
@ -3826,30 +3916,34 @@ bool DragPoint(int n_id, double* x, double* y, const ImVec4& col, float radius,
|
|||
bool hovered = false, held = false;
|
||||
|
||||
ImGui::KeepAliveID(id);
|
||||
if (input)
|
||||
ImGui::ButtonBehavior(rect,id,&hovered,&held);
|
||||
if (input) {
|
||||
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)) {
|
||||
*x = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
|
||||
*y = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
|
||||
dragging = true;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
PushPlotClipRect();
|
||||
ImDrawList& DrawList = *GetPlotDrawList();
|
||||
if ((hovered || held) && show_curs)
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
if (dragging && no_delay)
|
||||
if (modified && no_delay)
|
||||
pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO);
|
||||
DrawList.AddCircleFilled(pos, radius, col32);
|
||||
PopPlotClipRect();
|
||||
|
||||
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");
|
||||
ImPlotContext& gp = *GImPlot;
|
||||
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;
|
||||
|
||||
ImGui::KeepAliveID(id);
|
||||
if (input)
|
||||
ImGui::ButtonBehavior(rect,id,&hovered,&held);
|
||||
if (input) {
|
||||
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)
|
||||
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;
|
||||
ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color);
|
||||
|
||||
bool dragging = false;
|
||||
bool modified = false;
|
||||
if (held && ImGui::IsMouseDragging(0)) {
|
||||
*value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
|
||||
dragging = true;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
PushPlotClipRect();
|
||||
ImDrawList& DrawList = *GetPlotDrawList();
|
||||
if (dragging && no_delay)
|
||||
if (modified && no_delay)
|
||||
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,yt+len), col32, 3*thickness);
|
||||
|
@ -3897,10 +3995,10 @@ bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPl
|
|||
PopPlotClipRect();
|
||||
|
||||
// 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");
|
||||
ImPlotContext& gp = *GImPlot;
|
||||
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;
|
||||
|
||||
ImGui::KeepAliveID(id);
|
||||
if (input)
|
||||
ImGui::ButtonBehavior(rect,id,&hovered,&held);
|
||||
if (input) {
|
||||
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)
|
||||
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;
|
||||
ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color);
|
||||
|
||||
bool dragging = false;
|
||||
bool modified = false;
|
||||
if (held && ImGui::IsMouseDragging(0)) {
|
||||
*value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
|
||||
dragging = true;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
PushPlotClipRect();
|
||||
ImDrawList& DrawList = *GetPlotDrawList();
|
||||
if (dragging && no_delay)
|
||||
if (modified && no_delay)
|
||||
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(xl+len,y), col32, 3*thickness);
|
||||
|
@ -3949,10 +4051,10 @@ bool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPl
|
|||
PopPlotClipRect();
|
||||
|
||||
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");
|
||||
IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragRect() needs to be called between BeginPlot() and EndPlot()!");
|
||||
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);
|
||||
const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id);
|
||||
|
||||
bool dragging = false;
|
||||
bool hovered = false, held = false;
|
||||
bool modified = 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);
|
||||
|
||||
ImGui::KeepAliveID(id);
|
||||
if (input)
|
||||
ImGui::ButtonBehavior(b_rect,id,&hovered,&held);
|
||||
if (input) {
|
||||
// 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)
|
||||
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;
|
||||
*x[i] = pp.x;
|
||||
}
|
||||
dragging = true;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
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);
|
||||
ImGuiID p_id = id + i + 1;
|
||||
ImGui::KeepAliveID(p_id);
|
||||
if (input)
|
||||
ImGui::ButtonBehavior(b_rect,p_id,&hovered,&held);
|
||||
if (input) {
|
||||
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)
|
||||
ImGui::SetMouseCursor(cur[i]);
|
||||
|
||||
if (held && ImGui::IsMouseDragging(0)) {
|
||||
*x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
|
||||
*y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
|
||||
dragging = true;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// 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);
|
||||
ImGuiID e_id = id + i + 5;
|
||||
ImGui::KeepAliveID(e_id);
|
||||
if (input)
|
||||
ImGui::ButtonBehavior(b_rect,e_id,&hovered,&held);
|
||||
if (input) {
|
||||
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)
|
||||
h[i] ? ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
|
||||
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;
|
||||
else
|
||||
*x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
|
||||
dragging = true;
|
||||
modified = true;
|
||||
}
|
||||
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;
|
||||
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;
|
||||
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();
|
||||
ImDrawList& DrawList = *GetPlotDrawList();
|
||||
if (dragging && no_delay) {
|
||||
if (modified && no_delay) {
|
||||
for (int i = 0; i < 4; ++i)
|
||||
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);
|
||||
|
@ -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.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);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
DrawList.AddCircleFilled(p[i],DRAG_GRAB_HALF_SIZE,col32);
|
||||
}
|
||||
PopPlotClipRect();
|
||||
ImGui::PopID();
|
||||
return dragging;
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool DragRect(int id, ImPlotRect* bounds, const ImVec4& col, ImPlotDragToolFlags flags) {
|
||||
return DragRect(id, &bounds->X.Min, &bounds->Y.Min,&bounds->X.Max, &bounds->Y.Max, col, 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, out_clicked, out_hovered, out_held);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -4171,7 +4294,7 @@ bool BeginDragDropTargetAxis(ImAxis axis) {
|
|||
bool BeginDragDropTargetLegend() {
|
||||
SetupLock();
|
||||
ImPlotItemGroup& items = *GImPlot->CurrentItems;
|
||||
ImRect rect = items.Legend.Rect;
|
||||
ImRect rect = items.Legend.RectClamped;
|
||||
return ImGui::BeginDragDropTargetCustom(rect, items.ID);
|
||||
}
|
||||
|
||||
|
@ -5110,6 +5233,7 @@ void ShowMetricsWindow(bool* p_popen) {
|
|||
static bool show_frame_rects = false;
|
||||
static bool show_subplot_frame_rects = false;
|
||||
static bool show_subplot_grid_rects = false;
|
||||
static bool show_legend_rects = false;
|
||||
|
||||
ImDrawList& fg = *ImGui::GetForegroundDrawList();
|
||||
|
||||
|
@ -5134,6 +5258,7 @@ void ShowMetricsWindow(bool* p_popen) {
|
|||
ImGui::Checkbox("Show Axis Rects", &show_axis_rects);
|
||||
ImGui::Checkbox("Show Subplot Frame Rects", &show_subplot_frame_rects);
|
||||
ImGui::Checkbox("Show Subplot Grid Rects", &show_subplot_grid_rects);
|
||||
ImGui::Checkbox("Show Legend Rects", &show_legend_rects);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
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) {
|
||||
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));
|
||||
if (show_subplot_grid_rects)
|
||||
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)) {
|
||||
for (int p = 0; p < n_plots; ++p) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// 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
|
||||
// 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
|
||||
// SOFTWARE.
|
||||
|
||||
// ImPlot v0.14
|
||||
// ImPlot v0.17
|
||||
|
||||
// Table of Contents:
|
||||
//
|
||||
|
@ -60,7 +60,7 @@
|
|||
#endif
|
||||
|
||||
// ImPlot version string.
|
||||
#define IMPLOT_VERSION "0.14"
|
||||
#define IMPLOT_VERSION "0.17"
|
||||
// Indicates variable should deduced automatically.
|
||||
#define IMPLOT_AUTO -1
|
||||
// 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_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_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 << 7, // the ImGui frame will not be rendered
|
||||
ImPlotFlags_Equal = 1 << 8, // x and y axes pairs will be constrained to have the same units/pixel
|
||||
ImPlotFlags_Crosshairs = 1 << 9, // the default mouse cursor will be replaced with a crosshair when hovered
|
||||
ImPlotFlags_NoFrame = 1 << 6, // 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_Crosshairs = 1 << 8, // the default mouse cursor will be replaced with a crosshair when hovered
|
||||
ImPlotFlags_CanvasOnly = ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoMouseText
|
||||
};
|
||||
|
||||
|
@ -287,8 +286,9 @@ enum ImPlotInfLinesFlags_ {
|
|||
|
||||
// Flags for PlotPieChart
|
||||
enum ImPlotPieChartFlags_ {
|
||||
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_None = 0, // default
|
||||
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
|
||||
|
@ -464,41 +464,43 @@ enum ImPlotBin_ {
|
|||
};
|
||||
|
||||
// Double precision version of ImVec2 used by ImPlot. Extensible by end users.
|
||||
IM_MSVC_RUNTIME_CHECKS_OFF
|
||||
struct ImPlotPoint {
|
||||
double x, y;
|
||||
ImPlotPoint() { x = y = 0.0; }
|
||||
ImPlotPoint(double _x, double _y) { x = _x; y = _y; }
|
||||
ImPlotPoint(const ImVec2& p) { x = p.x; y = p.y; }
|
||||
double operator[] (size_t idx) const { return (&x)[idx]; }
|
||||
double& operator[] (size_t idx) { return (&x)[idx]; }
|
||||
constexpr ImPlotPoint() : x(0.0), y(0.0) { }
|
||||
constexpr ImPlotPoint(double _x, double _y) : x(_x), y(_y) { }
|
||||
constexpr ImPlotPoint(const ImVec2& p) : x((double)p.x), y((double)p.y) { }
|
||||
double& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1); return ((double*)(void*)(char*)this)[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
|
||||
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.
|
||||
#endif
|
||||
};
|
||||
IM_MSVC_RUNTIME_CHECKS_RESTORE
|
||||
|
||||
// Range defined by a min/max value.
|
||||
struct ImPlotRange {
|
||||
double Min, Max;
|
||||
ImPlotRange() { Min = 0; Max = 0; }
|
||||
ImPlotRange(double _min, double _max) { Min = _min; Max = _max; }
|
||||
bool Contains(double value) const { return value >= Min && value <= Max; }
|
||||
double Size() const { return Max - Min; }
|
||||
double Clamp(double value) const { return (value < Min) ? Min : (value > Max) ? Max : value; }
|
||||
constexpr ImPlotRange() : Min(0.0), Max(0.0) { }
|
||||
constexpr ImPlotRange(double _min, double _max) : Min(_min), Max(_max) { }
|
||||
bool Contains(double value) const { return value >= Min && value <= Max; }
|
||||
double Size() const { return Max - Min; }
|
||||
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().
|
||||
struct ImPlotRect {
|
||||
ImPlotRange X, Y;
|
||||
ImPlotRect() { }
|
||||
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; }
|
||||
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); }
|
||||
ImPlotPoint Size() const { return ImPlotPoint(X.Size(), Y.Size()); }
|
||||
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 Min() const { return ImPlotPoint(X.Min, Y.Min); }
|
||||
ImPlotPoint Max() const { return ImPlotPoint(X.Max, Y.Max); }
|
||||
constexpr ImPlotRect() : X(0.0,0.0), Y(0.0,0.0) { }
|
||||
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(double x, double y) const { return X.Contains(x) && Y.Contains(y); }
|
||||
ImPlotPoint Size() const { return ImPlotPoint(X.Size(), Y.Size()); }
|
||||
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 Min() const { return ImPlotPoint(X.Min, Y.Min); }
|
||||
ImPlotPoint Max() const { return ImPlotPoint(X.Max, Y.Max); }
|
||||
};
|
||||
|
||||
// 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.
|
||||
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);
|
||||
// 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);
|
||||
|
@ -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).
|
||||
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);
|
||||
// Set the location of the current plot's mouse position text (default = South|East).
|
||||
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));
|
||||
|
||||
// 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);
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
IMPLOT_API void Annotation(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, bool round = false);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// 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
|
||||
// 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
|
||||
// SOFTWARE.
|
||||
|
||||
// ImPlot v0.14
|
||||
// ImPlot v0.17
|
||||
|
||||
// We define this so that the demo does not accidentally use deprecated API
|
||||
#ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS
|
||||
|
@ -610,10 +610,8 @@ void Demo_PieCharts() {
|
|||
static ImPlotPieChartFlags flags = 0;
|
||||
ImGui::SetNextItemWidth(250);
|
||||
ImGui::DragFloat4("Values", data1, 0.01f, 0, 1);
|
||||
if ((data1[0] + data1[1] + data1[2] + data1[3]) < 1) {
|
||||
ImGui::SameLine();
|
||||
CHECKBOX_FLAG(flags,ImPlotPieChartFlags_Normalize);
|
||||
}
|
||||
CHECKBOX_FLAG(flags, ImPlotPieChartFlags_Normalize);
|
||||
CHECKBOX_FLAG(flags, ImPlotPieChartFlags_IgnoreHidden);
|
||||
|
||||
if (ImPlot::BeginPlot("##Pie1", ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) {
|
||||
ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations);
|
||||
|
@ -1147,7 +1145,7 @@ void Demo_MultipleAxes() {
|
|||
ImPlot::SetAxes(ImAxis_X1, ImAxis_Y2);
|
||||
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::PlotLine("f(x) = sin(x+.5)*100+200 ", xs2, ys3, 1001);
|
||||
}
|
||||
|
@ -1264,7 +1262,7 @@ ImPlotPoint SinewaveGetter(int i, void* data) {
|
|||
|
||||
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_NoTitle", (unsigned int*)&flags, ImPlotSubplotFlags_NoTitle);
|
||||
|
||||
|
@ -1272,17 +1270,26 @@ void Demo_SubplotsSizing() {
|
|||
static int cols = 3;
|
||||
ImGui::SliderInt("Rows",&rows,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 cratios[] = {5,1,1,1,1,1};
|
||||
ImGui::DragScalarN("Row Ratios",ImGuiDataType_Float,rratios,rows,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)) {
|
||||
int id = 0;
|
||||
for (int i = 0; i < rows*cols; ++i) {
|
||||
if (ImPlot::BeginPlot("",ImVec2(),ImPlotFlags_NoLegend)) {
|
||||
ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations);
|
||||
float fi = 0.01f * (i+1);
|
||||
ImPlot::SetNextLineStyle(SampleColormap((float)i/(float)(rows*cols-1),ImPlotColormap_Jet));
|
||||
ImPlot::PlotLineG("data",SinewaveGetter,&fi,1000);
|
||||
if (rows*cols > 1) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1302,6 +1309,7 @@ void Demo_SubplotItemSharing() {
|
|||
static int id[] = {0,1,2,3,4,5};
|
||||
static int curj = -1;
|
||||
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) {
|
||||
if (ImPlot::BeginPlot("")) {
|
||||
float fc = 0.01f;
|
||||
|
@ -1376,6 +1384,9 @@ void Demo_LegendOptions() {
|
|||
ImGui::SliderFloat2("LegendInnerPadding", (float*)&GetStyle().LegendInnerPadding, 0.0f, 10.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))) {
|
||||
ImPlot::SetupLegend(loc, flags);
|
||||
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 data5(0.001, 0.2, 4, 1.0);
|
||||
|
||||
ImPlot::PlotLineG("Item B", 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 002", MyImPlot::SawWave, &data1, 1000); // "Item B" added to legend
|
||||
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("Item C", 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, &data4, 1000); // "Item C" added to legend
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1403,15 +1419,18 @@ void Demo_DragPoints() {
|
|||
ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine();
|
||||
ImGui::CheckboxFlags("NoInput", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs);
|
||||
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)) {
|
||||
ImPlot::SetupAxes(nullptr,nullptr,ax_flags,ax_flags);
|
||||
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)};
|
||||
|
||||
ImPlot::DragPoint(0,&P[0].x,&P[0].y, ImVec4(0,0.9f,0,1),4,flags);
|
||||
ImPlot::DragPoint(1,&P[1].x,&P[1].y, ImVec4(1,0.5f,1,1),4,flags);
|
||||
ImPlot::DragPoint(2,&P[2].x,&P[2].y, ImVec4(0,0.5f,1,1),4,flags);
|
||||
ImPlot::DragPoint(3,&P[3].x,&P[3].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, &clicked[1], &hovered[1], &held[1]);
|
||||
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, &clicked[3], &hovered[3], &held[3]);
|
||||
|
||||
static ImPlotPoint B[100];
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
ImPlot::SetNextLineStyle(ImVec4(1,0.5f,1,1));
|
||||
ImPlot::SetNextLineStyle(ImVec4(1,0.5f,1,1),hovered[1]||held[1] ? 2.0f : 1.0f);
|
||||
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::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::EndPlot();
|
||||
}
|
||||
}
|
||||
|
@ -1445,6 +1462,9 @@ void Demo_DragLines() {
|
|||
static double y1 = 0.25;
|
||||
static double y2 = 0.75;
|
||||
static double f = 0.1;
|
||||
bool clicked = false;
|
||||
bool hovered = false;
|
||||
bool held = false;
|
||||
static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None;
|
||||
ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); 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);
|
||||
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::DragLineY(120482,&f,ImVec4(1,0.5f,1,1),1,flags);
|
||||
ImPlot::EndPlot();
|
||||
}
|
||||
}
|
||||
|
@ -1476,6 +1497,9 @@ void Demo_DragRects() {
|
|||
static float y_data3[512];
|
||||
static float sampling_freq = 44100;
|
||||
static float freq = 500;
|
||||
bool clicked = false;
|
||||
bool hovered = false;
|
||||
bool held = false;
|
||||
for (size_t i = 0; i < 512; ++i) {
|
||||
const float t = i / sampling_freq;
|
||||
x_data[i] = t;
|
||||
|
@ -1485,6 +1509,7 @@ void Demo_DragRects() {
|
|||
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("Double click edges to expand rect to plot extents.");
|
||||
static ImPlotRect rect(0.0025,0.0045,0,0.5);
|
||||
static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None;
|
||||
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 2", x_data, y_data2, 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();
|
||||
}
|
||||
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)) {
|
||||
ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations);
|
||||
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::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) {
|
||||
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::SetupAxesLimits(0, count - 1, min_v, max_v, ImGuiCond_Always);
|
||||
ImPlot::SetNextLineStyle(col);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// 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
|
||||
// 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
|
||||
// SOFTWARE.
|
||||
|
||||
// ImPlot v0.14
|
||||
// ImPlot v0.17
|
||||
|
||||
// You may use this file to debug, understand or extend ImPlot features but we
|
||||
// don't provide any guarantee of forward compatibility!
|
||||
|
@ -907,8 +907,9 @@ struct ImPlotAxis
|
|||
}
|
||||
|
||||
void PullLinks() {
|
||||
if (LinkedMin) { SetMin(*LinkedMin,true); }
|
||||
if (LinkedMax) { SetMax(*LinkedMax,true); }
|
||||
if (LinkedMin && LinkedMax) { SetRange(*LinkedMin, *LinkedMax); }
|
||||
else if (LinkedMin) { SetMin(*LinkedMin,true); }
|
||||
else if (LinkedMax) { SetMax(*LinkedMax,true); }
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -965,9 +966,11 @@ struct ImPlotLegend
|
|||
ImPlotLegendFlags PreviousFlags;
|
||||
ImPlotLocation Location;
|
||||
ImPlotLocation PreviousLocation;
|
||||
ImVec2 Scroll;
|
||||
ImVector<int> Indices;
|
||||
ImGuiTextBuffer Labels;
|
||||
ImRect Rect;
|
||||
ImRect RectClamped;
|
||||
bool Hovered;
|
||||
bool Held;
|
||||
bool CanGoInside;
|
||||
|
@ -977,6 +980,7 @@ struct ImPlotLegend
|
|||
CanGoInside = true;
|
||||
Hovered = Held = false;
|
||||
Location = PreviousLocation = ImPlotLocation_NorthWest;
|
||||
Scroll = ImVec2(0,0);
|
||||
}
|
||||
|
||||
void Reset() { Indices.shrink(0); Labels.Buf.shrink(0); }
|
||||
|
@ -1136,7 +1140,6 @@ struct ImPlotSubplot {
|
|||
ID = 0;
|
||||
Flags = PreviousFlags = ImPlotSubplotFlags_None;
|
||||
Rows = Cols = CurrentIdx = 0;
|
||||
FrameHovered = false;
|
||||
Items.Legend.Location = ImPlotLocation_North;
|
||||
Items.Legend.Flags = ImPlotLegendFlags_Horizontal|ImPlotLegendFlags_Outside;
|
||||
Items.Legend.CanGoInside = false;
|
||||
|
@ -1215,9 +1218,6 @@ struct ImPlotContext {
|
|||
ImPlotAnnotationCollection Annotations;
|
||||
ImPlotTagCollection Tags;
|
||||
|
||||
// Flags
|
||||
bool ChildWindowMade;
|
||||
|
||||
// Style and Colormaps
|
||||
ImPlotStyle Style;
|
||||
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.
|
||||
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);
|
||||
// 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
|
||||
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);
|
||||
// Shows an legends's context menu.
|
||||
// Shows a legend's context menu.
|
||||
IMPLOT_API bool ShowLegendContextMenu(ImPlotLegend& legend, bool visible);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// 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
|
||||
// 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
|
||||
// SOFTWARE.
|
||||
|
||||
// ImPlot v0.14
|
||||
// ImPlot v0.17
|
||||
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#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) x ## y
|
||||
#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_2(T) INSTANTIATE_MACRO(T); _INSTANTIATE_FOR_NUMERIC_TYPES_1
|
||||
#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_1_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 {
|
||||
|
||||
|
@ -1287,7 +1287,7 @@ struct RendererShaded : RendererBase {
|
|||
return false;
|
||||
}
|
||||
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].uv = UV;
|
||||
draw_list._VtxWritePtr[0].col = Col;
|
||||
|
@ -1569,6 +1569,10 @@ void RenderMarkers(const _Getter& getter, ImPlotMarker marker, float size, bool
|
|||
template <typename _Getter>
|
||||
void PlotLineEx(const char* label_id, const _Getter& getter, ImPlotLineFlags flags) {
|
||||
if (BeginItemEx(label_id, Fitter1<_Getter>(getter), flags, ImPlotCol_Line)) {
|
||||
if (getter.Count <= 0) {
|
||||
EndItem();
|
||||
return;
|
||||
}
|
||||
const ImPlotNextItemData& s = GetItemData();
|
||||
if (getter.Count > 1) {
|
||||
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>
|
||||
void PlotScatterEx(const char* label_id, const Getter& getter, ImPlotScatterFlags flags) {
|
||||
if (BeginItemEx(label_id, Fitter1<Getter>(getter), flags, ImPlotCol_MarkerOutline)) {
|
||||
if (getter.Count <= 0) {
|
||||
EndItem();
|
||||
return;
|
||||
}
|
||||
const ImPlotNextItemData& s = GetItemData();
|
||||
ImPlotMarker marker = s.Marker == ImPlotMarker_None ? ImPlotMarker_Circle: s.Marker;
|
||||
if (marker != ImPlotMarker_None) {
|
||||
|
@ -1686,8 +1694,12 @@ void PlotScatterG(const char* label_id, ImPlotGetter getter_func, void* data, in
|
|||
template <typename Getter>
|
||||
void PlotStairsEx(const char* label_id, const Getter& getter, ImPlotStairsFlags flags) {
|
||||
if (BeginItemEx(label_id, Fitter1<Getter>(getter), flags, ImPlotCol_Line)) {
|
||||
if (getter.Count <= 0) {
|
||||
EndItem();
|
||||
return;
|
||||
}
|
||||
const ImPlotNextItemData& s = GetItemData();
|
||||
if (getter.Count > 1 ) {
|
||||
if (getter.Count > 1) {
|
||||
if (s.RenderFill && ImHasFlag(flags,ImPlotStairsFlags_Shaded)) {
|
||||
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);
|
||||
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>
|
||||
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 (getter1.Count <= 0 || getter2.Count <= 0) {
|
||||
EndItem();
|
||||
return;
|
||||
}
|
||||
const ImPlotNextItemData& s = GetItemData();
|
||||
if (s.RenderFill) {
|
||||
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>
|
||||
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 (getter1.Count <= 0 || getter2.Count <= 0) {
|
||||
EndItem();
|
||||
return;
|
||||
}
|
||||
const ImPlotNextItemData& s = GetItemData();
|
||||
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);
|
||||
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>
|
||||
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 (getter1.Count <= 0 || getter2.Count <= 0) {
|
||||
EndItem();
|
||||
return;
|
||||
}
|
||||
const ImPlotNextItemData& s = GetItemData();
|
||||
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);
|
||||
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
|
||||
|
@ -1982,6 +2006,10 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
|
|||
template <typename _GetterPos, typename _GetterNeg>
|
||||
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 (getter_pos.Count <= 0 || getter_neg.Count <= 0) {
|
||||
EndItem();
|
||||
return;
|
||||
}
|
||||
const ImPlotNextItemData& s = GetItemData();
|
||||
ImDrawList& draw_list = *GetPlotDrawList();
|
||||
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>
|
||||
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 (getter_pos.Count <= 0 || getter_neg.Count <= 0) {
|
||||
EndItem();
|
||||
return;
|
||||
}
|
||||
const ImPlotNextItemData& s = GetItemData();
|
||||
ImDrawList& draw_list = *GetPlotDrawList();
|
||||
const ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_ErrorBar]);
|
||||
|
@ -2060,13 +2092,17 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
|
|||
//-----------------------------------------------------------------------------
|
||||
|
||||
template <typename _GetterM, typename _GetterB>
|
||||
void PlotStemsEx(const char* label_id, const _GetterM& get_mark, const _GetterB& get_base, ImPlotStemsFlags flags) {
|
||||
if (BeginItemEx(label_id, Fitter2<_GetterM,_GetterB>(get_mark,get_base), flags, ImPlotCol_Line)) {
|
||||
void PlotStemsEx(const char* label_id, const _GetterM& getter_mark, const _GetterB& getter_base, ImPlotStemsFlags flags) {
|
||||
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();
|
||||
// render stems
|
||||
if (s.RenderLine) {
|
||||
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
|
||||
if (s.Marker != ImPlotMarker_None) {
|
||||
|
@ -2074,7 +2110,7 @@ void PlotStemsEx(const char* label_id, const _GetterM& get_mark, const _GetterB&
|
|||
PushPlotClipRect(s.MarkerSize);
|
||||
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]);
|
||||
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();
|
||||
}
|
||||
|
@ -2123,13 +2159,17 @@ template <typename T>
|
|||
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);
|
||||
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>> get_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)) {
|
||||
GetterXY<IndexerConst,IndexerIdx<T>> getter_min(IndexerConst(lims.X.Min),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>>>(getter_min), flags, ImPlotCol_Line)) {
|
||||
if (count <= 0) {
|
||||
EndItem();
|
||||
return;
|
||||
}
|
||||
const ImPlotNextItemData& s = GetItemData();
|
||||
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
|
||||
if (s.RenderLine)
|
||||
RenderPrimitives2<RendererLineSegments2>(get_min, get_max, col_line, s.LineWeight);
|
||||
RenderPrimitives2<RendererLineSegments2>(getter_min, getter_max, col_line, s.LineWeight);
|
||||
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_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 (count <= 0) {
|
||||
EndItem();
|
||||
return;
|
||||
}
|
||||
const ImPlotNextItemData& s = GetItemData();
|
||||
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
|
||||
if (s.RenderLine)
|
||||
|
@ -2172,57 +2216,121 @@ IMPLOT_INLINE void RenderPieSlice(ImDrawList& draw_list, const ImPlotPoint& cent
|
|||
}
|
||||
|
||||
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) {
|
||||
IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "PlotPieChart() needs to be called between BeginPlot() and EndPlot()!");
|
||||
ImDrawList & draw_list = *GetPlotDrawList();
|
||||
double PieChartSum(const T* values, int count, bool ignore_hidden) {
|
||||
double sum = 0;
|
||||
for (int i = 0; i < count; ++i)
|
||||
sum += (double)values[i];
|
||||
const bool normalize = ImHasFlag(flags,ImPlotPieChartFlags_Normalize) || sum > 1.0;
|
||||
ImPlotPoint center(x,y);
|
||||
PushPlotClipRect();
|
||||
if (ignore_hidden) {
|
||||
ImPlotContext& gp = *GImPlot;
|
||||
ImPlotItemGroup& Items = *gp.CurrentItems;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
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 a1 = angle0 * 2 * IM_PI / 360.0;
|
||||
ImPlotPoint Pmin = ImPlotPoint(x-radius,y-radius);
|
||||
ImPlotPoint Pmax = ImPlotPoint(x+radius,y+radius);
|
||||
ImPlotPoint Pmin = ImPlotPoint(center.x - radius, center.y - radius);
|
||||
ImPlotPoint Pmax = ImPlotPoint(center.x + radius, center.y + radius);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
double percent = normalize ? (double)values[i] / sum : (double)values[i];
|
||||
a1 = a0 + 2 * IM_PI * percent;
|
||||
if (BeginItemEx(label_ids[i], FitterRect(Pmin,Pmax))) {
|
||||
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);
|
||||
ImPlotItem* item = GetItem(label_ids[i]);
|
||||
|
||||
const double percent = normalize ? (double)values[i] / sum : (double)values[i];
|
||||
const bool skip = sum <= 0.0 || (ignore_hidden && item != nullptr && !item->Show);
|
||||
if (!skip)
|
||||
a1 = a0 + 2 * IM_PI * percent;
|
||||
|
||||
if (BeginItemEx(label_ids[i], FitterRect(Pmin, Pmax))) {
|
||||
if (sum > 0.0) {
|
||||
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();
|
||||
}
|
||||
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) {
|
||||
a0 = angle0 * 2 * IM_PI / 360.0;
|
||||
a1 = angle0 * 2 * IM_PI / 360.0;
|
||||
double a0 = angle0 * 2 * IM_PI / 360.0;
|
||||
double a1 = angle0 * 2 * IM_PI / 360.0;
|
||||
char buffer[32];
|
||||
for (int i = 0; i < count; ++i) {
|
||||
ImPlotItem* item = GetItem(label_ids[i]);
|
||||
double percent = normalize ? (double)values[i] / sum : (double)values[i];
|
||||
a1 = a0 + 2 * IM_PI * percent;
|
||||
if (item->Show) {
|
||||
ImFormatString(buffer, 32, fmt, (double)values[i]);
|
||||
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);
|
||||
IM_ASSERT(item != nullptr);
|
||||
|
||||
const double percent = normalize ? (double)values[i] / sum : (double)values[i];
|
||||
const bool skip = ignore_hidden && item != nullptr && !item->Show;
|
||||
|
||||
if (!skip) {
|
||||
a1 = a0 + 2 * IM_PI * percent;
|
||||
if (item->Show) {
|
||||
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();
|
||||
}
|
||||
#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()
|
||||
#undef INSTANTIATE_MACRO
|
||||
|
||||
|
@ -2283,8 +2391,8 @@ struct GetterHeatmapColMaj {
|
|||
{ }
|
||||
template <typename I> IMPLOT_INLINE RectC operator()(I idx) const {
|
||||
double val = (double)Values[idx];
|
||||
const int r = idx % Cols;
|
||||
const int c = idx / Cols;
|
||||
const int r = idx % Rows;
|
||||
const int c = idx / Rows;
|
||||
const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height));
|
||||
RectC rect;
|
||||
rect.Pos = p;
|
||||
|
@ -2375,6 +2483,10 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d
|
|||
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) {
|
||||
if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max))) {
|
||||
if (rows <= 0 || cols <= 0) {
|
||||
EndItem();
|
||||
return;
|
||||
}
|
||||
ImDrawList& draw_list = *GetPlotDrawList();
|
||||
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);
|
||||
|
@ -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 (y_bins <= 0 || x_bins <= 0) {
|
||||
EndItem();
|
||||
return max_count;
|
||||
}
|
||||
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);
|
||||
EndItem();
|
||||
|
@ -2597,8 +2713,8 @@ void PlotDigitalEx(const char* label_id, Getter getter, ImPlotDigitalFlags flags
|
|||
//do not extend plot outside plot range
|
||||
if (pMin.x < x_axis.PixelMin) pMin.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 (pMax.x > x_axis.PixelMax) pMax.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 - 1; //fix issue related to https://github.com/ocornut/imgui/issues/3976
|
||||
//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))) {
|
||||
// ImVec4 colAlpha = item->Color;
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
// This is a slightly modified version of stb_textedit.h 1.14.
|
||||
// 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_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.
|
||||
// - 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
|
||||
// Development of this library was sponsored by RAD Game Tools
|
||||
|
@ -30,7 +31,7 @@
|
|||
// DEPENDENCIES
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//
|
||||
|
@ -274,8 +275,8 @@
|
|||
////
|
||||
////
|
||||
|
||||
#ifndef INCLUDE_STB_TEXTEDIT_H
|
||||
#define INCLUDE_STB_TEXTEDIT_H
|
||||
#ifndef INCLUDE_IMSTB_TEXTEDIT_H
|
||||
#define INCLUDE_IMSTB_TEXTEDIT_H
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -286,33 +287,33 @@
|
|||
// and undo state.
|
||||
//
|
||||
|
||||
#ifndef STB_TEXTEDIT_UNDOSTATECOUNT
|
||||
#define STB_TEXTEDIT_UNDOSTATECOUNT 99
|
||||
#ifndef IMSTB_TEXTEDIT_UNDOSTATECOUNT
|
||||
#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99
|
||||
#endif
|
||||
#ifndef STB_TEXTEDIT_UNDOCHARCOUNT
|
||||
#define STB_TEXTEDIT_UNDOCHARCOUNT 999
|
||||
#ifndef IMSTB_TEXTEDIT_UNDOCHARCOUNT
|
||||
#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999
|
||||
#endif
|
||||
#ifndef STB_TEXTEDIT_CHARTYPE
|
||||
#define STB_TEXTEDIT_CHARTYPE int
|
||||
#ifndef IMSTB_TEXTEDIT_CHARTYPE
|
||||
#define IMSTB_TEXTEDIT_CHARTYPE int
|
||||
#endif
|
||||
#ifndef STB_TEXTEDIT_POSITIONTYPE
|
||||
#define STB_TEXTEDIT_POSITIONTYPE int
|
||||
#ifndef IMSTB_TEXTEDIT_POSITIONTYPE
|
||||
#define IMSTB_TEXTEDIT_POSITIONTYPE int
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// private data
|
||||
STB_TEXTEDIT_POSITIONTYPE where;
|
||||
STB_TEXTEDIT_POSITIONTYPE insert_length;
|
||||
STB_TEXTEDIT_POSITIONTYPE delete_length;
|
||||
IMSTB_TEXTEDIT_POSITIONTYPE where;
|
||||
IMSTB_TEXTEDIT_POSITIONTYPE insert_length;
|
||||
IMSTB_TEXTEDIT_POSITIONTYPE delete_length;
|
||||
int char_storage;
|
||||
} StbUndoRecord;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// private data
|
||||
StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT];
|
||||
STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT];
|
||||
StbUndoRecord undo_rec [IMSTB_TEXTEDIT_UNDOSTATECOUNT];
|
||||
IMSTB_TEXTEDIT_CHARTYPE undo_char[IMSTB_TEXTEDIT_UNDOCHARCOUNT];
|
||||
short undo_point, redo_point;
|
||||
int undo_char_point, redo_char_point;
|
||||
} StbUndoState;
|
||||
|
@ -371,7 +372,7 @@ typedef struct
|
|||
float ymin,ymax; // height of row above and below baseline
|
||||
int num_chars;
|
||||
} 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
|
||||
// included just the "header" portion
|
||||
#ifdef STB_TEXTEDIT_IMPLEMENTATION
|
||||
#ifdef IMSTB_TEXTEDIT_IMPLEMENTATION
|
||||
|
||||
#ifndef STB_TEXTEDIT_memmove
|
||||
#ifndef IMSTB_TEXTEDIT_memmove
|
||||
#include <string.h>
|
||||
#define STB_TEXTEDIT_memmove memmove
|
||||
#define IMSTB_TEXTEDIT_memmove memmove
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -398,7 +399,7 @@ typedef struct
|
|||
//
|
||||
|
||||
// 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;
|
||||
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
|
||||
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
|
||||
// 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
|
||||
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;
|
||||
|
||||
|
@ -502,11 +503,11 @@ static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state
|
|||
//
|
||||
|
||||
// forward declarations
|
||||
static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
||||
static void stb_text_redo(STB_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_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
||||
static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
||||
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_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
|
||||
{
|
||||
|
@ -518,7 +519,7 @@ typedef struct
|
|||
|
||||
// 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)
|
||||
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;
|
||||
int prev_start = 0;
|
||||
|
@ -549,7 +550,10 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
|
|||
i += r.num_chars;
|
||||
find->y += r.baseline_y_delta;
|
||||
if (i == z) // [DEAR IMGUI]
|
||||
{
|
||||
r.num_chars = 0; // [DEAR IMGUI]
|
||||
break; // [DEAR IMGUI]
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
// 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);
|
||||
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
|
||||
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_TEXTEDIT_DELETECHARS(str, where, len);
|
||||
|
@ -588,7 +592,7 @@ static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *sta
|
|||
}
|
||||
|
||||
// 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);
|
||||
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
|
||||
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)) {
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
#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
|
||||
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
|
||||
|
||||
#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);
|
||||
++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
|
||||
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)) {
|
||||
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
|
||||
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
|
||||
stb_textedit_clamp(str, state);
|
||||
|
@ -717,14 +721,14 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta
|
|||
#endif
|
||||
|
||||
// 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:
|
||||
switch (key) {
|
||||
default: {
|
||||
int c = STB_TEXTEDIT_KEYTOTEXT(key);
|
||||
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
|
||||
if (c == '\n' && state->single_line)
|
||||
|
@ -889,8 +893,8 @@ retry:
|
|||
x = row.x0;
|
||||
for (i=0; i < row.num_chars; ++i) {
|
||||
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
|
||||
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
break;
|
||||
#endif
|
||||
x += dx;
|
||||
|
@ -951,8 +955,8 @@ retry:
|
|||
x = row.x0;
|
||||
for (i=0; i < row.num_chars; ++i) {
|
||||
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
|
||||
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
break;
|
||||
#endif
|
||||
x += dx;
|
||||
|
@ -1109,8 +1113,8 @@ retry:
|
|||
|
||||
static void stb_textedit_flush_redo(StbUndoState *state)
|
||||
{
|
||||
state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
|
||||
state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
|
||||
state->redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
|
||||
state->redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
|
||||
}
|
||||
|
||||
// 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;
|
||||
// delete n characters from all other records
|
||||
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)
|
||||
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_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
|
||||
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 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;
|
||||
// move the remaining redo character data to the end of the buffer
|
||||
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
|
||||
for (i=state->redo_point; i < k; ++i)
|
||||
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'
|
||||
// [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_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 + 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
|
||||
++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
|
||||
// existing records down
|
||||
if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
if (state->undo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
stb_textedit_discard_undo(state);
|
||||
|
||||
// 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_char_point = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
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);
|
||||
if (r == NULL)
|
||||
return NULL;
|
||||
|
||||
r->where = pos;
|
||||
r->insert_length = (STB_TEXTEDIT_POSITIONTYPE) insert_len;
|
||||
r->delete_length = (STB_TEXTEDIT_POSITIONTYPE) delete_len;
|
||||
r->insert_length = (IMSTB_TEXTEDIT_POSITIONTYPE) insert_len;
|
||||
r->delete_length = (IMSTB_TEXTEDIT_POSITIONTYPE) delete_len;
|
||||
|
||||
if (insert_len == 0) {
|
||||
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;
|
||||
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
|
||||
// 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
|
||||
r->insert_length = 0;
|
||||
} 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
|
||||
while (s->undo_char_point + u.delete_length > s->redo_char_point) {
|
||||
// should never happen:
|
||||
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
return;
|
||||
// there's currently not enough room, so discard a redo record
|
||||
stb_textedit_discard_redo(s);
|
||||
|
@ -1278,11 +1282,11 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
|||
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;
|
||||
StbUndoRecord *u, r;
|
||||
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
return;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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;
|
||||
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) {
|
||||
for (i=0; i < length; ++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;
|
||||
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) {
|
||||
for (i=0; i < old_length; ++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_char_point = 0;
|
||||
state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
|
||||
state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
|
||||
state->undostate.redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
|
||||
state->undostate.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
|
||||
state->select_end = state->select_start = 0;
|
||||
state->cursor = 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"
|
||||
#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__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif//STB_TEXTEDIT_IMPLEMENTATION
|
||||
#endif//IMSTB_TEXTEDIT_IMPLEMENTATION
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
|
|
|
@ -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
|
@ -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
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
|
@ -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
|
|
@ -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
|
@ -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);
|
|
@ -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);
|
||||
}
|
|
@ -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
|
@ -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
|
||||
|
||||
}
|
|
@ -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)
|
|
@ -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.)
|
||||
```
|
|
@ -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
|
@ -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 */
|
|
@ -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
|
|
@ -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 */
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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__)*/
|
||||
|
|
@ -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 (×tamp))
|
||||
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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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
|
@ -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
|
|
@ -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*/
|
|
@ -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
|
|
@ -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;
|
|
@ -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;
|
|
@ -1,4 +1,6 @@
|
|||
const gui = @import("gui.zig");
|
||||
const backend_glfw = @import("backend_glfw.zig");
|
||||
const backend_dx12 = @import("backend_dx12.zig");
|
||||
|
||||
pub fn init(
|
||||
window: *const anyopaque, // zglfw.Window
|
||||
|
@ -6,33 +8,28 @@ pub fn init(
|
|||
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,
|
||||
font_srv_cpu_desc_handle: backend_dx12.D3D12_CPU_DESCRIPTOR_HANDLE,
|
||||
font_srv_gpu_desc_handle: backend_dx12.D3D12_GPU_DESCRIPTOR_HANDLE,
|
||||
) void {
|
||||
if (!ImGui_ImplGlfw_InitForOther(window, true)) {
|
||||
@panic("failed to init glfw for imgui");
|
||||
}
|
||||
|
||||
if (!ImGui_ImplDX12_Init(
|
||||
backend_glfw.init(window);
|
||||
backend_dx12.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_ImplGlfw_Shutdown();
|
||||
ImGui_ImplDX12_Shutdown();
|
||||
backend_dx12.deinit();
|
||||
backend_glfw.deinit();
|
||||
}
|
||||
|
||||
pub fn newFrame(fb_width: u32, fb_height: u32) void {
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui_ImplDX12_NewFrame();
|
||||
backend_glfw.newFrame();
|
||||
backend_dx12.newFrame();
|
||||
|
||||
gui.io.setDisplaySize(@as(f32, @floatFromInt(fb_width)), @as(f32, @floatFromInt(fb_height)));
|
||||
gui.io.setDisplayFramebufferScale(1.0, 1.0);
|
||||
|
@ -44,31 +41,5 @@ pub fn draw(
|
|||
graphics_command_list: *const anyopaque, // *ID3D12GraphicsCommandList
|
||||
) void {
|
||||
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;
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
const gui = @import("gui.zig");
|
||||
const backend_glfw = @import("backend_glfw.zig");
|
||||
|
||||
pub fn initWithGlSlVersion(
|
||||
window: *const anyopaque, // zglfw.Window
|
||||
glsl_version: ?[:0]const u8, // e.g. "#version 130"
|
||||
) void {
|
||||
if (!ImGui_ImplGlfw_InitForOpenGL(window, true)) {
|
||||
unreachable;
|
||||
}
|
||||
backend_glfw.initOpenGL(window);
|
||||
|
||||
ImGui_ImplOpenGL3_Init(@ptrCast(glsl_version));
|
||||
}
|
||||
|
@ -18,12 +17,12 @@ pub fn init(
|
|||
}
|
||||
|
||||
pub fn deinit() void {
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
backend_glfw.deinit();
|
||||
}
|
||||
|
||||
pub fn newFrame(fb_width: u32, fb_height: u32) void {
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
backend_glfw.newFrame();
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
extern fn ImGui_ImplGlfw_InitForOpenGL(window: *const anyopaque, install_callbacks: bool) bool;
|
||||
extern fn ImGui_ImplGlfw_NewFrame() void;
|
||||
extern fn ImGui_ImplGlfw_Shutdown() void;
|
||||
// Those functions are defined in 'imgui_impl_opengl3.cpp`
|
||||
// (they include few custom changes).
|
||||
extern fn ImGui_ImplOpenGL3_Init(glsl_version: [*c]const u8) void;
|
||||
extern fn ImGui_ImplOpenGL3_Shutdown() void;
|
||||
extern fn ImGui_ImplOpenGL3_NewFrame() void;
|
||||
|
|
|
@ -1,33 +1,40 @@
|
|||
const gui = @import("gui.zig");
|
||||
const backend_glfw = @import("backend_glfw.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
|
||||
wgpu_device: *const anyopaque, // WGPUDevice
|
||||
wgpu_swap_chain_format: u32, // WGPUTextureFormat
|
||||
wgpu_depth_format: u32, // WGPUTextureFormat
|
||||
wgpu_device: *const anyopaque, // wgpu.Device
|
||||
wgpu_swap_chain_format: u32, // wgpu.TextureFormat
|
||||
wgpu_depth_format: u32, // wgpu.TextureFormat
|
||||
) void {
|
||||
if (!ImGui_ImplGlfw_InitForOther(window, true)) {
|
||||
unreachable;
|
||||
}
|
||||
backend_glfw.init(window);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit() void {
|
||||
ImGui_ImplWGPU_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
backend_glfw.deinit();
|
||||
}
|
||||
|
||||
pub fn newFrame(fb_width: u32, fb_height: u32) void {
|
||||
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.newFrame();
|
||||
|
@ -38,17 +45,23 @@ pub fn draw(wgpu_render_pass: *const anyopaque) void {
|
|||
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).
|
||||
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_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_Init(init_info: *ImGui_ImplWGPU_InitInfo) bool;
|
||||
extern fn ImGui_ImplWGPU_NewFrame() void;
|
||||
extern fn ImGui_ImplWGPU_RenderDrawData(draw_data: *const anyopaque, pass_encoder: *const anyopaque) void;
|
||||
extern fn ImGui_ImplWGPU_Shutdown() void;
|
||||
|
|
|
@ -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;
|
267
src/gui.zig
267
src/gui.zig
|
@ -5,13 +5,16 @@
|
|||
//
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
pub const plot = @import("plot.zig");
|
||||
pub const te = @import("te.zig");
|
||||
pub const backend = switch (@import("zgui_options").backend) {
|
||||
.glfw_wgpu => @import("backend_glfw_wgpu.zig"),
|
||||
.glfw_opengl3 => @import("backend_glfw_opengl.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 => .{},
|
||||
};
|
||||
const te_enabled = @import("zgui_options").with_te;
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
@ -26,6 +29,7 @@ pub const DrawVert = extern struct {
|
|||
color: u32,
|
||||
};
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) void {
|
||||
if (zguiGetCurrentContext() == null) {
|
||||
mem_allocator = allocator;
|
||||
|
@ -37,6 +41,10 @@ pub fn init(allocator: std.mem.Allocator) void {
|
|||
|
||||
temp_buffer = std.ArrayList(u8).init(allocator);
|
||||
temp_buffer.?.resize(3 * 1024 + 1) catch unreachable;
|
||||
|
||||
if (te_enabled) {
|
||||
te.init();
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn deinit() void {
|
||||
|
@ -44,6 +52,12 @@ pub fn deinit() void {
|
|||
temp_buffer.?.deinit();
|
||||
zguiDestroyContext(null);
|
||||
|
||||
// Must be after destroy imgui context.
|
||||
// And before allocation check
|
||||
if (te_enabled) {
|
||||
te.deinit();
|
||||
}
|
||||
|
||||
if (mem_allocations.?.count() > 0) {
|
||||
var it = mem_allocations.?.iterator();
|
||||
while (it.next()) |kv| {
|
||||
|
@ -124,7 +138,13 @@ pub const ConfigFlags = packed struct(c_int) {
|
|||
nav_no_capture_keyboard: bool = false,
|
||||
no_mouse: 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_touch_screen: bool = false,
|
||||
_padding: u10 = 0,
|
||||
|
@ -147,6 +167,7 @@ pub const FontConfig = extern struct {
|
|||
merge_mode: bool,
|
||||
font_builder_flags: c_uint,
|
||||
rasterizer_multiply: f32,
|
||||
rasterizer_density: f32,
|
||||
ellipsis_char: Wchar,
|
||||
name: [40]u8,
|
||||
dst_font: *Font,
|
||||
|
@ -326,7 +347,7 @@ pub const DrawData = *extern struct {
|
|||
cmd_lists_count: c_int,
|
||||
total_idx_count: c_int,
|
||||
total_vtx_count: c_int,
|
||||
cmd_lists: [*]DrawList,
|
||||
cmd_lists: Vector(DrawList),
|
||||
display_pos: [2]f32,
|
||||
display_size: [2]f32,
|
||||
framebuffer_scale: [2]f32,
|
||||
|
@ -334,7 +355,7 @@ pub const DrawData = *extern struct {
|
|||
pub const Font = *opaque {};
|
||||
pub const Ident = u32;
|
||||
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) {
|
||||
none = 0,
|
||||
tab = 512,
|
||||
|
@ -409,6 +430,18 @@ pub const Key = enum(c_int) {
|
|||
f10,
|
||||
f11,
|
||||
f12,
|
||||
f13,
|
||||
f14,
|
||||
f15,
|
||||
f16,
|
||||
f17,
|
||||
f18,
|
||||
f19,
|
||||
f20,
|
||||
f21,
|
||||
f22,
|
||||
f23,
|
||||
f24,
|
||||
apostrophe,
|
||||
comma,
|
||||
minus,
|
||||
|
@ -443,6 +476,9 @@ pub const Key = enum(c_int) {
|
|||
keypad_enter,
|
||||
keypad_equal,
|
||||
|
||||
app_back,
|
||||
app_forward,
|
||||
|
||||
gamepad_start,
|
||||
gamepad_back,
|
||||
gamepad_faceleft,
|
||||
|
@ -483,6 +519,7 @@ pub const Key = enum(c_int) {
|
|||
mod_super = 1 << 15,
|
||||
mod_mask_ = 0xf000,
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
pub const WindowFlags = packed struct(c_int) {
|
||||
no_title_bar: bool = false,
|
||||
|
@ -501,12 +538,11 @@ pub const WindowFlags = packed struct(c_int) {
|
|||
no_bring_to_front_on_focus: bool = false,
|
||||
always_vertical_scrollbar: bool = false,
|
||||
always_horizontal_scrollbar: bool = false,
|
||||
always_use_window_padding: bool = false,
|
||||
_removed: u1 = 0,
|
||||
no_nav_inputs: bool = false,
|
||||
no_nav_focus: 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_decoration = WindowFlags{
|
||||
|
@ -521,6 +557,20 @@ pub const WindowFlags = packed struct(c_int) {
|
|||
.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) {
|
||||
_reserved0: bool = false,
|
||||
|
@ -629,7 +679,12 @@ pub fn setNextWindowBgAlpha(args: SetNextWindowBgAlpha) void {
|
|||
zguiSetNextWindowBgAlpha(args.alpha);
|
||||
}
|
||||
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 {
|
||||
zguiSetKeyboardFocusHere(offset);
|
||||
}
|
||||
|
@ -651,19 +706,19 @@ extern fn zguiEnd() void;
|
|||
const BeginChild = struct {
|
||||
w: f32 = 0.0,
|
||||
h: f32 = 0.0,
|
||||
border: bool = false,
|
||||
flags: WindowFlags = .{},
|
||||
child_flags: ChildFlags = .{},
|
||||
window_flags: WindowFlags = .{},
|
||||
};
|
||||
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 {
|
||||
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 const endChild = zguiEndChild;
|
||||
extern fn zguiBeginChild(str_id: [*:0]const u8, w: f32, h: f32, border: bool, flags: WindowFlags) bool;
|
||||
extern fn zguiBeginChildId(id: Ident, 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, flags: ChildFlags, window_flags: WindowFlags) bool;
|
||||
extern fn zguiEndChild() void;
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// `pub fn zguiGetScrollX() f32`
|
||||
|
@ -720,7 +775,8 @@ pub const FocusedFlags = packed struct(c_int) {
|
|||
root_window: bool = false,
|
||||
any_window: 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 };
|
||||
};
|
||||
|
@ -730,22 +786,27 @@ pub const HoveredFlags = packed struct(c_int) {
|
|||
root_window: bool = false,
|
||||
any_window: bool = false,
|
||||
no_popup_hierarchy: bool = false,
|
||||
_reserved0: bool = false,
|
||||
dock_hierarchy: bool = false,
|
||||
allow_when_blocked_by_popup: bool = false,
|
||||
_reserved1: 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,
|
||||
no_nav_override: bool = false,
|
||||
for_tooltip: bool = false,
|
||||
stationary: bool = false,
|
||||
delay_none: bool = false,
|
||||
delay_normal: bool = false,
|
||||
delay_short: bool = false,
|
||||
no_shared_delay: bool = false,
|
||||
_padding: u18 = 0,
|
||||
_padding: u14 = 0,
|
||||
|
||||
pub const rect_only = HoveredFlags{
|
||||
.allow_when_blocked_by_popup = 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 };
|
||||
};
|
||||
|
@ -812,6 +873,79 @@ extern fn zguiGetContentRegionAvail(size: *[2]f32) void;
|
|||
extern fn zguiGetContentRegionMax(size: *[2]f32) void;
|
||||
extern fn zguiGetWindowContentRegionMin(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
|
||||
|
@ -847,6 +981,8 @@ pub const Style = extern struct {
|
|||
tab_rounding: f32,
|
||||
tab_border_size: f32,
|
||||
tab_min_width_for_close_button: f32,
|
||||
tab_bar_border_size: f32,
|
||||
table_angled_header_angle: f32,
|
||||
color_button_position: Direction,
|
||||
button_text_align: [2]f32,
|
||||
selectable_text_align: [2]f32,
|
||||
|
@ -855,6 +991,7 @@ pub const Style = extern struct {
|
|||
separator_text_padding: [2]f32,
|
||||
display_window_padding: [2]f32,
|
||||
display_safe_area_padding: [2]f32,
|
||||
docking_separator_size: f32,
|
||||
mouse_cursor_scale: f32,
|
||||
anti_aliased_lines: bool,
|
||||
anti_aliased_lines_use_tex: bool,
|
||||
|
@ -864,6 +1001,13 @@ pub const Style = extern struct {
|
|||
|
||||
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 const init = zguiStyle_Init;
|
||||
extern fn zguiStyle_Init() Style;
|
||||
|
@ -922,6 +1066,8 @@ pub const StyleCol = enum(c_int) {
|
|||
tab_active,
|
||||
tab_unfocused,
|
||||
tab_unfocused_active,
|
||||
docking_preview,
|
||||
docking_empty_bg,
|
||||
plot_lines,
|
||||
plot_lines_hovered,
|
||||
plot_histogram,
|
||||
|
@ -996,11 +1142,13 @@ pub const StyleVar = enum(c_int) {
|
|||
grab_min_size, // 1f
|
||||
grab_rounding, // 1f
|
||||
tab_rounding, // 1f
|
||||
tab_bar_border_size, // 1f
|
||||
button_text_align, // 2f
|
||||
selectable_text_align, // 2f
|
||||
separator_text_border_size, // 1f
|
||||
separator_text_align, // 2f
|
||||
separator_text_padding, // 2f
|
||||
docking_separator_size, // 1f
|
||||
};
|
||||
|
||||
pub fn pushStyleVar1f(args: struct {
|
||||
|
@ -1227,8 +1375,14 @@ pub fn getItemRectMin() [2]f32 {
|
|||
zguiGetItemRectMin(&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 zguiGetItemRectMin(rect: *[2]f32) void;
|
||||
extern fn zguiGetItemRectSize(rect: *[2]f32) void;
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// ID stack/scopes
|
||||
|
@ -1502,31 +1656,35 @@ pub fn comboFromEnum(
|
|||
/// i32 (the underlying imgui restriction)
|
||||
current_item: anytype,
|
||||
) bool {
|
||||
const item_names = comptime lbl: {
|
||||
const item_type = @typeInfo(@TypeOf(current_item.*));
|
||||
switch (item_type) {
|
||||
.Enum => |e| {
|
||||
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));
|
||||
},
|
||||
}
|
||||
const EnumType = @TypeOf(current_item.*);
|
||||
const enum_type_info = switch (@typeInfo(EnumType)) {
|
||||
.Enum => |enum_type_info| enum_type_info,
|
||||
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, .{
|
||||
.items_separated_by_zeros = item_names,
|
||||
.current_item = &item,
|
||||
});
|
||||
|
||||
current_item.* = @enumFromInt(item);
|
||||
current_item.* = index_to_enum[@intCast(item)];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -1545,7 +1703,8 @@ pub const ComboFlags = packed struct(c_int) {
|
|||
height_largest: bool = false,
|
||||
no_arrow_button: bool = false,
|
||||
no_preview: bool = false,
|
||||
_padding: u25 = 0,
|
||||
width_fit_preview: bool = false,
|
||||
_padding: u24 = 0,
|
||||
};
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const BeginCombo = struct {
|
||||
|
@ -2235,7 +2394,7 @@ pub const InputTextCallbackData = extern struct {
|
|||
pub const InputTextCallback = *const fn (data: *InputTextCallbackData) i32;
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
pub fn inputText(label: [:0]const u8, args: struct {
|
||||
buf: []u8,
|
||||
buf: [:0]u8,
|
||||
flags: InputTextFlags = .{},
|
||||
callback: ?InputTextCallback = null,
|
||||
user_data: ?*anyopaque = null,
|
||||
|
@ -2243,7 +2402,7 @@ pub fn inputText(label: [:0]const u8, args: struct {
|
|||
return zguiInputText(
|
||||
label,
|
||||
args.buf.ptr,
|
||||
args.buf.len,
|
||||
args.buf.len + 1, // + 1 for sentinel
|
||||
args.flags,
|
||||
if (args.callback) |cb| cb else null,
|
||||
args.user_data,
|
||||
|
@ -2259,7 +2418,7 @@ extern fn zguiInputText(
|
|||
) bool;
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
pub fn inputTextMultiline(label: [:0]const u8, args: struct {
|
||||
buf: []u8,
|
||||
buf: [:0]u8,
|
||||
w: f32 = 0.0,
|
||||
h: f32 = 0.0,
|
||||
flags: InputTextFlags = .{},
|
||||
|
@ -2269,7 +2428,7 @@ pub fn inputTextMultiline(label: [:0]const u8, args: struct {
|
|||
return zguiInputTextMultiline(
|
||||
label,
|
||||
args.buf.ptr,
|
||||
args.buf.len,
|
||||
args.buf.len + 1, // + 1 for sentinel
|
||||
args.w,
|
||||
args.h,
|
||||
args.flags,
|
||||
|
@ -2290,7 +2449,7 @@ extern fn zguiInputTextMultiline(
|
|||
//--------------------------------------------------------------------------------------------------
|
||||
pub fn inputTextWithHint(label: [:0]const u8, args: struct {
|
||||
hint: [:0]const u8,
|
||||
buf: []u8,
|
||||
buf: [:0]u8,
|
||||
flags: InputTextFlags = .{},
|
||||
callback: ?InputTextCallback = null,
|
||||
user_data: ?*anyopaque = null,
|
||||
|
@ -2299,7 +2458,7 @@ pub fn inputTextWithHint(label: [:0]const u8, args: struct {
|
|||
label,
|
||||
args.hint,
|
||||
args.buf.ptr,
|
||||
args.buf.len,
|
||||
args.buf.len + 1, // + 1 for sentinel
|
||||
args.flags,
|
||||
if (args.callback) |cb| cb else null,
|
||||
args.user_data,
|
||||
|
@ -2620,7 +2779,7 @@ extern fn zguiColorButton(
|
|||
pub const TreeNodeFlags = packed struct(c_int) {
|
||||
selected: bool = false,
|
||||
framed: bool = false,
|
||||
allow_item_overlap: bool = false,
|
||||
allow_overlap: bool = false,
|
||||
no_tree_push_on_open: bool = false,
|
||||
no_auto_open_on_log: bool = false,
|
||||
default_open: bool = false,
|
||||
|
@ -2631,8 +2790,9 @@ pub const TreeNodeFlags = packed struct(c_int) {
|
|||
frame_padding: bool = false,
|
||||
span_avail_width: bool = false,
|
||||
span_full_width: bool = false,
|
||||
span_all_columns: bool = false,
|
||||
nav_left_jumps_back_here: bool = false,
|
||||
_padding: u18 = 0,
|
||||
_padding: u17 = 0,
|
||||
|
||||
pub const collapsing_header = TreeNodeFlags{
|
||||
.framed = true,
|
||||
|
@ -2732,7 +2892,7 @@ pub const SelectableFlags = packed struct(c_int) {
|
|||
span_all_columns: bool = false,
|
||||
allow_double_click: bool = false,
|
||||
disabled: bool = false,
|
||||
allow_item_overlap: bool = false,
|
||||
allow_overlap: bool = false,
|
||||
_padding: u27 = 0,
|
||||
};
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
@ -3245,11 +3405,13 @@ pub const PopupFlags = packed struct(c_int) {
|
|||
_reserved0: bool = false,
|
||||
_reserved1: bool = false,
|
||||
|
||||
no_reopen: bool = false,
|
||||
_reserved2: bool = false,
|
||||
no_open_over_existing_popup: bool = false,
|
||||
no_open_over_items: bool = false,
|
||||
any_popup_id: 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 };
|
||||
};
|
||||
|
@ -3297,7 +3459,8 @@ pub const TabItemFlags = packed struct(c_int) {
|
|||
no_reorder: bool = false,
|
||||
leading: 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 {
|
||||
return zguiBeginTabBar(label, flags);
|
||||
|
@ -4380,6 +4543,14 @@ pub const DrawList = *opaque {
|
|||
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 {
|
||||
const testing = std.testing;
|
||||
|
||||
|
|
|
@ -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.?);
|
||||
}
|
241
src/zgui.cpp
241
src/zgui.cpp
|
@ -4,6 +4,16 @@
|
|||
#include "implot.h"
|
||||
#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
|
||||
#define ZGUI_API
|
||||
#endif
|
||||
|
@ -51,6 +61,10 @@ ZGUI_API void zguiSetNextWindowBgAlpha(float alpha) {
|
|||
ImGui::SetNextWindowBgAlpha(alpha);
|
||||
}
|
||||
|
||||
ZGUI_API void zguiSetWindowFocus(const char* name) {
|
||||
ImGui::SetWindowFocus(name);
|
||||
}
|
||||
|
||||
ZGUI_API void zguiSetKeyboardFocusHere(int offset) {
|
||||
ImGui::SetKeyboardFocusHere(offset);
|
||||
}
|
||||
|
@ -63,12 +77,12 @@ ZGUI_API void zguiEnd(void) {
|
|||
ImGui::End();
|
||||
}
|
||||
|
||||
ZGUI_API bool zguiBeginChild(const char* str_id, float w, float h, bool border, ImGuiWindowFlags flags) {
|
||||
return ImGui::BeginChild(str_id, { w, h }, border, 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 }, child_flags, window_flags);
|
||||
}
|
||||
|
||||
ZGUI_API bool zguiBeginChildId(ImGuiID id, float w, float h, bool border, ImGuiWindowFlags flags) {
|
||||
return ImGui::BeginChild(id, { w, h }, border, flags);
|
||||
ZGUI_API bool zguiBeginChildId(ImGuiID id, float w, float h, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) {
|
||||
return ImGui::BeginChild(id, { w, h }, child_flags, window_flags);
|
||||
}
|
||||
|
||||
ZGUI_API void zguiEndChild(void) {
|
||||
|
@ -213,6 +227,12 @@ ZGUI_API void zguiGetItemRectMin(float rect[2]) {
|
|||
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]) {
|
||||
const ImVec2 p = ImGui::GetCursorPos();
|
||||
pos[0] = p.x;
|
||||
|
@ -2208,6 +2228,66 @@ ZGUI_API void zguiViewport_GetWorkSize(ImGuiViewport* viewport, float p[2]) {
|
|||
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
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
//
|
||||
|
@ -2473,3 +2553,156 @@ ZGUI_API void zguiPlot_PlotText(
|
|||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
} /* 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
|
||||
|
|
Loading…
Reference in New Issue