chore: update to 2a0400ade5535001bfc09e0e7531515e232be6e7
This commit is contained in:
parent
e009f01d38
commit
c461e29eaa
|
@ -1,2 +1,3 @@
|
||||||
/zig-cache
|
.zig-cache/
|
||||||
/zig-out
|
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.
|
Easy to use, hand-crafted API with default arguments, named parameters and Zig style text formatting. [Here](https://github.com/michal-z/zig-gamedev/tree/main/samples/minimal_zgpu_zgui) is a simple sample application, and [here](https://github.com/michal-z/zig-gamedev/tree/main/samples/gui_test_wgpu) is a full one.
|
||||||
|
|
||||||
|
@ -8,6 +8,13 @@ Easy to use, hand-crafted API with default arguments, named parameters and Zig s
|
||||||
* All memory allocations go through user provided Zig allocator
|
* All memory allocations go through user provided Zig allocator
|
||||||
* [DrawList API](#drawlist-api) for vector graphics, text rendering and custom widgets
|
* [DrawList API](#drawlist-api) for vector graphics, text rendering and custom widgets
|
||||||
* [Plot API](#plot-api) for advanced data visualizations
|
* [Plot API](#plot-api) for advanced data visualizations
|
||||||
|
* [Test engine API](#test-engine-api) for automatic testing
|
||||||
|
|
||||||
|
## Versions
|
||||||
|
|
||||||
|
* [ImGui](https://github.com/ocornut/imgui/tree/v1.90.4-docking) `1.90.4-docking`
|
||||||
|
* [ImGui test engine](https://github.com/ocornut/imgui_test_engine/tree/v1.90.4) `1.90.4`
|
||||||
|
* [ImPlot](https://github.com/epezent/implot) `O.17`
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
|
@ -37,11 +44,11 @@ pub fn build(b: *std.Build) void {
|
||||||
exe.linkLibrary(zglfw.artifact("glfw"));
|
exe.linkLibrary(zglfw.artifact("glfw"));
|
||||||
|
|
||||||
const zpool = b.dependency("zpool", .{});
|
const zpool = b.dependency("zpool", .{});
|
||||||
exe.root_module.addImport("zpool", zglfw.module("root"));
|
exe.root_module.addImport("zpool", zpool.module("root"));
|
||||||
|
|
||||||
const zgpu = b.dependency("zgpu", .{});
|
const zgpu = b.dependency("zgpu", .{});
|
||||||
exe.root_module.addImport("zgpu", zglfw.module("root"));
|
exe.root_module.addImport("zgpu", zgpu.module("root"));
|
||||||
exe.linkLibrary(zglfw.artifact("wgpu"));
|
exe.linkLibrary(zgpu.artifact("zdawn"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -147,3 +154,48 @@ if (zgui.plot.beginPlot("Line Plot", .{ .h = -1.0 })) {
|
||||||
zgui.plot.endPlot();
|
zgui.plot.endPlot();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Test Engine API
|
||||||
|
Zig wraper for [ImGUI test engine](https://github.com/ocornut/imgui_test_engine).
|
||||||
|
|
||||||
|
```zig
|
||||||
|
var check_b = false;
|
||||||
|
var _te: *zgui.te.TestEngine = zgui.te.getTestEngine().?;
|
||||||
|
fn registerTests() void {
|
||||||
|
_ = _te.registerTest(
|
||||||
|
"Awesome",
|
||||||
|
"should_do_some_another_magic",
|
||||||
|
@src(),
|
||||||
|
struct {
|
||||||
|
pub fn gui(ctx: *zgui.te.TestContext) !void {
|
||||||
|
_ = ctx; // autofix
|
||||||
|
_ = zgui.begin("Test Window", .{ .flags = .{ .no_saved_settings = true } });
|
||||||
|
defer zgui.end();
|
||||||
|
|
||||||
|
zgui.text("Hello, automation world", .{});
|
||||||
|
_ = zgui.button("Click Me", .{});
|
||||||
|
if (zgui.treeNode("Node")) {
|
||||||
|
defer zgui.treePop();
|
||||||
|
|
||||||
|
_ = zgui.checkbox("Checkbox", .{ .v = &check_b });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(ctx: *zgui.te.TestContext) !void {
|
||||||
|
ctx.setRef("/Test Window");
|
||||||
|
ctx.windowFocus("");
|
||||||
|
|
||||||
|
ctx.itemAction(.click, "Click Me", .{}, null);
|
||||||
|
ctx.itemAction(.open, "Node", .{}, null);
|
||||||
|
ctx.itemAction(.check, "Node/Checkbox", .{}, null);
|
||||||
|
ctx.itemAction(.uncheck, "Node/Checkbox", .{}, null);
|
||||||
|
|
||||||
|
std.testing.expect(true) catch |err| {
|
||||||
|
zgui.te.checkTestError(@src(), err);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
112
build.zig
112
build.zig
|
@ -6,6 +6,7 @@ pub const Backend = enum {
|
||||||
glfw_opengl3,
|
glfw_opengl3,
|
||||||
glfw_dx12,
|
glfw_dx12,
|
||||||
win32_dx12,
|
win32_dx12,
|
||||||
|
glfw,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
pub fn build(b: *std.Build) void {
|
||||||
|
@ -24,6 +25,16 @@ pub fn build(b: *std.Build) void {
|
||||||
"with_implot",
|
"with_implot",
|
||||||
"Build with bundled implot source",
|
"Build with bundled implot source",
|
||||||
) orelse true,
|
) orelse true,
|
||||||
|
.with_te = b.option(
|
||||||
|
bool,
|
||||||
|
"with_te",
|
||||||
|
"Build with bundled test engine support",
|
||||||
|
) orelse false,
|
||||||
|
.use_wchar32 = b.option(
|
||||||
|
bool,
|
||||||
|
"use_wchar32",
|
||||||
|
"Extended unicode support",
|
||||||
|
) orelse false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const options_step = b.addOptions();
|
const options_step = b.addOptions();
|
||||||
|
@ -34,7 +45,7 @@ pub fn build(b: *std.Build) void {
|
||||||
const options_module = options_step.createModule();
|
const options_module = options_step.createModule();
|
||||||
|
|
||||||
_ = b.addModule("root", .{
|
_ = b.addModule("root", .{
|
||||||
.root_source_file = .{ .path = "src/gui.zig" },
|
.root_source_file = b.path("src/gui.zig"),
|
||||||
.imports = &.{
|
.imports = &.{
|
||||||
.{ .name = "zgui_options", .module = options_module },
|
.{ .name = "zgui_options", .module = options_module },
|
||||||
},
|
},
|
||||||
|
@ -49,7 +60,6 @@ pub fn build(b: *std.Build) void {
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
|
||||||
b.installArtifact(lib);
|
|
||||||
if (target.result.os.tag == .windows) {
|
if (target.result.os.tag == .windows) {
|
||||||
lib.defineCMacro("IMGUI_API", "__declspec(dllexport)");
|
lib.defineCMacro("IMGUI_API", "__declspec(dllexport)");
|
||||||
lib.defineCMacro("IMPLOT_API", "__declspec(dllexport)");
|
lib.defineCMacro("IMPLOT_API", "__declspec(dllexport)");
|
||||||
|
@ -69,15 +79,15 @@ pub fn build(b: *std.Build) void {
|
||||||
|
|
||||||
b.installArtifact(imgui);
|
b.installArtifact(imgui);
|
||||||
|
|
||||||
imgui.addIncludePath(.{ .path = "libs" });
|
imgui.addIncludePath(b.path("libs"));
|
||||||
imgui.addIncludePath(.{ .path = "libs/imgui" });
|
imgui.addIncludePath(b.path("libs/imgui"));
|
||||||
|
|
||||||
imgui.linkLibC();
|
imgui.linkLibC();
|
||||||
if (target.result.abi != .msvc)
|
if (target.result.abi != .msvc)
|
||||||
imgui.linkLibCpp();
|
imgui.linkLibCpp();
|
||||||
|
|
||||||
imgui.addCSourceFile(.{
|
imgui.addCSourceFile(.{
|
||||||
.file = .{ .path = "src/zgui.cpp" },
|
.file = b.path("src/zgui.cpp"),
|
||||||
.flags = cflags,
|
.flags = cflags,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -106,12 +116,79 @@ pub fn build(b: *std.Build) void {
|
||||||
imgui.defineCMacro("ZGUI_IMPLOT", "0");
|
imgui.defineCMacro("ZGUI_IMPLOT", "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.use_wchar32) {
|
||||||
|
imgui.defineCMacro("IMGUI_USE_WCHAR32", "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.with_te) {
|
||||||
|
imgui.defineCMacro("ZGUI_TE", "1");
|
||||||
|
|
||||||
|
imgui.defineCMacro("IMGUI_ENABLE_TEST_ENGINE", null);
|
||||||
|
imgui.defineCMacro("IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL", "1");
|
||||||
|
|
||||||
|
imgui.addIncludePath(b.path("libs/imgui_test_engine/"));
|
||||||
|
|
||||||
|
imgui.addCSourceFile(.{ .file = b.path("libs/imgui_test_engine/imgui_capture_tool.cpp"), .flags = cflags });
|
||||||
|
imgui.addCSourceFile(.{ .file = b.path("libs/imgui_test_engine/imgui_te_context.cpp"), .flags = cflags });
|
||||||
|
imgui.addCSourceFile(.{ .file = b.path("libs/imgui_test_engine/imgui_te_coroutine.cpp"), .flags = cflags });
|
||||||
|
imgui.addCSourceFile(.{ .file = b.path("libs/imgui_test_engine/imgui_te_engine.cpp"), .flags = cflags });
|
||||||
|
imgui.addCSourceFile(.{ .file = b.path("libs/imgui_test_engine/imgui_te_exporters.cpp"), .flags = cflags });
|
||||||
|
imgui.addCSourceFile(.{ .file = b.path("libs/imgui_test_engine/imgui_te_perftool.cpp"), .flags = cflags });
|
||||||
|
imgui.addCSourceFile(.{ .file = b.path("libs/imgui_test_engine/imgui_te_ui.cpp"), .flags = cflags });
|
||||||
|
imgui.addCSourceFile(.{ .file = b.path("libs/imgui_test_engine/imgui_te_utils.cpp"), .flags = cflags });
|
||||||
|
|
||||||
|
// TODO: Workaround because zig on win64 doesn have phtreads
|
||||||
|
// TODO: Implement corutine in zig can solve this
|
||||||
|
if (target.result.os.tag == .windows) {
|
||||||
|
const src: []const []const u8 = &.{
|
||||||
|
"libs/winpthreads/src/nanosleep.c",
|
||||||
|
"libs/winpthreads/src/cond.c",
|
||||||
|
"libs/winpthreads/src/barrier.c",
|
||||||
|
"libs/winpthreads/src/misc.c",
|
||||||
|
"libs/winpthreads/src/clock.c",
|
||||||
|
"libs/winpthreads/src/libgcc/dll_math.c",
|
||||||
|
"libs/winpthreads/src/spinlock.c",
|
||||||
|
"libs/winpthreads/src/thread.c",
|
||||||
|
"libs/winpthreads/src/mutex.c",
|
||||||
|
"libs/winpthreads/src/sem.c",
|
||||||
|
"libs/winpthreads/src/sched.c",
|
||||||
|
"libs/winpthreads/src/ref.c",
|
||||||
|
"libs/winpthreads/src/rwlock.c",
|
||||||
|
};
|
||||||
|
|
||||||
|
const winpthreads = b.addStaticLibrary(.{
|
||||||
|
.name = "winpthreads",
|
||||||
|
.optimize = optimize,
|
||||||
|
.target = target,
|
||||||
|
});
|
||||||
|
winpthreads.want_lto = false;
|
||||||
|
winpthreads.root_module.sanitize_c = false;
|
||||||
|
if (optimize == .Debug or optimize == .ReleaseSafe)
|
||||||
|
winpthreads.bundle_compiler_rt = true
|
||||||
|
else
|
||||||
|
winpthreads.root_module.strip = true;
|
||||||
|
winpthreads.addCSourceFiles(.{ .files = src, .flags = &.{
|
||||||
|
"-Wall",
|
||||||
|
"-Wextra",
|
||||||
|
} });
|
||||||
|
winpthreads.defineCMacro("__USE_MINGW_ANSI_STDIO", "1");
|
||||||
|
winpthreads.addIncludePath(b.path("libs/winpthreads/include"));
|
||||||
|
winpthreads.addIncludePath(b.path("libs/winpthreads/src"));
|
||||||
|
winpthreads.linkLibC();
|
||||||
|
b.installArtifact(winpthreads);
|
||||||
|
imgui.linkLibrary(winpthreads);
|
||||||
|
imgui.addSystemIncludePath(b.path("libs/winpthreads/include"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
imgui.defineCMacro("ZGUI_TE", "0");
|
||||||
|
}
|
||||||
|
|
||||||
switch (options.backend) {
|
switch (options.backend) {
|
||||||
.glfw_wgpu => {
|
.glfw_wgpu => {
|
||||||
const zglfw = b.dependency("zglfw", .{});
|
const zglfw = b.dependency("zglfw", .{});
|
||||||
const zgpu = b.dependency("zgpu", .{});
|
const zgpu = b.dependency("zgpu", .{});
|
||||||
imgui.addIncludePath(.{ .path = zglfw.path("libs/glfw/include").getPath(b) });
|
imgui.addIncludePath(zglfw.path("libs/glfw/include"));
|
||||||
imgui.addIncludePath(.{ .path = zgpu.path("libs/dawn/include").getPath(b) });
|
imgui.addIncludePath(zgpu.path("libs/dawn/include"));
|
||||||
imgui.addCSourceFiles(.{
|
imgui.addCSourceFiles(.{
|
||||||
.files = &.{
|
.files = &.{
|
||||||
"libs/imgui/backends/imgui_impl_glfw.cpp",
|
"libs/imgui/backends/imgui_impl_glfw.cpp",
|
||||||
|
@ -122,7 +199,7 @@ pub fn build(b: *std.Build) void {
|
||||||
},
|
},
|
||||||
.glfw_opengl3 => {
|
.glfw_opengl3 => {
|
||||||
const zglfw = b.dependency("zglfw", .{});
|
const zglfw = b.dependency("zglfw", .{});
|
||||||
imgui.addIncludePath(.{ .path = zglfw.path("libs/glfw/include").getPath(b) });
|
imgui.addIncludePath(zglfw.path("libs/glfw/include"));
|
||||||
imgui.addCSourceFiles(.{
|
imgui.addCSourceFiles(.{
|
||||||
.files = &.{
|
.files = &.{
|
||||||
"libs/imgui/backends/imgui_impl_glfw.cpp",
|
"libs/imgui/backends/imgui_impl_glfw.cpp",
|
||||||
|
@ -133,7 +210,7 @@ pub fn build(b: *std.Build) void {
|
||||||
},
|
},
|
||||||
.glfw_dx12 => {
|
.glfw_dx12 => {
|
||||||
const zglfw = b.dependency("zglfw", .{});
|
const zglfw = b.dependency("zglfw", .{});
|
||||||
imgui.addIncludePath(.{ .path = zglfw.path("libs/glfw/include").getPath(b) });
|
imgui.addIncludePath(zglfw.path("libs/glfw/include"));
|
||||||
imgui.addCSourceFiles(.{
|
imgui.addCSourceFiles(.{
|
||||||
.files = &.{
|
.files = &.{
|
||||||
"libs/imgui/backends/imgui_impl_glfw.cpp",
|
"libs/imgui/backends/imgui_impl_glfw.cpp",
|
||||||
|
@ -153,6 +230,21 @@ pub fn build(b: *std.Build) void {
|
||||||
});
|
});
|
||||||
imgui.linkSystemLibrary("d3dcompiler_47");
|
imgui.linkSystemLibrary("d3dcompiler_47");
|
||||||
imgui.linkSystemLibrary("dwmapi");
|
imgui.linkSystemLibrary("dwmapi");
|
||||||
|
switch (target.result.abi) {
|
||||||
|
.msvc => imgui.linkSystemLibrary("Gdi32"),
|
||||||
|
.gnu => imgui.linkSystemLibrary("gdi32"),
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.glfw => {
|
||||||
|
const zglfw = b.dependency("zglfw", .{});
|
||||||
|
imgui.addIncludePath(zglfw.path("libs/glfw/include"));
|
||||||
|
imgui.addCSourceFiles(.{
|
||||||
|
.files = &.{
|
||||||
|
"libs/imgui/backends/imgui_impl_glfw.cpp",
|
||||||
|
},
|
||||||
|
.flags = cflags,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
.no_backend => {},
|
.no_backend => {},
|
||||||
}
|
}
|
||||||
|
@ -161,7 +253,7 @@ pub fn build(b: *std.Build) void {
|
||||||
|
|
||||||
const tests = b.addTest(.{
|
const tests = b.addTest(.{
|
||||||
.name = "zgui-tests",
|
.name = "zgui-tests",
|
||||||
.root_source_file = .{ .path = "src/gui.zig" },
|
.root_source_file = b.path("src/gui.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.{
|
.{
|
||||||
.name = "zgui",
|
.name = "zgui",
|
||||||
.version = "0.1.0",
|
.version = "0.2.0",
|
||||||
.paths = .{
|
.paths = .{
|
||||||
"build.zig",
|
"build.zig",
|
||||||
"build.zig.zon",
|
"build.zig.zon",
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
|
|
||||||
// Implemented features:
|
// Implemented features:
|
||||||
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
|
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
|
||||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices.
|
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||||
|
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
|
||||||
|
// FIXME: The transition from removing a viewport and moving the window in an existing hosted viewport tends to flicker.
|
||||||
|
|
||||||
// Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
|
// Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
|
||||||
// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*.
|
// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*.
|
||||||
|
@ -15,11 +17,15 @@
|
||||||
|
|
||||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
// Learn about Dear ImGui:
|
||||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
// CHANGELOG
|
// CHANGELOG
|
||||||
// (minor and older changes stripped away, please see git history for details)
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
|
||||||
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
||||||
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||||
// 2021-05-19: DirectX12: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
// 2021-05-19: DirectX12: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
||||||
|
@ -39,6 +45,7 @@
|
||||||
// 2018-02-22: Merged into master with all Win32 code synchronized to other examples.
|
// 2018-02-22: Merged into master with all Win32 code synchronized to other examples.
|
||||||
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
#include "imgui_impl_dx12.h"
|
#include "imgui_impl_dx12.h"
|
||||||
|
|
||||||
// DirectX
|
// DirectX
|
||||||
|
@ -47,43 +54,22 @@
|
||||||
#include <d3dcompiler.h>
|
#include <d3dcompiler.h>
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
|
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
|
||||||
#else // mziulek: PFN_D3D12_SERIALIZE_ROOT_SIGNATURE is not defined when using MinGW
|
|
||||||
typedef HRESULT (WINAPI* PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)(
|
|
||||||
const D3D12_ROOT_SIGNATURE_DESC* pRootSignature,
|
|
||||||
D3D_ROOT_SIGNATURE_VERSION Version,
|
|
||||||
ID3DBlob** ppBlob,
|
|
||||||
ID3DBlob** ppErrorBlob);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// DirectX data
|
// DirectX data
|
||||||
struct ImGui_ImplDX12_RenderBuffers
|
|
||||||
{
|
|
||||||
ID3D12Resource* IndexBuffer;
|
|
||||||
ID3D12Resource* VertexBuffer;
|
|
||||||
int IndexBufferSize;
|
|
||||||
int VertexBufferSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ImGui_ImplDX12_Data
|
struct ImGui_ImplDX12_Data
|
||||||
{
|
{
|
||||||
ID3D12Device* pd3dDevice;
|
ID3D12Device* pd3dDevice;
|
||||||
ID3D12RootSignature* pRootSignature;
|
ID3D12RootSignature* pRootSignature;
|
||||||
ID3D12PipelineState* pPipelineState;
|
ID3D12PipelineState* pPipelineState;
|
||||||
DXGI_FORMAT RTVFormat;
|
DXGI_FORMAT RTVFormat;
|
||||||
ID3D12Resource* pFontTextureResource;
|
ID3D12Resource* pFontTextureResource;
|
||||||
D3D12_CPU_DESCRIPTOR_HANDLE hFontSrvCpuDescHandle;
|
D3D12_CPU_DESCRIPTOR_HANDLE hFontSrvCpuDescHandle;
|
||||||
D3D12_GPU_DESCRIPTOR_HANDLE hFontSrvGpuDescHandle;
|
D3D12_GPU_DESCRIPTOR_HANDLE hFontSrvGpuDescHandle;
|
||||||
|
ID3D12DescriptorHeap* pd3dSrvDescHeap;
|
||||||
|
UINT numFramesInFlight;
|
||||||
|
|
||||||
ImGui_ImplDX12_RenderBuffers* pFrameResources;
|
ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||||
UINT numFramesInFlight;
|
|
||||||
UINT frameIndex;
|
|
||||||
|
|
||||||
ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); frameIndex = UINT_MAX; }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VERTEX_CONSTANT_BUFFER_DX12
|
|
||||||
{
|
|
||||||
float mvp[4][4];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||||
|
@ -93,6 +79,97 @@ static ImGui_ImplDX12_Data* ImGui_ImplDX12_GetBackendData()
|
||||||
return ImGui::GetCurrentContext() ? (ImGui_ImplDX12_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
|
return ImGui::GetCurrentContext() ? (ImGui_ImplDX12_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Buffers used during the rendering of a frame
|
||||||
|
struct ImGui_ImplDX12_RenderBuffers
|
||||||
|
{
|
||||||
|
ID3D12Resource* IndexBuffer;
|
||||||
|
ID3D12Resource* VertexBuffer;
|
||||||
|
int IndexBufferSize;
|
||||||
|
int VertexBufferSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Buffers used for secondary viewports created by the multi-viewports systems
|
||||||
|
struct ImGui_ImplDX12_FrameContext
|
||||||
|
{
|
||||||
|
ID3D12CommandAllocator* CommandAllocator;
|
||||||
|
ID3D12Resource* RenderTarget;
|
||||||
|
D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetCpuDescriptors;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data.
|
||||||
|
// Main viewport created by application will only use the Resources field.
|
||||||
|
// Secondary viewports created by this backend will use all the fields (including Window fields),
|
||||||
|
struct ImGui_ImplDX12_ViewportData
|
||||||
|
{
|
||||||
|
// Window
|
||||||
|
ID3D12CommandQueue* CommandQueue;
|
||||||
|
ID3D12GraphicsCommandList* CommandList;
|
||||||
|
ID3D12DescriptorHeap* RtvDescHeap;
|
||||||
|
IDXGISwapChain3* SwapChain;
|
||||||
|
ID3D12Fence* Fence;
|
||||||
|
UINT64 FenceSignaledValue;
|
||||||
|
HANDLE FenceEvent;
|
||||||
|
UINT NumFramesInFlight;
|
||||||
|
ImGui_ImplDX12_FrameContext* FrameCtx;
|
||||||
|
|
||||||
|
// Render buffers
|
||||||
|
UINT FrameIndex;
|
||||||
|
ImGui_ImplDX12_RenderBuffers* FrameRenderBuffers;
|
||||||
|
|
||||||
|
ImGui_ImplDX12_ViewportData(UINT num_frames_in_flight)
|
||||||
|
{
|
||||||
|
CommandQueue = nullptr;
|
||||||
|
CommandList = nullptr;
|
||||||
|
RtvDescHeap = nullptr;
|
||||||
|
SwapChain = nullptr;
|
||||||
|
Fence = nullptr;
|
||||||
|
FenceSignaledValue = 0;
|
||||||
|
FenceEvent = nullptr;
|
||||||
|
NumFramesInFlight = num_frames_in_flight;
|
||||||
|
FrameCtx = new ImGui_ImplDX12_FrameContext[NumFramesInFlight];
|
||||||
|
FrameIndex = UINT_MAX;
|
||||||
|
FrameRenderBuffers = new ImGui_ImplDX12_RenderBuffers[NumFramesInFlight];
|
||||||
|
|
||||||
|
for (UINT i = 0; i < NumFramesInFlight; ++i)
|
||||||
|
{
|
||||||
|
FrameCtx[i].CommandAllocator = nullptr;
|
||||||
|
FrameCtx[i].RenderTarget = nullptr;
|
||||||
|
|
||||||
|
// Create buffers with a default size (they will later be grown as needed)
|
||||||
|
FrameRenderBuffers[i].IndexBuffer = nullptr;
|
||||||
|
FrameRenderBuffers[i].VertexBuffer = nullptr;
|
||||||
|
FrameRenderBuffers[i].VertexBufferSize = 5000;
|
||||||
|
FrameRenderBuffers[i].IndexBufferSize = 10000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~ImGui_ImplDX12_ViewportData()
|
||||||
|
{
|
||||||
|
IM_ASSERT(CommandQueue == nullptr && CommandList == nullptr);
|
||||||
|
IM_ASSERT(RtvDescHeap == nullptr);
|
||||||
|
IM_ASSERT(SwapChain == nullptr);
|
||||||
|
IM_ASSERT(Fence == nullptr);
|
||||||
|
IM_ASSERT(FenceEvent == nullptr);
|
||||||
|
|
||||||
|
for (UINT i = 0; i < NumFramesInFlight; ++i)
|
||||||
|
{
|
||||||
|
IM_ASSERT(FrameCtx[i].CommandAllocator == nullptr && FrameCtx[i].RenderTarget == nullptr);
|
||||||
|
IM_ASSERT(FrameRenderBuffers[i].IndexBuffer == nullptr && FrameRenderBuffers[i].VertexBuffer == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] FrameCtx; FrameCtx = nullptr;
|
||||||
|
delete[] FrameRenderBuffers; FrameRenderBuffers = nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VERTEX_CONSTANT_BUFFER_DX12
|
||||||
|
{
|
||||||
|
float mvp[4][4];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Forward Declarations
|
||||||
|
static void ImGui_ImplDX12_InitPlatformInterface();
|
||||||
|
static void ImGui_ImplDX12_ShutdownPlatformInterface();
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx, ImGui_ImplDX12_RenderBuffers* fr)
|
static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx, ImGui_ImplDX12_RenderBuffers* fr)
|
||||||
{
|
{
|
||||||
|
@ -166,11 +243,10 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL
|
||||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// FIXME: I'm assuming that this only gets called once per frame!
|
|
||||||
// If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator.
|
|
||||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||||
bd->frameIndex = bd->frameIndex + 1;
|
ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)draw_data->OwnerViewport->RendererUserData;
|
||||||
ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[bd->frameIndex % bd->numFramesInFlight];
|
vd->FrameIndex++;
|
||||||
|
ImGui_ImplDX12_RenderBuffers* fr = &vd->FrameRenderBuffers[vd->FrameIndex % bd->numFramesInFlight];
|
||||||
|
|
||||||
// Create and grow vertex/index buffers if needed
|
// Create and grow vertex/index buffers if needed
|
||||||
if (fr->VertexBuffer == nullptr || fr->VertexBufferSize < draw_data->TotalVtxCount)
|
if (fr->VertexBuffer == nullptr || fr->VertexBufferSize < draw_data->TotalVtxCount)
|
||||||
|
@ -522,6 +598,19 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fix(zig-gamedev): TODO workaround from https://github.com/ocornut/imgui/pull/4604/files
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
PFN_D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE D3D12SerializeVersionedRootSignatureFn =
|
||||||
|
(PFN_D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE)::GetProcAddress(d3d12_dll, "D3D12SerializeVersionedRootSignature");
|
||||||
|
if (D3D12SerializeVersionedRootSignatureFn == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ID3DBlob* blob = nullptr;
|
||||||
|
D3D12_VERSIONED_ROOT_SIGNATURE_DESC versioned_desc = {.Version = D3D_ROOT_SIGNATURE_VERSION_1_0, .Desc_1_0 = desc};
|
||||||
|
if (D3D12SerializeVersionedRootSignatureFn(&versioned_desc, &blob, nullptr) != S_OK)
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
|
||||||
PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignatureFn = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)::GetProcAddress(d3d12_dll, "D3D12SerializeRootSignature");
|
PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignatureFn = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)::GetProcAddress(d3d12_dll, "D3D12SerializeRootSignature");
|
||||||
if (D3D12SerializeRootSignatureFn == nullptr)
|
if (D3D12SerializeRootSignatureFn == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
@ -529,6 +618,7 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
|
||||||
ID3DBlob* blob = nullptr;
|
ID3DBlob* blob = nullptr;
|
||||||
if (D3D12SerializeRootSignatureFn(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, nullptr) != S_OK)
|
if (D3D12SerializeRootSignatureFn(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, nullptr) != S_OK)
|
||||||
return false;
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
bd->pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignature));
|
bd->pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignature));
|
||||||
blob->Release();
|
blob->Release();
|
||||||
|
@ -591,9 +681,9 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
|
||||||
// Create the input layout
|
// Create the input layout
|
||||||
static D3D12_INPUT_ELEMENT_DESC local_layout[] =
|
static D3D12_INPUT_ELEMENT_DESC local_layout[] =
|
||||||
{
|
{
|
||||||
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(ImDrawVert, pos), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
||||||
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(ImDrawVert, uv), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
||||||
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)offsetof(ImDrawVert, col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
||||||
};
|
};
|
||||||
psoDesc.InputLayout = { local_layout, 3 };
|
psoDesc.InputLayout = { local_layout, 3 };
|
||||||
}
|
}
|
||||||
|
@ -677,24 +767,24 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplDX12_DestroyRenderBuffers(ImGui_ImplDX12_RenderBuffers* render_buffers)
|
||||||
|
{
|
||||||
|
SafeRelease(render_buffers->IndexBuffer);
|
||||||
|
SafeRelease(render_buffers->VertexBuffer);
|
||||||
|
render_buffers->IndexBufferSize = render_buffers->VertexBufferSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void ImGui_ImplDX12_InvalidateDeviceObjects()
|
void ImGui_ImplDX12_InvalidateDeviceObjects()
|
||||||
{
|
{
|
||||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||||
if (!bd || !bd->pd3dDevice)
|
if (!bd || !bd->pd3dDevice)
|
||||||
return;
|
return;
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
SafeRelease(bd->pRootSignature);
|
SafeRelease(bd->pRootSignature);
|
||||||
SafeRelease(bd->pPipelineState);
|
SafeRelease(bd->pPipelineState);
|
||||||
SafeRelease(bd->pFontTextureResource);
|
SafeRelease(bd->pFontTextureResource);
|
||||||
io.Fonts->SetTexID(0); // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
io.Fonts->SetTexID(0); // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||||
|
|
||||||
for (UINT i = 0; i < bd->numFramesInFlight; i++)
|
|
||||||
{
|
|
||||||
ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[i];
|
|
||||||
SafeRelease(fr->IndexBuffer);
|
|
||||||
SafeRelease(fr->VertexBuffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap,
|
bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap,
|
||||||
|
@ -708,25 +798,21 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO
|
||||||
io.BackendRendererUserData = (void*)bd;
|
io.BackendRendererUserData = (void*)bd;
|
||||||
io.BackendRendererName = "imgui_impl_dx12";
|
io.BackendRendererName = "imgui_impl_dx12";
|
||||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
|
||||||
|
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
||||||
|
ImGui_ImplDX12_InitPlatformInterface();
|
||||||
|
|
||||||
bd->pd3dDevice = device;
|
bd->pd3dDevice = device;
|
||||||
bd->RTVFormat = rtv_format;
|
bd->RTVFormat = rtv_format;
|
||||||
bd->hFontSrvCpuDescHandle = font_srv_cpu_desc_handle;
|
bd->hFontSrvCpuDescHandle = font_srv_cpu_desc_handle;
|
||||||
bd->hFontSrvGpuDescHandle = font_srv_gpu_desc_handle;
|
bd->hFontSrvGpuDescHandle = font_srv_gpu_desc_handle;
|
||||||
bd->pFrameResources = new ImGui_ImplDX12_RenderBuffers[num_frames_in_flight];
|
|
||||||
bd->numFramesInFlight = num_frames_in_flight;
|
bd->numFramesInFlight = num_frames_in_flight;
|
||||||
bd->frameIndex = UINT_MAX;
|
bd->pd3dSrvDescHeap = cbv_srv_heap;
|
||||||
IM_UNUSED(cbv_srv_heap); // Unused in master branch (will be used by multi-viewports)
|
|
||||||
|
|
||||||
// Create buffers with a default size (they will later be grown as needed)
|
// Create a dummy ImGui_ImplDX12_ViewportData holder for the main viewport,
|
||||||
for (int i = 0; i < num_frames_in_flight; i++)
|
// Since this is created and managed by the application, we will only use the ->Resources[] fields.
|
||||||
{
|
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
|
||||||
ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[i];
|
main_viewport->RendererUserData = IM_NEW(ImGui_ImplDX12_ViewportData)(bd->numFramesInFlight);
|
||||||
fr->IndexBuffer = nullptr;
|
|
||||||
fr->VertexBuffer = nullptr;
|
|
||||||
fr->IndexBufferSize = 10000;
|
|
||||||
fr->VertexBufferSize = 5000;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -737,10 +823,24 @@ void ImGui_ImplDX12_Shutdown()
|
||||||
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
|
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
// Manually delete main viewport render resources in-case we haven't initialized for viewports
|
||||||
|
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
|
||||||
|
if (ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)main_viewport->RendererUserData)
|
||||||
|
{
|
||||||
|
// We could just call ImGui_ImplDX12_DestroyWindow(main_viewport) as a convenience but that would be misleading since we only use data->Resources[]
|
||||||
|
for (UINT i = 0; i < bd->numFramesInFlight; i++)
|
||||||
|
ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]);
|
||||||
|
IM_DELETE(vd);
|
||||||
|
main_viewport->RendererUserData = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up windows and device objects
|
||||||
|
ImGui_ImplDX12_ShutdownPlatformInterface();
|
||||||
ImGui_ImplDX12_InvalidateDeviceObjects();
|
ImGui_ImplDX12_InvalidateDeviceObjects();
|
||||||
delete[] bd->pFrameResources;
|
|
||||||
io.BackendRendererName = nullptr;
|
io.BackendRendererName = nullptr;
|
||||||
io.BackendRendererUserData = nullptr;
|
io.BackendRendererUserData = nullptr;
|
||||||
|
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasViewports);
|
||||||
IM_DELETE(bd);
|
IM_DELETE(bd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -752,3 +852,247 @@ void ImGui_ImplDX12_NewFrame()
|
||||||
if (!bd->pPipelineState)
|
if (!bd->pPipelineState)
|
||||||
ImGui_ImplDX12_CreateDeviceObjects();
|
ImGui_ImplDX12_CreateDeviceObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------
|
||||||
|
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
|
||||||
|
// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
|
||||||
|
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
|
||||||
|
//--------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport)
|
||||||
|
{
|
||||||
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||||
|
ImGui_ImplDX12_ViewportData* vd = IM_NEW(ImGui_ImplDX12_ViewportData)(bd->numFramesInFlight);
|
||||||
|
viewport->RendererUserData = vd;
|
||||||
|
|
||||||
|
// PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*).
|
||||||
|
// Some backends will leave PlatformHandleRaw == 0, in which case we assume PlatformHandle will contain the HWND.
|
||||||
|
HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle;
|
||||||
|
IM_ASSERT(hwnd != 0);
|
||||||
|
|
||||||
|
vd->FrameIndex = UINT_MAX;
|
||||||
|
|
||||||
|
// Create command queue.
|
||||||
|
D3D12_COMMAND_QUEUE_DESC queue_desc = {};
|
||||||
|
queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||||
|
queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||||
|
|
||||||
|
HRESULT res = S_OK;
|
||||||
|
res = bd->pd3dDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&vd->CommandQueue));
|
||||||
|
IM_ASSERT(res == S_OK);
|
||||||
|
|
||||||
|
// Create command allocator.
|
||||||
|
for (UINT i = 0; i < bd->numFramesInFlight; ++i)
|
||||||
|
{
|
||||||
|
res = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&vd->FrameCtx[i].CommandAllocator));
|
||||||
|
IM_ASSERT(res == S_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create command list.
|
||||||
|
res = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, vd->FrameCtx[0].CommandAllocator, nullptr, IID_PPV_ARGS(&vd->CommandList));
|
||||||
|
IM_ASSERT(res == S_OK);
|
||||||
|
vd->CommandList->Close();
|
||||||
|
|
||||||
|
// Create fence.
|
||||||
|
res = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&vd->Fence));
|
||||||
|
IM_ASSERT(res == S_OK);
|
||||||
|
|
||||||
|
vd->FenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||||
|
IM_ASSERT(vd->FenceEvent != nullptr);
|
||||||
|
|
||||||
|
// Create swap chain
|
||||||
|
// FIXME-VIEWPORT: May want to copy/inherit swap chain settings from the user/application.
|
||||||
|
DXGI_SWAP_CHAIN_DESC1 sd1;
|
||||||
|
ZeroMemory(&sd1, sizeof(sd1));
|
||||||
|
sd1.BufferCount = bd->numFramesInFlight;
|
||||||
|
sd1.Width = (UINT)viewport->Size.x;
|
||||||
|
sd1.Height = (UINT)viewport->Size.y;
|
||||||
|
sd1.Format = bd->RTVFormat;
|
||||||
|
sd1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||||
|
sd1.SampleDesc.Count = 1;
|
||||||
|
sd1.SampleDesc.Quality = 0;
|
||||||
|
sd1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||||
|
sd1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
|
||||||
|
sd1.Scaling = DXGI_SCALING_NONE;
|
||||||
|
sd1.Stereo = FALSE;
|
||||||
|
|
||||||
|
IDXGIFactory4* dxgi_factory = nullptr;
|
||||||
|
res = ::CreateDXGIFactory1(IID_PPV_ARGS(&dxgi_factory));
|
||||||
|
IM_ASSERT(res == S_OK);
|
||||||
|
|
||||||
|
IDXGISwapChain1* swap_chain = nullptr;
|
||||||
|
res = dxgi_factory->CreateSwapChainForHwnd(vd->CommandQueue, hwnd, &sd1, nullptr, nullptr, &swap_chain);
|
||||||
|
IM_ASSERT(res == S_OK);
|
||||||
|
|
||||||
|
dxgi_factory->Release();
|
||||||
|
|
||||||
|
// Or swapChain.As(&mSwapChain)
|
||||||
|
IM_ASSERT(vd->SwapChain == nullptr);
|
||||||
|
swap_chain->QueryInterface(IID_PPV_ARGS(&vd->SwapChain));
|
||||||
|
swap_chain->Release();
|
||||||
|
|
||||||
|
// Create the render targets
|
||||||
|
if (vd->SwapChain)
|
||||||
|
{
|
||||||
|
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
|
||||||
|
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
|
||||||
|
desc.NumDescriptors = bd->numFramesInFlight;
|
||||||
|
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
|
||||||
|
desc.NodeMask = 1;
|
||||||
|
|
||||||
|
HRESULT hr = bd->pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&vd->RtvDescHeap));
|
||||||
|
IM_ASSERT(hr == S_OK);
|
||||||
|
|
||||||
|
SIZE_T rtv_descriptor_size = bd->pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
|
||||||
|
D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = vd->RtvDescHeap->GetCPUDescriptorHandleForHeapStart();
|
||||||
|
for (UINT i = 0; i < bd->numFramesInFlight; i++)
|
||||||
|
{
|
||||||
|
vd->FrameCtx[i].RenderTargetCpuDescriptors = rtv_handle;
|
||||||
|
rtv_handle.ptr += rtv_descriptor_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ID3D12Resource* back_buffer;
|
||||||
|
for (UINT i = 0; i < bd->numFramesInFlight; i++)
|
||||||
|
{
|
||||||
|
IM_ASSERT(vd->FrameCtx[i].RenderTarget == nullptr);
|
||||||
|
vd->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer));
|
||||||
|
bd->pd3dDevice->CreateRenderTargetView(back_buffer, nullptr, vd->FrameCtx[i].RenderTargetCpuDescriptors);
|
||||||
|
vd->FrameCtx[i].RenderTarget = back_buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (UINT i = 0; i < bd->numFramesInFlight; i++)
|
||||||
|
ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_WaitForPendingOperations(ImGui_ImplDX12_ViewportData* vd)
|
||||||
|
{
|
||||||
|
HRESULT hr = S_FALSE;
|
||||||
|
if (vd && vd->CommandQueue && vd->Fence && vd->FenceEvent)
|
||||||
|
{
|
||||||
|
hr = vd->CommandQueue->Signal(vd->Fence, ++vd->FenceSignaledValue);
|
||||||
|
IM_ASSERT(hr == S_OK);
|
||||||
|
::WaitForSingleObject(vd->FenceEvent, 0); // Reset any forgotten waits
|
||||||
|
hr = vd->Fence->SetEventOnCompletion(vd->FenceSignaledValue, vd->FenceEvent);
|
||||||
|
IM_ASSERT(hr == S_OK);
|
||||||
|
::WaitForSingleObject(vd->FenceEvent, INFINITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport)
|
||||||
|
{
|
||||||
|
// The main viewport (owned by the application) will always have RendererUserData == 0 since we didn't create the data for it.
|
||||||
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||||
|
if (ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData)
|
||||||
|
{
|
||||||
|
ImGui_WaitForPendingOperations(vd);
|
||||||
|
|
||||||
|
SafeRelease(vd->CommandQueue);
|
||||||
|
SafeRelease(vd->CommandList);
|
||||||
|
SafeRelease(vd->SwapChain);
|
||||||
|
SafeRelease(vd->RtvDescHeap);
|
||||||
|
SafeRelease(vd->Fence);
|
||||||
|
::CloseHandle(vd->FenceEvent);
|
||||||
|
vd->FenceEvent = nullptr;
|
||||||
|
|
||||||
|
for (UINT i = 0; i < bd->numFramesInFlight; i++)
|
||||||
|
{
|
||||||
|
SafeRelease(vd->FrameCtx[i].RenderTarget);
|
||||||
|
SafeRelease(vd->FrameCtx[i].CommandAllocator);
|
||||||
|
ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]);
|
||||||
|
}
|
||||||
|
IM_DELETE(vd);
|
||||||
|
}
|
||||||
|
viewport->RendererUserData = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
|
||||||
|
{
|
||||||
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||||
|
ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData;
|
||||||
|
|
||||||
|
ImGui_WaitForPendingOperations(vd);
|
||||||
|
|
||||||
|
for (UINT i = 0; i < bd->numFramesInFlight; i++)
|
||||||
|
SafeRelease(vd->FrameCtx[i].RenderTarget);
|
||||||
|
|
||||||
|
if (vd->SwapChain)
|
||||||
|
{
|
||||||
|
ID3D12Resource* back_buffer = nullptr;
|
||||||
|
vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0);
|
||||||
|
for (UINT i = 0; i < bd->numFramesInFlight; i++)
|
||||||
|
{
|
||||||
|
vd->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer));
|
||||||
|
bd->pd3dDevice->CreateRenderTargetView(back_buffer, nullptr, vd->FrameCtx[i].RenderTargetCpuDescriptors);
|
||||||
|
vd->FrameCtx[i].RenderTarget = back_buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*)
|
||||||
|
{
|
||||||
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||||
|
ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData;
|
||||||
|
|
||||||
|
ImGui_ImplDX12_FrameContext* frame_context = &vd->FrameCtx[vd->FrameIndex % bd->numFramesInFlight];
|
||||||
|
UINT back_buffer_idx = vd->SwapChain->GetCurrentBackBufferIndex();
|
||||||
|
|
||||||
|
const ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
D3D12_RESOURCE_BARRIER barrier = {};
|
||||||
|
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||||
|
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||||
|
barrier.Transition.pResource = vd->FrameCtx[back_buffer_idx].RenderTarget;
|
||||||
|
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||||
|
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
|
||||||
|
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||||
|
|
||||||
|
// Draw
|
||||||
|
ID3D12GraphicsCommandList* cmd_list = vd->CommandList;
|
||||||
|
|
||||||
|
frame_context->CommandAllocator->Reset();
|
||||||
|
cmd_list->Reset(frame_context->CommandAllocator, nullptr);
|
||||||
|
cmd_list->ResourceBarrier(1, &barrier);
|
||||||
|
cmd_list->OMSetRenderTargets(1, &vd->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, FALSE, nullptr);
|
||||||
|
if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
|
||||||
|
cmd_list->ClearRenderTargetView(vd->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, (float*)&clear_color, 0, nullptr);
|
||||||
|
cmd_list->SetDescriptorHeaps(1, &bd->pd3dSrvDescHeap);
|
||||||
|
|
||||||
|
ImGui_ImplDX12_RenderDrawData(viewport->DrawData, cmd_list);
|
||||||
|
|
||||||
|
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||||
|
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
|
||||||
|
cmd_list->ResourceBarrier(1, &barrier);
|
||||||
|
cmd_list->Close();
|
||||||
|
|
||||||
|
vd->CommandQueue->Wait(vd->Fence, vd->FenceSignaledValue);
|
||||||
|
vd->CommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmd_list);
|
||||||
|
vd->CommandQueue->Signal(vd->Fence, ++vd->FenceSignaledValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport, void*)
|
||||||
|
{
|
||||||
|
ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData;
|
||||||
|
|
||||||
|
vd->SwapChain->Present(0, 0);
|
||||||
|
while (vd->Fence->GetCompletedValue() < vd->FenceSignaledValue)
|
||||||
|
::SwitchToThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplDX12_InitPlatformInterface()
|
||||||
|
{
|
||||||
|
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||||
|
platform_io.Renderer_CreateWindow = ImGui_ImplDX12_CreateWindow;
|
||||||
|
platform_io.Renderer_DestroyWindow = ImGui_ImplDX12_DestroyWindow;
|
||||||
|
platform_io.Renderer_SetWindowSize = ImGui_ImplDX12_SetWindowSize;
|
||||||
|
platform_io.Renderer_RenderWindow = ImGui_ImplDX12_RenderWindow;
|
||||||
|
platform_io.Renderer_SwapBuffers = ImGui_ImplDX12_SwapBuffers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplDX12_ShutdownPlatformInterface()
|
||||||
|
{
|
||||||
|
ImGui::DestroyPlatformWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
||||||
|
|
|
@ -3,18 +3,23 @@
|
||||||
|
|
||||||
// Implemented features:
|
// Implemented features:
|
||||||
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
|
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
|
||||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices.
|
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||||
|
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
|
||||||
|
|
||||||
// Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
|
// Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
|
||||||
// See imgui_impl_dx12.cpp file for details.
|
// See imgui_impl_dx12.cpp file for details.
|
||||||
|
|
||||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
// Learn about Dear ImGui:
|
||||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "imgui.h" // IMGUI_IMPL_API
|
#include "imgui.h" // IMGUI_IMPL_API
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
#include <dxgiformat.h> // DXGI_FORMAT
|
#include <dxgiformat.h> // DXGI_FORMAT
|
||||||
|
|
||||||
struct ID3D12Device;
|
struct ID3D12Device;
|
||||||
|
@ -23,7 +28,8 @@ struct ID3D12GraphicsCommandList;
|
||||||
struct D3D12_CPU_DESCRIPTOR_HANDLE;
|
struct D3D12_CPU_DESCRIPTOR_HANDLE;
|
||||||
struct D3D12_GPU_DESCRIPTOR_HANDLE;
|
struct D3D12_GPU_DESCRIPTOR_HANDLE;
|
||||||
|
|
||||||
extern "C" { // mziulek
|
// FIX(zig-gamedev)
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
// cmd_list is the command list that the implementation will use to render imgui draw lists.
|
// cmd_list is the command list that the implementation will use to render imgui draw lists.
|
||||||
// Before calling the render function, caller must prepare cmd_list by resetting it and setting the appropriate
|
// Before calling the render function, caller must prepare cmd_list by resetting it and setting the appropriate
|
||||||
|
@ -40,3 +46,5 @@ IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects();
|
||||||
IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects();
|
IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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.
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
// Learn about Dear ImGui:
|
||||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
// CHANGELOG
|
// CHANGELOG
|
||||||
// (minor and older changes stripped away, please see git history for details)
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2024-01-09: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" and variants, fixing regression on distros missing a symlink.
|
||||||
|
// 2023-11-08: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" instead of "libGL.so.1", accommodating for NetBSD systems having only "libGL.so.3" available. (#6983)
|
||||||
|
// 2023-10-05: OpenGL: Rename symbols in our internal loader so that LTO compilation with another copy of gl3w is possible. (#6875, #6668, #4445)
|
||||||
// 2023-06-20: OpenGL: Fixed erroneous use glGetIntegerv(GL_CONTEXT_PROFILE_MASK) on contexts lower than 3.2. (#6539, #6333)
|
// 2023-06-20: OpenGL: Fixed erroneous use glGetIntegerv(GL_CONTEXT_PROFILE_MASK) on contexts lower than 3.2. (#6539, #6333)
|
||||||
// 2023-05-09: OpenGL: Support for glBindSampler() backup/restore on ES3. (#6375)
|
// 2023-05-09: OpenGL: Support for glBindSampler() backup/restore on ES3. (#6375)
|
||||||
// 2023-04-18: OpenGL: Restore front and back polygon mode separately when supported by context. (#6333)
|
// 2023-04-18: OpenGL: Restore front and back polygon mode separately when supported by context. (#6333)
|
||||||
|
@ -103,9 +109,8 @@
|
||||||
#define _CRT_SECURE_NO_WARNINGS
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef IMGUI_DISABLE
|
|
||||||
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
#include "imgui_impl_opengl3.h"
|
#include "imgui_impl_opengl3.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h> // intptr_t
|
#include <stdint.h> // intptr_t
|
||||||
|
@ -175,9 +180,20 @@ extern "C" {
|
||||||
#define GL_VERTEX_ARRAY_BINDING GL_VERTEX_ARRAY_BINDING_OES
|
#define GL_VERTEX_ARRAY_BINDING GL_VERTEX_ARRAY_BINDING_OES
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Desktop GL 2.0+ has glPolygonMode() which GL ES and WebGL don't have.
|
// Desktop GL 2.0+ has extension and glPolygonMode() which GL ES and WebGL don't have..
|
||||||
#ifdef GL_POLYGON_MODE
|
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)
|
||||||
#define IMGUI_IMPL_HAS_POLYGON_MODE
|
#define IMGUI_IMPL_OPENGL_HAS_EXTENSIONS // has glGetIntegerv(GL_NUM_EXTENSIONS)
|
||||||
|
#define IMGUI_IMPL_OPENGL_HAS_POLYGON_MODE // has glPolygonMode()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Desktop GL 2.1+ and GL ES 3.0+ have glBindBuffer() with GL_PIXEL_UNPACK_BUFFER target.
|
||||||
|
#if !defined(IMGUI_IMPL_OPENGL_ES2)
|
||||||
|
#define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Desktop GL 3.1+ has GL_PRIMITIVE_RESTART state
|
||||||
|
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_1)
|
||||||
|
#define IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
|
// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
|
||||||
|
@ -190,16 +206,6 @@ extern "C" {
|
||||||
#define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
#define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Desktop GL 3.1+ has GL_PRIMITIVE_RESTART state
|
|
||||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_1)
|
|
||||||
#define IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Desktop GL use extension detection
|
|
||||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)
|
|
||||||
#define IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// [Debugging]
|
// [Debugging]
|
||||||
//#define IMGUI_IMPL_OPENGL_DEBUG
|
//#define IMGUI_IMPL_OPENGL_DEBUG
|
||||||
#ifdef IMGUI_IMPL_OPENGL_DEBUG
|
#ifdef IMGUI_IMPL_OPENGL_DEBUG
|
||||||
|
@ -272,13 +278,13 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
|
||||||
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
|
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
|
||||||
|
|
||||||
// Initialize our loader
|
// Initialize our loader
|
||||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
|
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
|
||||||
if (imgl3wInit() != 0)
|
if (imgl3wInit() != 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Failed to initialize OpenGL loader!\n");
|
fprintf(stderr, "Failed to initialize OpenGL loader!\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Setup backend capabilities flags
|
// Setup backend capabilities flags
|
||||||
ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)();
|
ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)();
|
||||||
|
@ -358,7 +364,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
|
||||||
|
|
||||||
// Detect extensions we support
|
// Detect extensions we support
|
||||||
bd->HasClipOrigin = (bd->GlVersion >= 450);
|
bd->HasClipOrigin = (bd->GlVersion >= 450);
|
||||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS
|
#ifdef IMGUI_IMPL_OPENGL_HAS_EXTENSIONS
|
||||||
GLint num_extensions = 0;
|
GLint num_extensions = 0;
|
||||||
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
|
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
|
||||||
for (GLint i = 0; i < num_extensions; i++)
|
for (GLint i = 0; i < num_extensions; i++)
|
||||||
|
@ -410,7 +416,7 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
|
||||||
if (bd->GlVersion >= 310)
|
if (bd->GlVersion >= 310)
|
||||||
glDisable(GL_PRIMITIVE_RESTART);
|
glDisable(GL_PRIMITIVE_RESTART);
|
||||||
#endif
|
#endif
|
||||||
#ifdef IMGUI_IMPL_HAS_POLYGON_MODE
|
#ifdef IMGUI_IMPL_OPENGL_HAS_POLYGON_MODE
|
||||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -462,9 +468,9 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
|
||||||
GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxPos));
|
GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxPos));
|
||||||
GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxUV));
|
GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxUV));
|
||||||
GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxColor));
|
GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxColor));
|
||||||
GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)));
|
GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, pos)));
|
||||||
GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)));
|
GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, uv)));
|
||||||
GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)));
|
GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, col)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenGL3 Render function.
|
// OpenGL3 Render function.
|
||||||
|
@ -499,7 +505,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||||
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||||
GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object);
|
GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object);
|
||||||
#endif
|
#endif
|
||||||
#ifdef IMGUI_IMPL_HAS_POLYGON_MODE
|
#ifdef IMGUI_IMPL_OPENGL_HAS_POLYGON_MODE
|
||||||
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
|
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
|
||||||
#endif
|
#endif
|
||||||
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
|
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
|
||||||
|
@ -638,7 +644,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||||
if (bd->GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); }
|
if (bd->GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef IMGUI_IMPL_HAS_POLYGON_MODE
|
#ifdef IMGUI_IMPL_OPENGL_HAS_POLYGON_MODE
|
||||||
// Desktop OpenGL 3.0 and OpenGL 3.1 had separate polygon draw modes for front-facing and back-facing faces of polygons
|
// Desktop OpenGL 3.0 and OpenGL 3.1 had separate polygon draw modes for front-facing and back-facing faces of polygons
|
||||||
if (bd->GlVersion <= 310 || bd->GlProfileIsCompat)
|
if (bd->GlVersion <= 310 || bd->GlProfileIsCompat)
|
||||||
{
|
{
|
||||||
|
@ -649,7 +655,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||||
{
|
{
|
||||||
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
|
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
|
||||||
}
|
}
|
||||||
#endif // IMGUI_IMPL_HAS_POLYGON_MODE
|
#endif // IMGUI_IMPL_OPENGL_HAS_POLYGON_MODE
|
||||||
|
|
||||||
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
|
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
|
||||||
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
|
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
|
||||||
|
@ -746,6 +752,10 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
|
||||||
GLint last_texture, last_array_buffer;
|
GLint last_texture, last_array_buffer;
|
||||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
||||||
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
|
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK
|
||||||
|
GLint last_pixel_unpack_buffer;
|
||||||
|
if (bd->GlVersion >= 210) { glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &last_pixel_unpack_buffer); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); }
|
||||||
|
#endif
|
||||||
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||||
GLint last_vertex_array;
|
GLint last_vertex_array;
|
||||||
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
|
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
|
||||||
|
@ -919,6 +929,9 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
|
||||||
// Restore modified GL state
|
// Restore modified GL state
|
||||||
glBindTexture(GL_TEXTURE_2D, last_texture);
|
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
|
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK
|
||||||
|
if (bd->GlVersion >= 210) { glBindBuffer(GL_PIXEL_UNPACK_BUFFER, last_pixel_unpack_buffer); }
|
||||||
|
#endif
|
||||||
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||||
glBindVertexArray(last_vertex_array);
|
glBindVertexArray(last_vertex_array);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -14,8 +14,11 @@
|
||||||
|
|
||||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
// Learn about Dear ImGui:
|
||||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
// About GLSL version:
|
// About GLSL version:
|
||||||
// The 'glsl_version' initialization parameter should be nullptr (default) or a "#version XXX" string.
|
// The 'glsl_version' initialization parameter should be nullptr (default) or a "#version XXX" string.
|
||||||
|
@ -26,20 +29,19 @@
|
||||||
#include "imgui.h" // IMGUI_IMPL_API
|
#include "imgui.h" // IMGUI_IMPL_API
|
||||||
#ifndef IMGUI_DISABLE
|
#ifndef IMGUI_DISABLE
|
||||||
|
|
||||||
|
// FIX(zig-gamedev)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
// Backend API
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
|
||||||
|
|
||||||
// Backend API
|
// (Optional) Called by Init/NewFrame/Shutdown
|
||||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr);
|
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
|
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
|
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
|
||||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
|
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
||||||
|
|
||||||
// (Optional) Called by Init/NewFrame/Shutdown
|
|
||||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
|
|
||||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
|
|
||||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
|
|
||||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specific OpenGL ES versions
|
// Specific OpenGL ES versions
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// zig-gamedev changes marked with `FIX(zig-gamedev)`
|
|
||||||
|
|
||||||
// dear imgui: Renderer for WebGPU
|
// dear imgui: Renderer for WebGPU
|
||||||
// This needs to be used along with a Platform Binding (e.g. GLFW)
|
// This needs to be used along with a Platform Binding (e.g. GLFW)
|
||||||
// (Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break.)
|
// (Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break.)
|
||||||
|
@ -7,14 +5,24 @@
|
||||||
// Implemented features:
|
// Implemented features:
|
||||||
// [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID!
|
// [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID!
|
||||||
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||||
|
// Missing features:
|
||||||
|
// [ ] Renderer: Multi-viewport support (multiple windows). Not meaningful on the web.
|
||||||
|
|
||||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
// Learn about Dear ImGui:
|
||||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
// CHANGELOG
|
// CHANGELOG
|
||||||
// (minor and older changes stripped away, please see git history for details)
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2024-01-22: Added configurable PipelineMultisampleState struct. (#7240)
|
||||||
|
// 2024-01-22: (Breaking) ImGui_ImplWGPU_Init() now takes a ImGui_ImplWGPU_InitInfo structure instead of variety of parameters, allowing for easier further changes.
|
||||||
|
// 2024-01-22: Fixed pipeline layout leak. (#7245)
|
||||||
|
// 2024-01-17: Explicitly fill all of WGPUDepthStencilState since standard removed defaults.
|
||||||
|
// 2023-07-13: Use WGPUShaderModuleWGSLDescriptor's code instead of source. use WGPUMipmapFilterMode_Linear instead of WGPUFilterMode_Linear. (#6602)
|
||||||
// 2023-04-11: Align buffer sizes. Use WGSL shaders instead of precompiled SPIR-V.
|
// 2023-04-11: Align buffer sizes. Use WGSL shaders instead of precompiled SPIR-V.
|
||||||
// 2023-04-11: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
// 2023-04-11: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||||
// 2023-01-25: Revert automatic pipeline layout generation (see https://github.com/gpuweb/gpuweb/issues/2470)
|
// 2023-01-25: Revert automatic pipeline layout generation (see https://github.com/gpuweb/gpuweb/issues/2470)
|
||||||
|
@ -30,6 +38,11 @@
|
||||||
// 2021-01-28: Initial version.
|
// 2021-01-28: Initial version.
|
||||||
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
|
||||||
|
// FIX(zig-gamedev):
|
||||||
|
//#include "imgui_impl_wgpu.h"
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <webgpu/webgpu.h>
|
#include <webgpu/webgpu.h>
|
||||||
|
|
||||||
|
@ -40,14 +53,31 @@ extern ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed = 0);
|
||||||
// FIX(zig-gamedev): We removed header file and declare all our external functions here.
|
// FIX(zig-gamedev): We removed header file and declare all our external functions here.
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
IMGUI_IMPL_API bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format, WGPUTextureFormat depth_format = WGPUTextureFormat_Undefined);
|
// Initialization data, for ImGui_ImplWGPU_Init()
|
||||||
IMGUI_IMPL_API void ImGui_ImplWGPU_Shutdown(void);
|
struct ImGui_ImplWGPU_InitInfo
|
||||||
IMGUI_IMPL_API void ImGui_ImplWGPU_NewFrame(void);
|
{
|
||||||
|
WGPUDevice Device;
|
||||||
|
int NumFramesInFlight = 3;
|
||||||
|
WGPUTextureFormat RenderTargetFormat = WGPUTextureFormat_Undefined;
|
||||||
|
WGPUTextureFormat DepthStencilFormat = WGPUTextureFormat_Undefined;
|
||||||
|
WGPUMultisampleState PipelineMultisampleState = {};
|
||||||
|
|
||||||
|
ImGui_ImplWGPU_InitInfo()
|
||||||
|
{
|
||||||
|
PipelineMultisampleState.count = 1;
|
||||||
|
PipelineMultisampleState.mask = -1u;
|
||||||
|
PipelineMultisampleState.alphaToCoverageEnabled = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWGPU_Shutdown();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWGPU_NewFrame();
|
||||||
IMGUI_IMPL_API void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder);
|
IMGUI_IMPL_API void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder);
|
||||||
|
|
||||||
// Use if you want to reset your rendering device without losing Dear ImGui state.
|
// Use if you want to reset your rendering device without losing Dear ImGui state.
|
||||||
IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects(void);
|
IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects();
|
||||||
IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects(void);
|
IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects();
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
||||||
|
@ -82,16 +112,17 @@ struct Uniforms
|
||||||
|
|
||||||
struct ImGui_ImplWGPU_Data
|
struct ImGui_ImplWGPU_Data
|
||||||
{
|
{
|
||||||
WGPUDevice wgpuDevice = nullptr;
|
ImGui_ImplWGPU_InitInfo initInfo;
|
||||||
WGPUQueue defaultQueue = nullptr;
|
WGPUDevice wgpuDevice = nullptr;
|
||||||
WGPUTextureFormat renderTargetFormat = WGPUTextureFormat_Undefined;
|
WGPUQueue defaultQueue = nullptr;
|
||||||
WGPUTextureFormat depthStencilFormat = WGPUTextureFormat_Undefined;
|
WGPUTextureFormat renderTargetFormat = WGPUTextureFormat_Undefined;
|
||||||
WGPURenderPipeline pipelineState = nullptr;
|
WGPUTextureFormat depthStencilFormat = WGPUTextureFormat_Undefined;
|
||||||
|
WGPURenderPipeline pipelineState = nullptr;
|
||||||
|
|
||||||
RenderResources renderResources;
|
RenderResources renderResources;
|
||||||
FrameResources* pFrameResources = nullptr;
|
FrameResources* pFrameResources = nullptr;
|
||||||
unsigned int numFramesInFlight = 0;
|
unsigned int numFramesInFlight = 0;
|
||||||
unsigned int frameIndex = UINT_MAX;
|
unsigned int frameIndex = UINT_MAX;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||||
|
@ -189,13 +220,12 @@ static void SafeRelease(WGPUBuffer& res)
|
||||||
wgpuBufferRelease(res);
|
wgpuBufferRelease(res);
|
||||||
res = nullptr;
|
res = nullptr;
|
||||||
}
|
}
|
||||||
// FIX(zig-gamedev): https://github.com/ocornut/imgui/commit/9266c0d2d1390e50d2d8070896932c2564594407
|
|
||||||
static void SafeRelease(WGPUPipelineLayout& res)
|
static void SafeRelease(WGPUPipelineLayout& res)
|
||||||
{
|
{
|
||||||
if (res)
|
if (res)
|
||||||
wgpuPipelineLayoutRelease(res);
|
wgpuPipelineLayoutRelease(res);
|
||||||
res = nullptr;
|
res = nullptr;
|
||||||
}
|
}
|
||||||
static void SafeRelease(WGPURenderPipeline& res)
|
static void SafeRelease(WGPURenderPipeline& res)
|
||||||
{
|
{
|
||||||
if (res)
|
if (res)
|
||||||
|
@ -252,7 +282,6 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const c
|
||||||
|
|
||||||
WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
|
WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
|
||||||
wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
|
wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
|
||||||
// FiX(zig-gamedev): `.source` renamed to `.code`
|
|
||||||
wgsl_desc.code = wgsl_source;
|
wgsl_desc.code = wgsl_source;
|
||||||
|
|
||||||
WGPUShaderModuleDescriptor desc = {};
|
WGPUShaderModuleDescriptor desc = {};
|
||||||
|
@ -347,7 +376,9 @@ static void ImGui_ImplWGPU_SetupRenderState(ImDrawData* draw_data, WGPURenderPas
|
||||||
void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder)
|
void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder)
|
||||||
{
|
{
|
||||||
// Avoid rendering when minimized
|
// Avoid rendering when minimized
|
||||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
|
||||||
|
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
|
||||||
|
if (fb_width <= 0 || fb_height <= 0 || draw_data->CmdListsCount == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// FIXME: Assuming that this only gets called once per frame!
|
// FIXME: Assuming that this only gets called once per frame!
|
||||||
|
@ -466,15 +497,15 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
|
||||||
// Project scissor/clipping rectangles into framebuffer space
|
// Project scissor/clipping rectangles into framebuffer space
|
||||||
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
|
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
|
||||||
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
|
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
|
||||||
|
|
||||||
|
// Clamp to viewport as wgpuRenderPassEncoderSetScissorRect() won't accept values that are off bounds
|
||||||
|
if (clip_min.x < 0.0f) { clip_min.x = 0.0f; }
|
||||||
|
if (clip_min.y < 0.0f) { clip_min.y = 0.0f; }
|
||||||
|
if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; }
|
||||||
|
if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; }
|
||||||
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// FIX(zig-gamedev): Fixes 'Popups and Modal windows->Modals->Stacked modals..' from showDemoWindow().
|
|
||||||
if (clip_min.x < 0.0f) clip_min.x = 0.0f;
|
|
||||||
if (clip_min.y < 0.0f) clip_min.y = 0.0f;
|
|
||||||
if (clip_max.x > draw_data->DisplaySize.x) clip_max.x = draw_data->DisplaySize.x;
|
|
||||||
if (clip_max.y > draw_data->DisplaySize.y) clip_max.y = draw_data->DisplaySize.y;
|
|
||||||
|
|
||||||
// Apply scissor/clipping rectangle, Draw
|
// Apply scissor/clipping rectangle, Draw
|
||||||
wgpuRenderPassEncoderSetScissorRect(pass_encoder, (uint32_t)clip_min.x, (uint32_t)clip_min.y, (uint32_t)(clip_max.x - clip_min.x), (uint32_t)(clip_max.y - clip_min.y));
|
wgpuRenderPassEncoderSetScissorRect(pass_encoder, (uint32_t)clip_min.x, (uint32_t)clip_min.y, (uint32_t)(clip_max.x - clip_min.x), (uint32_t)(clip_max.y - clip_min.y));
|
||||||
wgpuRenderPassEncoderDrawIndexed(pass_encoder, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
|
wgpuRenderPassEncoderDrawIndexed(pass_encoder, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
|
||||||
|
@ -540,7 +571,6 @@ static void ImGui_ImplWGPU_CreateFontsTexture()
|
||||||
WGPUSamplerDescriptor sampler_desc = {};
|
WGPUSamplerDescriptor sampler_desc = {};
|
||||||
sampler_desc.minFilter = WGPUFilterMode_Linear;
|
sampler_desc.minFilter = WGPUFilterMode_Linear;
|
||||||
sampler_desc.magFilter = WGPUFilterMode_Linear;
|
sampler_desc.magFilter = WGPUFilterMode_Linear;
|
||||||
// FIX(zig-gamedev): WGPUFilterMode_Linear should be WGPUMipmapFilterMode_Linear
|
|
||||||
sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Linear;
|
sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Linear;
|
||||||
sampler_desc.addressModeU = WGPUAddressMode_Repeat;
|
sampler_desc.addressModeU = WGPUAddressMode_Repeat;
|
||||||
sampler_desc.addressModeV = WGPUAddressMode_Repeat;
|
sampler_desc.addressModeV = WGPUAddressMode_Repeat;
|
||||||
|
@ -582,9 +612,7 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
|
||||||
graphics_pipeline_desc.primitive.stripIndexFormat = WGPUIndexFormat_Undefined;
|
graphics_pipeline_desc.primitive.stripIndexFormat = WGPUIndexFormat_Undefined;
|
||||||
graphics_pipeline_desc.primitive.frontFace = WGPUFrontFace_CW;
|
graphics_pipeline_desc.primitive.frontFace = WGPUFrontFace_CW;
|
||||||
graphics_pipeline_desc.primitive.cullMode = WGPUCullMode_None;
|
graphics_pipeline_desc.primitive.cullMode = WGPUCullMode_None;
|
||||||
graphics_pipeline_desc.multisample.count = 1;
|
graphics_pipeline_desc.multisample = bd->initInfo.PipelineMultisampleState;
|
||||||
graphics_pipeline_desc.multisample.mask = UINT_MAX;
|
|
||||||
graphics_pipeline_desc.multisample.alphaToCoverageEnabled = false;
|
|
||||||
|
|
||||||
// Bind group layouts
|
// Bind group layouts
|
||||||
WGPUBindGroupLayoutEntry common_bg_layout_entries[2] = {};
|
WGPUBindGroupLayoutEntry common_bg_layout_entries[2] = {};
|
||||||
|
@ -626,9 +654,9 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
|
||||||
// Vertex input configuration
|
// Vertex input configuration
|
||||||
WGPUVertexAttribute attribute_desc[] =
|
WGPUVertexAttribute attribute_desc[] =
|
||||||
{
|
{
|
||||||
{ WGPUVertexFormat_Float32x2, (uint64_t)IM_OFFSETOF(ImDrawVert, pos), 0 },
|
{ WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, pos), 0 },
|
||||||
{ WGPUVertexFormat_Float32x2, (uint64_t)IM_OFFSETOF(ImDrawVert, uv), 1 },
|
{ WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, uv), 1 },
|
||||||
{ WGPUVertexFormat_Unorm8x4, (uint64_t)IM_OFFSETOF(ImDrawVert, col), 2 },
|
{ WGPUVertexFormat_Unorm8x4, (uint64_t)offsetof(ImDrawVert, col), 2 },
|
||||||
};
|
};
|
||||||
|
|
||||||
WGPUVertexBufferLayout buffer_layouts[1];
|
WGPUVertexBufferLayout buffer_layouts[1];
|
||||||
|
@ -671,12 +699,10 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
|
||||||
depth_stencil_state.depthWriteEnabled = false;
|
depth_stencil_state.depthWriteEnabled = false;
|
||||||
depth_stencil_state.depthCompare = WGPUCompareFunction_Always;
|
depth_stencil_state.depthCompare = WGPUCompareFunction_Always;
|
||||||
depth_stencil_state.stencilFront.compare = WGPUCompareFunction_Always;
|
depth_stencil_state.stencilFront.compare = WGPUCompareFunction_Always;
|
||||||
// FIX(zig-gamedev): https://github.com/ocornut/imgui/commit/03417cc77d15100b18c486b55db409ee5e9c363e
|
|
||||||
depth_stencil_state.stencilFront.failOp = WGPUStencilOperation_Keep;
|
depth_stencil_state.stencilFront.failOp = WGPUStencilOperation_Keep;
|
||||||
depth_stencil_state.stencilFront.depthFailOp = WGPUStencilOperation_Keep;
|
depth_stencil_state.stencilFront.depthFailOp = WGPUStencilOperation_Keep;
|
||||||
depth_stencil_state.stencilFront.passOp = WGPUStencilOperation_Keep;
|
depth_stencil_state.stencilFront.passOp = WGPUStencilOperation_Keep;
|
||||||
depth_stencil_state.stencilBack.compare = WGPUCompareFunction_Always;
|
depth_stencil_state.stencilBack.compare = WGPUCompareFunction_Always;
|
||||||
// FIX(zig-gamedev): https://github.com/ocornut/imgui/commit/03417cc77d15100b18c486b55db409ee5e9c363e
|
|
||||||
depth_stencil_state.stencilBack.failOp = WGPUStencilOperation_Keep;
|
depth_stencil_state.stencilBack.failOp = WGPUStencilOperation_Keep;
|
||||||
depth_stencil_state.stencilBack.depthFailOp = WGPUStencilOperation_Keep;
|
depth_stencil_state.stencilBack.depthFailOp = WGPUStencilOperation_Keep;
|
||||||
depth_stencil_state.stencilBack.passOp = WGPUStencilOperation_Keep;
|
depth_stencil_state.stencilBack.passOp = WGPUStencilOperation_Keep;
|
||||||
|
@ -709,7 +735,6 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
|
||||||
|
|
||||||
SafeRelease(vertex_shader_desc.module);
|
SafeRelease(vertex_shader_desc.module);
|
||||||
SafeRelease(pixel_shader_desc.module);
|
SafeRelease(pixel_shader_desc.module);
|
||||||
// FIX(zig-gamedev): https://github.com/ocornut/imgui/commit/9266c0d2d1390e50d2d8070896932c2564594407
|
|
||||||
SafeRelease(graphics_pipeline_desc.layout);
|
SafeRelease(graphics_pipeline_desc.layout);
|
||||||
SafeRelease(bg_layouts[0]);
|
SafeRelease(bg_layouts[0]);
|
||||||
|
|
||||||
|
@ -732,7 +757,7 @@ void ImGui_ImplWGPU_InvalidateDeviceObjects()
|
||||||
SafeRelease(bd->pFrameResources[i]);
|
SafeRelease(bd->pFrameResources[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format, WGPUTextureFormat depth_format)
|
bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info)
|
||||||
{
|
{
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
|
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
|
||||||
|
@ -743,11 +768,12 @@ bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextur
|
||||||
io.BackendRendererName = "imgui_impl_webgpu";
|
io.BackendRendererName = "imgui_impl_webgpu";
|
||||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||||
|
|
||||||
bd->wgpuDevice = device;
|
bd->initInfo = *init_info;
|
||||||
|
bd->wgpuDevice = init_info->Device;
|
||||||
bd->defaultQueue = wgpuDeviceGetQueue(bd->wgpuDevice);
|
bd->defaultQueue = wgpuDeviceGetQueue(bd->wgpuDevice);
|
||||||
bd->renderTargetFormat = rt_format;
|
bd->renderTargetFormat = init_info->RenderTargetFormat;
|
||||||
bd->depthStencilFormat = depth_format;
|
bd->depthStencilFormat = init_info->DepthStencilFormat;
|
||||||
bd->numFramesInFlight = num_frames_in_flight;
|
bd->numFramesInFlight = init_info->NumFramesInFlight;
|
||||||
bd->frameIndex = UINT_MAX;
|
bd->frameIndex = UINT_MAX;
|
||||||
|
|
||||||
bd->renderResources.FontTexture = nullptr;
|
bd->renderResources.FontTexture = nullptr;
|
||||||
|
@ -760,8 +786,8 @@ bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextur
|
||||||
bd->renderResources.ImageBindGroupLayout = nullptr;
|
bd->renderResources.ImageBindGroupLayout = nullptr;
|
||||||
|
|
||||||
// Create buffers with a default size (they will later be grown as needed)
|
// Create buffers with a default size (they will later be grown as needed)
|
||||||
bd->pFrameResources = new FrameResources[num_frames_in_flight];
|
bd->pFrameResources = new FrameResources[bd->numFramesInFlight];
|
||||||
for (int i = 0; i < num_frames_in_flight; i++)
|
for (int i = 0; i < bd->numFramesInFlight; i++)
|
||||||
{
|
{
|
||||||
FrameResources* fr = &bd->pFrameResources[i];
|
FrameResources* fr = &bd->pFrameResources[i];
|
||||||
fr->IndexBuffer = nullptr;
|
fr->IndexBuffer = nullptr;
|
||||||
|
@ -801,3 +827,7 @@ void ImGui_ImplWGPU_NewFrame()
|
||||||
if (!bd->pipelineState)
|
if (!bd->pipelineState)
|
||||||
ImGui_ImplWGPU_CreateDeviceObjects();
|
ImGui_ImplWGPU_CreateDeviceObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
||||||
|
|
|
@ -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:
|
// Implemented features:
|
||||||
// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
|
// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
|
||||||
|
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
|
||||||
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
||||||
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||||
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||||
|
// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
|
||||||
|
|
||||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
// Learn about Dear ImGui:
|
||||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "imgui_impl_win32.h"
|
#ifndef IMGUI_DISABLE
|
||||||
|
// FIX(zig-gamedev):
|
||||||
|
// #include "imgui_impl_win32.h"
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#endif
|
#endif
|
||||||
|
@ -32,8 +39,48 @@ typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILIT
|
||||||
typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*);
|
typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// FIX(zig-gamedev):
|
||||||
|
extern "C" {
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd);
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame();
|
||||||
|
|
||||||
|
// Win32 message handler your application need to call.
|
||||||
|
// - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on <windows.h> from this helper.
|
||||||
|
// - You should COPY the line below into your .cpp code to forward declare the function and then you can call it.
|
||||||
|
// - Call from your application's message handler. Keep calling your message handler unless this function returns TRUE.
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// DPI-related helpers (optional)
|
||||||
|
// - Use to enable DPI awareness without having to create an application manifest.
|
||||||
|
// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps.
|
||||||
|
// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc.
|
||||||
|
// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime,
|
||||||
|
// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies.
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness();
|
||||||
|
IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd
|
||||||
|
IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor
|
||||||
|
|
||||||
|
// Transparency related helpers (optional) [experimental]
|
||||||
|
// - Use to enable alpha compositing transparency with the desktop.
|
||||||
|
// - Use together with e.g. clearing your framebuffer with zero-alpha.
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd
|
||||||
|
}; // extern "C"
|
||||||
|
|
||||||
// CHANGELOG
|
// CHANGELOG
|
||||||
// (minor and older changes stripped away, please see git history for details)
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
|
||||||
|
// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys.
|
||||||
|
// 2023-09-25: Inputs: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it (same behavior as GLFW/SDL).
|
||||||
|
// 2023-09-07: Inputs: Added support for keyboard codepage conversion for when application is compiled in MBCS mode and using a non-Unicode window.
|
||||||
|
// 2023-04-19: Added ImGui_ImplWin32_InitForOpenGL() to facilitate combining raw Win32/Winapi with OpenGL. (#3218)
|
||||||
|
// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen. (#2702)
|
||||||
|
// 2023-02-15: Inputs: Use WM_NCMOUSEMOVE / WM_NCMOUSELEAVE to track mouse position over non-client area (e.g. OS decorations) when app is not focused. (#6045, #6162)
|
||||||
|
// 2023-02-02: Inputs: Flipping WM_MOUSEHWHEEL (horizontal mouse-wheel) value to match other backends and offer consistent horizontal scrolling direction. (#4019, #6096, #1463)
|
||||||
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
||||||
// 2022-09-28: Inputs: Convert WM_CHAR values with MultiByteToWideChar() when window class was registered as MBCS (not Unicode).
|
// 2022-09-28: Inputs: Convert WM_CHAR values with MultiByteToWideChar() when window class was registered as MBCS (not Unicode).
|
||||||
// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).
|
// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).
|
||||||
|
@ -79,15 +126,22 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*);
|
||||||
// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging.
|
// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging.
|
||||||
// 2016-11-12: Inputs: Only call Win32 ::SetCursor(nullptr) when io.MouseDrawCursor is set.
|
// 2016-11-12: Inputs: Only call Win32 ::SetCursor(nullptr) when io.MouseDrawCursor is set.
|
||||||
|
|
||||||
|
// Forward Declarations
|
||||||
|
static void ImGui_ImplWin32_InitPlatformInterface(bool platformHasOwnDC);
|
||||||
|
static void ImGui_ImplWin32_ShutdownPlatformInterface();
|
||||||
|
static void ImGui_ImplWin32_UpdateMonitors();
|
||||||
|
|
||||||
struct ImGui_ImplWin32_Data
|
struct ImGui_ImplWin32_Data
|
||||||
{
|
{
|
||||||
HWND hWnd;
|
HWND hWnd;
|
||||||
HWND MouseHwnd;
|
HWND MouseHwnd;
|
||||||
bool MouseTracked;
|
int MouseTrackedArea; // 0: not tracked, 1: client are, 2: non-client area
|
||||||
int MouseButtonsDown;
|
int MouseButtonsDown;
|
||||||
INT64 Time;
|
INT64 Time;
|
||||||
INT64 TicksPerSecond;
|
INT64 TicksPerSecond;
|
||||||
ImGuiMouseCursor LastMouseCursor;
|
ImGuiMouseCursor LastMouseCursor;
|
||||||
|
UINT32 KeyboardCodePage;
|
||||||
|
bool WantUpdateMonitors;
|
||||||
|
|
||||||
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
bool HasGamepad;
|
bool HasGamepad;
|
||||||
|
@ -110,7 +164,17 @@ static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
bool ImGui_ImplWin32_Init(void* hwnd)
|
static void ImGui_ImplWin32_UpdateKeyboardCodePage()
|
||||||
|
{
|
||||||
|
// Retrieve keyboard code page, required for handling of non-Unicode Windows.
|
||||||
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
|
HKL keyboard_layout = ::GetKeyboardLayout(0);
|
||||||
|
LCID keyboard_lcid = MAKELCID(HIWORD(keyboard_layout), SORT_DEFAULT);
|
||||||
|
if (::GetLocaleInfoA(keyboard_lcid, (LOCALE_RETURN_NUMBER | LOCALE_IDEFAULTANSICODEPAGE), (LPSTR)&bd->KeyboardCodePage, sizeof(bd->KeyboardCodePage)) == 0)
|
||||||
|
bd->KeyboardCodePage = CP_ACP; // Fallback to default ANSI code page when fails.
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc)
|
||||||
{
|
{
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
|
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
|
||||||
|
@ -127,14 +191,21 @@ bool ImGui_ImplWin32_Init(void* hwnd)
|
||||||
io.BackendPlatformName = "imgui_impl_win32";
|
io.BackendPlatformName = "imgui_impl_win32";
|
||||||
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
||||||
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
|
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional)
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional)
|
||||||
|
|
||||||
bd->hWnd = (HWND)hwnd;
|
bd->hWnd = (HWND)hwnd;
|
||||||
|
bd->WantUpdateMonitors = true;
|
||||||
bd->TicksPerSecond = perf_frequency;
|
bd->TicksPerSecond = perf_frequency;
|
||||||
bd->Time = perf_counter;
|
bd->Time = perf_counter;
|
||||||
bd->LastMouseCursor = ImGuiMouseCursor_COUNT;
|
bd->LastMouseCursor = ImGuiMouseCursor_COUNT;
|
||||||
|
ImGui_ImplWin32_UpdateKeyboardCodePage();
|
||||||
|
|
||||||
// Set platform dependent data in viewport
|
// Our mouse update function expect PlatformHandle to be filled for the main viewport
|
||||||
ImGui::GetMainViewport()->PlatformHandleRaw = (void*)hwnd;
|
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
|
||||||
|
main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (void*)bd->hWnd;
|
||||||
|
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
||||||
|
ImGui_ImplWin32_InitPlatformInterface(platform_has_own_dc);
|
||||||
|
|
||||||
// Dynamically load XInput library
|
// Dynamically load XInput library
|
||||||
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
@ -160,12 +231,25 @@ bool ImGui_ImplWin32_Init(void* hwnd)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd)
|
||||||
|
{
|
||||||
|
return ImGui_ImplWin32_InitEx(hwnd, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd)
|
||||||
|
{
|
||||||
|
// OpenGL needs CS_OWNDC
|
||||||
|
return ImGui_ImplWin32_InitEx(hwnd, true);
|
||||||
|
}
|
||||||
|
|
||||||
void ImGui_ImplWin32_Shutdown()
|
void ImGui_ImplWin32_Shutdown()
|
||||||
{
|
{
|
||||||
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
|
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
ImGui_ImplWin32_ShutdownPlatformInterface();
|
||||||
|
|
||||||
// Unload XInput library
|
// Unload XInput library
|
||||||
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
if (bd->XInputDLL)
|
if (bd->XInputDLL)
|
||||||
|
@ -174,6 +258,7 @@ void ImGui_ImplWin32_Shutdown()
|
||||||
|
|
||||||
io.BackendPlatformName = nullptr;
|
io.BackendPlatformName = nullptr;
|
||||||
io.BackendPlatformUserData = nullptr;
|
io.BackendPlatformUserData = nullptr;
|
||||||
|
io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_PlatformHasViewports | ImGuiBackendFlags_HasMouseHoveredViewport);
|
||||||
IM_DELETE(bd);
|
IM_DELETE(bd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,31 +332,59 @@ static void ImGui_ImplWin32_UpdateKeyModifiers()
|
||||||
io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_APPS));
|
io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_APPS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This code supports multi-viewports (multiple OS Windows mapped into different Dear ImGui viewports)
|
||||||
|
// Because of that, it is a little more complicated than your typical single-viewport binding code!
|
||||||
static void ImGui_ImplWin32_UpdateMouseData()
|
static void ImGui_ImplWin32_UpdateMouseData()
|
||||||
{
|
{
|
||||||
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
IM_ASSERT(bd->hWnd != 0);
|
IM_ASSERT(bd->hWnd != 0);
|
||||||
|
|
||||||
const bool is_app_focused = (::GetForegroundWindow() == bd->hWnd);
|
POINT mouse_screen_pos;
|
||||||
|
bool has_mouse_screen_pos = ::GetCursorPos(&mouse_screen_pos) != 0;
|
||||||
|
|
||||||
|
HWND focused_window = ::GetForegroundWindow();
|
||||||
|
const bool is_app_focused = (focused_window && (focused_window == bd->hWnd || ::IsChild(focused_window, bd->hWnd) || ImGui::FindViewportByPlatformHandle((void*)focused_window)));
|
||||||
if (is_app_focused)
|
if (is_app_focused)
|
||||||
{
|
{
|
||||||
// (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
|
// (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
|
||||||
|
// When multi-viewports are enabled, all Dear ImGui positions are same as OS positions.
|
||||||
if (io.WantSetMousePos)
|
if (io.WantSetMousePos)
|
||||||
{
|
{
|
||||||
POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
|
POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
|
||||||
if (::ClientToScreen(bd->hWnd, &pos))
|
if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0)
|
||||||
::SetCursorPos(pos.x, pos.y);
|
::ClientToScreen(focused_window, &pos);
|
||||||
|
::SetCursorPos(pos.x, pos.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
// (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured)
|
// (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured)
|
||||||
if (!io.WantSetMousePos && !bd->MouseTracked)
|
// This also fills a short gap when clicking non-client area: WM_NCMOUSELEAVE -> modal OS move -> gap -> WM_NCMOUSEMOVE
|
||||||
|
if (!io.WantSetMousePos && bd->MouseTrackedArea == 0 && has_mouse_screen_pos)
|
||||||
{
|
{
|
||||||
POINT pos;
|
// Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)
|
||||||
if (::GetCursorPos(&pos) && ::ScreenToClient(bd->hWnd, &pos))
|
// (This is the position you can get with ::GetCursorPos() + ::ScreenToClient() or WM_MOUSEMOVE.)
|
||||||
io.AddMousePosEvent((float)pos.x, (float)pos.y);
|
// Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor)
|
||||||
|
// (This is the position you can get with ::GetCursorPos() or WM_MOUSEMOVE + ::ClientToScreen(). In theory adding viewport->Pos to a client position would also be the same.)
|
||||||
|
POINT mouse_pos = mouse_screen_pos;
|
||||||
|
if (!(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable))
|
||||||
|
::ScreenToClient(bd->hWnd, &mouse_pos);
|
||||||
|
io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering.
|
||||||
|
// If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic.
|
||||||
|
// - [X] Win32 backend correctly ignore viewports with the _NoInputs flag (here using ::WindowFromPoint with WM_NCHITTEST + HTTRANSPARENT in WndProc does that)
|
||||||
|
// Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window
|
||||||
|
// for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported
|
||||||
|
// by the backend, and use its flawed heuristic to guess the viewport behind.
|
||||||
|
// - [X] Win32 backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target).
|
||||||
|
ImGuiID mouse_viewport_id = 0;
|
||||||
|
if (has_mouse_screen_pos)
|
||||||
|
if (HWND hovered_hwnd = ::WindowFromPoint(mouse_screen_pos))
|
||||||
|
if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd))
|
||||||
|
mouse_viewport_id = viewport->ID;
|
||||||
|
io.AddMouseViewportEvent(mouse_viewport_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gamepad navigation mapping
|
// Gamepad navigation mapping
|
||||||
|
@ -331,6 +444,35 @@ static void ImGui_ImplWin32_UpdateGamepads()
|
||||||
#endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
#endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, HDC, LPRECT, LPARAM)
|
||||||
|
{
|
||||||
|
MONITORINFO info = {};
|
||||||
|
info.cbSize = sizeof(MONITORINFO);
|
||||||
|
if (!::GetMonitorInfo(monitor, &info))
|
||||||
|
return TRUE;
|
||||||
|
ImGuiPlatformMonitor imgui_monitor;
|
||||||
|
imgui_monitor.MainPos = ImVec2((float)info.rcMonitor.left, (float)info.rcMonitor.top);
|
||||||
|
imgui_monitor.MainSize = ImVec2((float)(info.rcMonitor.right - info.rcMonitor.left), (float)(info.rcMonitor.bottom - info.rcMonitor.top));
|
||||||
|
imgui_monitor.WorkPos = ImVec2((float)info.rcWork.left, (float)info.rcWork.top);
|
||||||
|
imgui_monitor.WorkSize = ImVec2((float)(info.rcWork.right - info.rcWork.left), (float)(info.rcWork.bottom - info.rcWork.top));
|
||||||
|
imgui_monitor.DpiScale = ImGui_ImplWin32_GetDpiScaleForMonitor(monitor);
|
||||||
|
imgui_monitor.PlatformHandle = (void*)monitor;
|
||||||
|
ImGuiPlatformIO& io = ImGui::GetPlatformIO();
|
||||||
|
if (info.dwFlags & MONITORINFOF_PRIMARY)
|
||||||
|
io.Monitors.push_front(imgui_monitor);
|
||||||
|
else
|
||||||
|
io.Monitors.push_back(imgui_monitor);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_UpdateMonitors()
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
|
ImGui::GetPlatformIO().Monitors.resize(0);
|
||||||
|
::EnumDisplayMonitors(nullptr, nullptr, ImGui_ImplWin32_UpdateMonitors_EnumFunc, 0);
|
||||||
|
bd->WantUpdateMonitors = false;
|
||||||
|
}
|
||||||
|
|
||||||
void ImGui_ImplWin32_NewFrame()
|
void ImGui_ImplWin32_NewFrame()
|
||||||
{
|
{
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
@ -341,6 +483,8 @@ void ImGui_ImplWin32_NewFrame()
|
||||||
RECT rect = { 0, 0, 0, 0 };
|
RECT rect = { 0, 0, 0, 0 };
|
||||||
::GetClientRect(bd->hWnd, &rect);
|
::GetClientRect(bd->hWnd, &rect);
|
||||||
io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));
|
io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));
|
||||||
|
if (bd->WantUpdateMonitors)
|
||||||
|
ImGui_ImplWin32_UpdateMonitors();
|
||||||
|
|
||||||
// Setup time step
|
// Setup time step
|
||||||
INT64 current_time = 0;
|
INT64 current_time = 0;
|
||||||
|
@ -478,6 +622,20 @@ static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam)
|
||||||
case VK_F10: return ImGuiKey_F10;
|
case VK_F10: return ImGuiKey_F10;
|
||||||
case VK_F11: return ImGuiKey_F11;
|
case VK_F11: return ImGuiKey_F11;
|
||||||
case VK_F12: return ImGuiKey_F12;
|
case VK_F12: return ImGuiKey_F12;
|
||||||
|
case VK_F13: return ImGuiKey_F13;
|
||||||
|
case VK_F14: return ImGuiKey_F14;
|
||||||
|
case VK_F15: return ImGuiKey_F15;
|
||||||
|
case VK_F16: return ImGuiKey_F16;
|
||||||
|
case VK_F17: return ImGuiKey_F17;
|
||||||
|
case VK_F18: return ImGuiKey_F18;
|
||||||
|
case VK_F19: return ImGuiKey_F19;
|
||||||
|
case VK_F20: return ImGuiKey_F20;
|
||||||
|
case VK_F21: return ImGuiKey_F21;
|
||||||
|
case VK_F22: return ImGuiKey_F22;
|
||||||
|
case VK_F23: return ImGuiKey_F23;
|
||||||
|
case VK_F24: return ImGuiKey_F24;
|
||||||
|
case VK_BROWSER_BACK: return ImGuiKey_AppBack;
|
||||||
|
case VK_BROWSER_FORWARD: return ImGuiKey_AppForward;
|
||||||
default: return ImGuiKey_None;
|
default: return ImGuiKey_None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -502,6 +660,19 @@ static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam)
|
||||||
// Copy this line into your .cpp file to forward declare the function.
|
// Copy this line into your .cpp file to forward declare the function.
|
||||||
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// See https://learn.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages
|
||||||
|
// Prefer to call this at the top of the message handler to avoid the possibility of other Win32 calls interfering with this.
|
||||||
|
static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo()
|
||||||
|
{
|
||||||
|
LPARAM extra_info = ::GetMessageExtraInfo();
|
||||||
|
if ((extra_info & 0xFFFFFF80) == 0xFF515700)
|
||||||
|
return ImGuiMouseSource_Pen;
|
||||||
|
if ((extra_info & 0xFFFFFF80) == 0xFF515780)
|
||||||
|
return ImGuiMouseSource_TouchScreen;
|
||||||
|
return ImGuiMouseSource_Mouse;
|
||||||
|
}
|
||||||
|
|
||||||
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
if (ImGui::GetCurrentContext() == nullptr)
|
if (ImGui::GetCurrentContext() == nullptr)
|
||||||
|
@ -513,27 +684,50 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
|
||||||
switch (msg)
|
switch (msg)
|
||||||
{
|
{
|
||||||
case WM_MOUSEMOVE:
|
case WM_MOUSEMOVE:
|
||||||
|
case WM_NCMOUSEMOVE:
|
||||||
|
{
|
||||||
// We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events
|
// We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events
|
||||||
|
ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
|
||||||
|
const int area = (msg == WM_MOUSEMOVE) ? 1 : 2;
|
||||||
bd->MouseHwnd = hwnd;
|
bd->MouseHwnd = hwnd;
|
||||||
if (!bd->MouseTracked)
|
if (bd->MouseTrackedArea != area)
|
||||||
{
|
{
|
||||||
TRACKMOUSEEVENT tme = { sizeof(tme), TME_LEAVE, hwnd, 0 };
|
TRACKMOUSEEVENT tme_cancel = { sizeof(tme_cancel), TME_CANCEL, hwnd, 0 };
|
||||||
::TrackMouseEvent(&tme);
|
TRACKMOUSEEVENT tme_track = { sizeof(tme_track), (DWORD)((area == 2) ? (TME_LEAVE | TME_NONCLIENT) : TME_LEAVE), hwnd, 0 };
|
||||||
bd->MouseTracked = true;
|
if (bd->MouseTrackedArea != 0)
|
||||||
|
::TrackMouseEvent(&tme_cancel);
|
||||||
|
::TrackMouseEvent(&tme_track);
|
||||||
|
bd->MouseTrackedArea = area;
|
||||||
}
|
}
|
||||||
io.AddMousePosEvent((float)GET_X_LPARAM(lParam), (float)GET_Y_LPARAM(lParam));
|
POINT mouse_pos = { (LONG)GET_X_LPARAM(lParam), (LONG)GET_Y_LPARAM(lParam) };
|
||||||
|
bool want_absolute_pos = (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) != 0;
|
||||||
|
if (msg == WM_MOUSEMOVE && want_absolute_pos) // WM_MOUSEMOVE are client-relative coordinates.
|
||||||
|
::ClientToScreen(hwnd, &mouse_pos);
|
||||||
|
if (msg == WM_NCMOUSEMOVE && !want_absolute_pos) // WM_NCMOUSEMOVE are absolute coordinates.
|
||||||
|
::ScreenToClient(hwnd, &mouse_pos);
|
||||||
|
io.AddMouseSourceEvent(mouse_source);
|
||||||
|
io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case WM_MOUSELEAVE:
|
case WM_MOUSELEAVE:
|
||||||
if (bd->MouseHwnd == hwnd)
|
case WM_NCMOUSELEAVE:
|
||||||
bd->MouseHwnd = nullptr;
|
{
|
||||||
bd->MouseTracked = false;
|
const int area = (msg == WM_MOUSELEAVE) ? 1 : 2;
|
||||||
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
|
if (bd->MouseTrackedArea == area)
|
||||||
|
{
|
||||||
|
if (bd->MouseHwnd == hwnd)
|
||||||
|
bd->MouseHwnd = nullptr;
|
||||||
|
bd->MouseTrackedArea = 0;
|
||||||
|
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
|
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
|
||||||
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
|
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
|
||||||
case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
|
case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
|
||||||
case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
|
case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
|
||||||
{
|
{
|
||||||
|
ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
|
||||||
int button = 0;
|
int button = 0;
|
||||||
if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; }
|
if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; }
|
||||||
if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; }
|
if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; }
|
||||||
|
@ -542,6 +736,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
|
||||||
if (bd->MouseButtonsDown == 0 && ::GetCapture() == nullptr)
|
if (bd->MouseButtonsDown == 0 && ::GetCapture() == nullptr)
|
||||||
::SetCapture(hwnd);
|
::SetCapture(hwnd);
|
||||||
bd->MouseButtonsDown |= 1 << button;
|
bd->MouseButtonsDown |= 1 << button;
|
||||||
|
io.AddMouseSourceEvent(mouse_source);
|
||||||
io.AddMouseButtonEvent(button, true);
|
io.AddMouseButtonEvent(button, true);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -550,6 +745,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
|
||||||
case WM_MBUTTONUP:
|
case WM_MBUTTONUP:
|
||||||
case WM_XBUTTONUP:
|
case WM_XBUTTONUP:
|
||||||
{
|
{
|
||||||
|
ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
|
||||||
int button = 0;
|
int button = 0;
|
||||||
if (msg == WM_LBUTTONUP) { button = 0; }
|
if (msg == WM_LBUTTONUP) { button = 0; }
|
||||||
if (msg == WM_RBUTTONUP) { button = 1; }
|
if (msg == WM_RBUTTONUP) { button = 1; }
|
||||||
|
@ -558,6 +754,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
|
||||||
bd->MouseButtonsDown &= ~(1 << button);
|
bd->MouseButtonsDown &= ~(1 << button);
|
||||||
if (bd->MouseButtonsDown == 0 && ::GetCapture() == hwnd)
|
if (bd->MouseButtonsDown == 0 && ::GetCapture() == hwnd)
|
||||||
::ReleaseCapture();
|
::ReleaseCapture();
|
||||||
|
io.AddMouseSourceEvent(mouse_source);
|
||||||
io.AddMouseButtonEvent(button, false);
|
io.AddMouseButtonEvent(button, false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -565,7 +762,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
|
||||||
io.AddMouseWheelEvent(0.0f, (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA);
|
io.AddMouseWheelEvent(0.0f, (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA);
|
||||||
return 0;
|
return 0;
|
||||||
case WM_MOUSEHWHEEL:
|
case WM_MOUSEHWHEEL:
|
||||||
io.AddMouseWheelEvent((float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f);
|
io.AddMouseWheelEvent(-(float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f);
|
||||||
return 0;
|
return 0;
|
||||||
case WM_KEYDOWN:
|
case WM_KEYDOWN:
|
||||||
case WM_KEYUP:
|
case WM_KEYUP:
|
||||||
|
@ -583,10 +780,14 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
|
||||||
int vk = (int)wParam;
|
int vk = (int)wParam;
|
||||||
if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED))
|
if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED))
|
||||||
vk = IM_VK_KEYPAD_ENTER;
|
vk = IM_VK_KEYPAD_ENTER;
|
||||||
|
|
||||||
// Submit key event
|
|
||||||
const ImGuiKey key = ImGui_ImplWin32_VirtualKeyToImGuiKey(vk);
|
const ImGuiKey key = ImGui_ImplWin32_VirtualKeyToImGuiKey(vk);
|
||||||
const int scancode = (int)LOBYTE(HIWORD(lParam));
|
const int scancode = (int)LOBYTE(HIWORD(lParam));
|
||||||
|
|
||||||
|
// Special behavior for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit the key down event.
|
||||||
|
if (key == ImGuiKey_PrintScreen && !is_key_down)
|
||||||
|
ImGui_ImplWin32_AddKeyEvent(key, true, vk, scancode);
|
||||||
|
|
||||||
|
// Submit key event
|
||||||
if (key != ImGuiKey_None)
|
if (key != ImGuiKey_None)
|
||||||
ImGui_ImplWin32_AddKeyEvent(key, is_key_down, vk, scancode);
|
ImGui_ImplWin32_AddKeyEvent(key, is_key_down, vk, scancode);
|
||||||
|
|
||||||
|
@ -614,6 +815,9 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
|
||||||
case WM_KILLFOCUS:
|
case WM_KILLFOCUS:
|
||||||
io.AddFocusEvent(msg == WM_SETFOCUS);
|
io.AddFocusEvent(msg == WM_SETFOCUS);
|
||||||
return 0;
|
return 0;
|
||||||
|
case WM_INPUTLANGCHANGE:
|
||||||
|
ImGui_ImplWin32_UpdateKeyboardCodePage();
|
||||||
|
return 0;
|
||||||
case WM_CHAR:
|
case WM_CHAR:
|
||||||
if (::IsWindowUnicode(hwnd))
|
if (::IsWindowUnicode(hwnd))
|
||||||
{
|
{
|
||||||
|
@ -624,7 +828,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
wchar_t wch = 0;
|
wchar_t wch = 0;
|
||||||
::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (char*)&wParam, 1, &wch, 1);
|
::MultiByteToWideChar(bd->KeyboardCodePage, MB_PRECOMPOSED, (char*)&wParam, 1, &wch, 1);
|
||||||
io.AddInputCharacter(wch);
|
io.AddInputCharacter(wch);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -639,6 +843,9 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
|
||||||
bd->WantUpdateHasGamepad = true;
|
bd->WantUpdateHasGamepad = true;
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
|
case WM_DISPLAYCHANGE:
|
||||||
|
bd->WantUpdateMonitors = true;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -703,6 +910,10 @@ typedef DPI_AWARENESS_CONTEXT(WINAPI* PFN_SetThreadDpiAwarenessContext)(DPI_AWAR
|
||||||
// Helper function to enable DPI awareness without setting up a manifest
|
// Helper function to enable DPI awareness without setting up a manifest
|
||||||
void ImGui_ImplWin32_EnableDpiAwareness()
|
void ImGui_ImplWin32_EnableDpiAwareness()
|
||||||
{
|
{
|
||||||
|
// Make sure monitors will be updated with latest correct scaling
|
||||||
|
if (ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData())
|
||||||
|
bd->WantUpdateMonitors = true;
|
||||||
|
|
||||||
if (_IsWindows10OrGreater())
|
if (_IsWindows10OrGreater())
|
||||||
{
|
{
|
||||||
static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process
|
static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process
|
||||||
|
@ -803,3 +1014,347 @@ void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------------------------------
|
||||||
|
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
|
||||||
|
// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
|
||||||
|
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
|
||||||
|
//--------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data.
|
||||||
|
struct ImGui_ImplWin32_ViewportData
|
||||||
|
{
|
||||||
|
HWND Hwnd;
|
||||||
|
HWND HwndParent;
|
||||||
|
bool HwndOwned;
|
||||||
|
DWORD DwStyle;
|
||||||
|
DWORD DwExStyle;
|
||||||
|
|
||||||
|
ImGui_ImplWin32_ViewportData() { Hwnd = HwndParent = nullptr; HwndOwned = false; DwStyle = DwExStyle = 0; }
|
||||||
|
~ImGui_ImplWin32_ViewportData() { IM_ASSERT(Hwnd == nullptr); }
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_GetWin32StyleFromViewportFlags(ImGuiViewportFlags flags, DWORD* out_style, DWORD* out_ex_style)
|
||||||
|
{
|
||||||
|
if (flags & ImGuiViewportFlags_NoDecoration)
|
||||||
|
*out_style = WS_POPUP;
|
||||||
|
else
|
||||||
|
*out_style = WS_OVERLAPPEDWINDOW;
|
||||||
|
|
||||||
|
if (flags & ImGuiViewportFlags_NoTaskBarIcon)
|
||||||
|
*out_ex_style = WS_EX_TOOLWINDOW;
|
||||||
|
else
|
||||||
|
*out_ex_style = WS_EX_APPWINDOW;
|
||||||
|
|
||||||
|
if (flags & ImGuiViewportFlags_TopMost)
|
||||||
|
*out_ex_style |= WS_EX_TOPMOST;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HWND ImGui_ImplWin32_GetHwndFromViewportID(ImGuiID viewport_id)
|
||||||
|
{
|
||||||
|
if (viewport_id != 0)
|
||||||
|
if (ImGuiViewport* viewport = ImGui::FindViewportByID(viewport_id))
|
||||||
|
return (HWND)viewport->PlatformHandle;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport)
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_ViewportData* vd = IM_NEW(ImGui_ImplWin32_ViewportData)();
|
||||||
|
viewport->PlatformUserData = vd;
|
||||||
|
|
||||||
|
// Select style and parent window
|
||||||
|
ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &vd->DwStyle, &vd->DwExStyle);
|
||||||
|
vd->HwndParent = ImGui_ImplWin32_GetHwndFromViewportID(viewport->ParentViewportId);
|
||||||
|
|
||||||
|
// Create window
|
||||||
|
RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) };
|
||||||
|
::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle);
|
||||||
|
vd->Hwnd = ::CreateWindowEx(
|
||||||
|
vd->DwExStyle, _T("ImGui Platform"), _T("Untitled"), vd->DwStyle, // Style, class name, window name
|
||||||
|
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area
|
||||||
|
vd->HwndParent, nullptr, ::GetModuleHandle(nullptr), nullptr); // Owner window, Menu, Instance, Param
|
||||||
|
vd->HwndOwned = true;
|
||||||
|
viewport->PlatformRequestResize = false;
|
||||||
|
viewport->PlatformHandle = viewport->PlatformHandleRaw = vd->Hwnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport)
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
|
if (ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData)
|
||||||
|
{
|
||||||
|
if (::GetCapture() == vd->Hwnd)
|
||||||
|
{
|
||||||
|
// Transfer capture so if we started dragging from a window that later disappears, we'll still receive the MOUSEUP event.
|
||||||
|
::ReleaseCapture();
|
||||||
|
::SetCapture(bd->hWnd);
|
||||||
|
}
|
||||||
|
if (vd->Hwnd && vd->HwndOwned)
|
||||||
|
::DestroyWindow(vd->Hwnd);
|
||||||
|
vd->Hwnd = nullptr;
|
||||||
|
IM_DELETE(vd);
|
||||||
|
}
|
||||||
|
viewport->PlatformUserData = viewport->PlatformHandle = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport)
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
|
||||||
|
IM_ASSERT(vd->Hwnd != 0);
|
||||||
|
if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing)
|
||||||
|
::ShowWindow(vd->Hwnd, SW_SHOWNA);
|
||||||
|
else
|
||||||
|
::ShowWindow(vd->Hwnd, SW_SHOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_UpdateWindow(ImGuiViewport* viewport)
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
|
||||||
|
IM_ASSERT(vd->Hwnd != 0);
|
||||||
|
|
||||||
|
// Update Win32 parent if it changed _after_ creation
|
||||||
|
// Unlike style settings derived from configuration flags, this is more likely to change for advanced apps that are manipulating ParentViewportID manually.
|
||||||
|
HWND new_parent = ImGui_ImplWin32_GetHwndFromViewportID(viewport->ParentViewportId);
|
||||||
|
if (new_parent != vd->HwndParent)
|
||||||
|
{
|
||||||
|
// Win32 windows can either have a "Parent" (for WS_CHILD window) or an "Owner" (which among other thing keeps window above its owner).
|
||||||
|
// Our Dear Imgui-side concept of parenting only mostly care about what Win32 call "Owner".
|
||||||
|
// The parent parameter of CreateWindowEx() sets up Parent OR Owner depending on WS_CHILD flag. In our case an Owner as we never use WS_CHILD.
|
||||||
|
// Calling ::SetParent() here would be incorrect: it will create a full child relation, alter coordinate system and clipping.
|
||||||
|
// Calling ::SetWindowLongPtr() with GWLP_HWNDPARENT seems correct although poorly documented.
|
||||||
|
// https://devblogs.microsoft.com/oldnewthing/20100315-00/?p=14613
|
||||||
|
vd->HwndParent = new_parent;
|
||||||
|
::SetWindowLongPtr(vd->Hwnd, GWLP_HWNDPARENT, (LONG_PTR)vd->HwndParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// (Optional) Update Win32 style if it changed _after_ creation.
|
||||||
|
// Generally they won't change unless configuration flags are changed, but advanced uses (such as manually rewriting viewport flags) make this useful.
|
||||||
|
DWORD new_style;
|
||||||
|
DWORD new_ex_style;
|
||||||
|
ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &new_style, &new_ex_style);
|
||||||
|
|
||||||
|
// Only reapply the flags that have been changed from our point of view (as other flags are being modified by Windows)
|
||||||
|
if (vd->DwStyle != new_style || vd->DwExStyle != new_ex_style)
|
||||||
|
{
|
||||||
|
// (Optional) Update TopMost state if it changed _after_ creation
|
||||||
|
bool top_most_changed = (vd->DwExStyle & WS_EX_TOPMOST) != (new_ex_style & WS_EX_TOPMOST);
|
||||||
|
HWND insert_after = top_most_changed ? ((viewport->Flags & ImGuiViewportFlags_TopMost) ? HWND_TOPMOST : HWND_NOTOPMOST) : 0;
|
||||||
|
UINT swp_flag = top_most_changed ? 0 : SWP_NOZORDER;
|
||||||
|
|
||||||
|
// Apply flags and position (since it is affected by flags)
|
||||||
|
vd->DwStyle = new_style;
|
||||||
|
vd->DwExStyle = new_ex_style;
|
||||||
|
::SetWindowLong(vd->Hwnd, GWL_STYLE, vd->DwStyle);
|
||||||
|
::SetWindowLong(vd->Hwnd, GWL_EXSTYLE, vd->DwExStyle);
|
||||||
|
RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) };
|
||||||
|
::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle); // Client to Screen
|
||||||
|
::SetWindowPos(vd->Hwnd, insert_after, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, swp_flag | SWP_NOACTIVATE | SWP_FRAMECHANGED);
|
||||||
|
::ShowWindow(vd->Hwnd, SW_SHOWNA); // This is necessary when we alter the style
|
||||||
|
viewport->PlatformRequestMove = viewport->PlatformRequestResize = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ImVec2 ImGui_ImplWin32_GetWindowPos(ImGuiViewport* viewport)
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
|
||||||
|
IM_ASSERT(vd->Hwnd != 0);
|
||||||
|
POINT pos = { 0, 0 };
|
||||||
|
::ClientToScreen(vd->Hwnd, &pos);
|
||||||
|
return ImVec2((float)pos.x, (float)pos.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos)
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
|
||||||
|
IM_ASSERT(vd->Hwnd != 0);
|
||||||
|
RECT rect = { (LONG)pos.x, (LONG)pos.y, (LONG)pos.x, (LONG)pos.y };
|
||||||
|
::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle);
|
||||||
|
::SetWindowPos(vd->Hwnd, nullptr, rect.left, rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ImVec2 ImGui_ImplWin32_GetWindowSize(ImGuiViewport* viewport)
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
|
||||||
|
IM_ASSERT(vd->Hwnd != 0);
|
||||||
|
RECT rect;
|
||||||
|
::GetClientRect(vd->Hwnd, &rect);
|
||||||
|
return ImVec2(float(rect.right - rect.left), float(rect.bottom - rect.top));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
|
||||||
|
IM_ASSERT(vd->Hwnd != 0);
|
||||||
|
RECT rect = { 0, 0, (LONG)size.x, (LONG)size.y };
|
||||||
|
::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle); // Client to Screen
|
||||||
|
::SetWindowPos(vd->Hwnd, nullptr, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_SetWindowFocus(ImGuiViewport* viewport)
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
|
||||||
|
IM_ASSERT(vd->Hwnd != 0);
|
||||||
|
::BringWindowToTop(vd->Hwnd);
|
||||||
|
::SetForegroundWindow(vd->Hwnd);
|
||||||
|
::SetFocus(vd->Hwnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImGui_ImplWin32_GetWindowFocus(ImGuiViewport* viewport)
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
|
||||||
|
IM_ASSERT(vd->Hwnd != 0);
|
||||||
|
return ::GetForegroundWindow() == vd->Hwnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImGui_ImplWin32_GetWindowMinimized(ImGuiViewport* viewport)
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
|
||||||
|
IM_ASSERT(vd->Hwnd != 0);
|
||||||
|
return ::IsIconic(vd->Hwnd) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* title)
|
||||||
|
{
|
||||||
|
// ::SetWindowTextA() doesn't properly handle UTF-8 so we explicitely convert our string.
|
||||||
|
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
|
||||||
|
IM_ASSERT(vd->Hwnd != 0);
|
||||||
|
int n = ::MultiByteToWideChar(CP_UTF8, 0, title, -1, nullptr, 0);
|
||||||
|
ImVector<wchar_t> title_w;
|
||||||
|
title_w.resize(n);
|
||||||
|
::MultiByteToWideChar(CP_UTF8, 0, title, -1, title_w.Data, n);
|
||||||
|
::SetWindowTextW(vd->Hwnd, title_w.Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_SetWindowAlpha(ImGuiViewport* viewport, float alpha)
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
|
||||||
|
IM_ASSERT(vd->Hwnd != 0);
|
||||||
|
IM_ASSERT(alpha >= 0.0f && alpha <= 1.0f);
|
||||||
|
if (alpha < 1.0f)
|
||||||
|
{
|
||||||
|
DWORD style = ::GetWindowLongW(vd->Hwnd, GWL_EXSTYLE) | WS_EX_LAYERED;
|
||||||
|
::SetWindowLongW(vd->Hwnd, GWL_EXSTYLE, style);
|
||||||
|
::SetLayeredWindowAttributes(vd->Hwnd, 0, (BYTE)(255 * alpha), LWA_ALPHA);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DWORD style = ::GetWindowLongW(vd->Hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED;
|
||||||
|
::SetWindowLongW(vd->Hwnd, GWL_EXSTYLE, style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static float ImGui_ImplWin32_GetWindowDpiScale(ImGuiViewport* viewport)
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
|
||||||
|
IM_ASSERT(vd->Hwnd != 0);
|
||||||
|
return ImGui_ImplWin32_GetDpiScaleForHwnd(vd->Hwnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME-DPI: Testing DPI related ideas
|
||||||
|
static void ImGui_ImplWin32_OnChangedViewport(ImGuiViewport* viewport)
|
||||||
|
{
|
||||||
|
(void)viewport;
|
||||||
|
#if 0
|
||||||
|
ImGuiStyle default_style;
|
||||||
|
//default_style.WindowPadding = ImVec2(0, 0);
|
||||||
|
//default_style.WindowBorderSize = 0.0f;
|
||||||
|
//default_style.ItemSpacing.y = 3.0f;
|
||||||
|
//default_style.FramePadding = ImVec2(0, 0);
|
||||||
|
default_style.ScaleAllSizes(viewport->DpiScale);
|
||||||
|
ImGuiStyle& style = ImGui::GetStyle();
|
||||||
|
style = default_style;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hWnd))
|
||||||
|
{
|
||||||
|
switch (msg)
|
||||||
|
{
|
||||||
|
case WM_CLOSE:
|
||||||
|
viewport->PlatformRequestClose = true;
|
||||||
|
return 0;
|
||||||
|
case WM_MOVE:
|
||||||
|
viewport->PlatformRequestMove = true;
|
||||||
|
break;
|
||||||
|
case WM_SIZE:
|
||||||
|
viewport->PlatformRequestResize = true;
|
||||||
|
break;
|
||||||
|
case WM_MOUSEACTIVATE:
|
||||||
|
if (viewport->Flags & ImGuiViewportFlags_NoFocusOnClick)
|
||||||
|
return MA_NOACTIVATE;
|
||||||
|
break;
|
||||||
|
case WM_NCHITTEST:
|
||||||
|
// Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() correctly. (which is optional).
|
||||||
|
// The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging.
|
||||||
|
// If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in
|
||||||
|
// your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system.
|
||||||
|
if (viewport->Flags & ImGuiViewportFlags_NoInputs)
|
||||||
|
return HTTRANSPARENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefWindowProc(hWnd, msg, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_InitPlatformInterface(bool platform_has_own_dc)
|
||||||
|
{
|
||||||
|
WNDCLASSEX wcex;
|
||||||
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
||||||
|
wcex.style = CS_HREDRAW | CS_VREDRAW | (platform_has_own_dc ? CS_OWNDC : 0);
|
||||||
|
wcex.lpfnWndProc = ImGui_ImplWin32_WndProcHandler_PlatformWindow;
|
||||||
|
wcex.cbClsExtra = 0;
|
||||||
|
wcex.cbWndExtra = 0;
|
||||||
|
wcex.hInstance = ::GetModuleHandle(nullptr);
|
||||||
|
wcex.hIcon = nullptr;
|
||||||
|
wcex.hCursor = nullptr;
|
||||||
|
wcex.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
|
||||||
|
wcex.lpszMenuName = nullptr;
|
||||||
|
wcex.lpszClassName = _T("ImGui Platform");
|
||||||
|
wcex.hIconSm = nullptr;
|
||||||
|
::RegisterClassEx(&wcex);
|
||||||
|
|
||||||
|
ImGui_ImplWin32_UpdateMonitors();
|
||||||
|
|
||||||
|
// Register platform interface (will be coupled with a renderer interface)
|
||||||
|
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||||
|
platform_io.Platform_CreateWindow = ImGui_ImplWin32_CreateWindow;
|
||||||
|
platform_io.Platform_DestroyWindow = ImGui_ImplWin32_DestroyWindow;
|
||||||
|
platform_io.Platform_ShowWindow = ImGui_ImplWin32_ShowWindow;
|
||||||
|
platform_io.Platform_SetWindowPos = ImGui_ImplWin32_SetWindowPos;
|
||||||
|
platform_io.Platform_GetWindowPos = ImGui_ImplWin32_GetWindowPos;
|
||||||
|
platform_io.Platform_SetWindowSize = ImGui_ImplWin32_SetWindowSize;
|
||||||
|
platform_io.Platform_GetWindowSize = ImGui_ImplWin32_GetWindowSize;
|
||||||
|
platform_io.Platform_SetWindowFocus = ImGui_ImplWin32_SetWindowFocus;
|
||||||
|
platform_io.Platform_GetWindowFocus = ImGui_ImplWin32_GetWindowFocus;
|
||||||
|
platform_io.Platform_GetWindowMinimized = ImGui_ImplWin32_GetWindowMinimized;
|
||||||
|
platform_io.Platform_SetWindowTitle = ImGui_ImplWin32_SetWindowTitle;
|
||||||
|
platform_io.Platform_SetWindowAlpha = ImGui_ImplWin32_SetWindowAlpha;
|
||||||
|
platform_io.Platform_UpdateWindow = ImGui_ImplWin32_UpdateWindow;
|
||||||
|
platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; // FIXME-DPI
|
||||||
|
platform_io.Platform_OnChangedViewport = ImGui_ImplWin32_OnChangedViewport; // FIXME-DPI
|
||||||
|
|
||||||
|
// Register main window handle (which is owned by the main application, not by us)
|
||||||
|
// This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports.
|
||||||
|
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
|
||||||
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
|
ImGui_ImplWin32_ViewportData* vd = IM_NEW(ImGui_ImplWin32_ViewportData)();
|
||||||
|
vd->Hwnd = bd->hWnd;
|
||||||
|
vd->HwndOwned = false;
|
||||||
|
main_viewport->PlatformUserData = vd;
|
||||||
|
main_viewport->PlatformHandle = (void*)bd->hWnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_ShutdownPlatformInterface()
|
||||||
|
{
|
||||||
|
::UnregisterClass(_T("ImGui Platform"), ::GetModuleHandle(nullptr));
|
||||||
|
ImGui::DestroyPlatformWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
||||||
|
|
|
@ -3,21 +3,26 @@
|
||||||
|
|
||||||
// Implemented features:
|
// Implemented features:
|
||||||
// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
|
// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
|
||||||
|
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
|
||||||
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
||||||
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||||
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||||
|
// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
|
||||||
|
|
||||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
// Learn about Dear ImGui:
|
||||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "imgui.h" // IMGUI_IMPL_API
|
#include "imgui.h" // IMGUI_IMPL_API
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
extern "C" { // mziulek
|
|
||||||
|
|
||||||
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd);
|
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd);
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd);
|
||||||
IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();
|
IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();
|
||||||
IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame();
|
IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame();
|
||||||
|
|
||||||
|
@ -45,4 +50,4 @@ IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); //
|
||||||
// - Use together with e.g. clearing your framebuffer with zero-alpha.
|
// - Use together with e.g. clearing your framebuffer with zero-alpha.
|
||||||
IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd
|
IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd
|
||||||
|
|
||||||
}
|
#endif // #ifndef IMGUI_DISABLE
|
||||||
|
|
|
@ -153,6 +153,8 @@ typedef khronos_intptr_t GLintptr;
|
||||||
#define GL_ARRAY_BUFFER_BINDING 0x8894
|
#define GL_ARRAY_BUFFER_BINDING 0x8894
|
||||||
#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
|
#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
|
||||||
#define GL_STREAM_DRAW 0x88E0
|
#define GL_STREAM_DRAW 0x88E0
|
||||||
|
#define GL_PIXEL_UNPACK_BUFFER 0x88EC
|
||||||
|
#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF
|
||||||
typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
|
typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
|
||||||
typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
|
typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
|
||||||
typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
|
typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
|
// DEAR IMGUI COMPILE-TIME OPTIONS
|
||||||
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
|
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
|
||||||
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
|
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
|
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
|
||||||
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
|
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
|
||||||
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
|
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
|
||||||
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
@ -26,21 +26,21 @@
|
||||||
//#define IMGUI_API __declspec( dllexport )
|
//#define IMGUI_API __declspec( dllexport )
|
||||||
//#define IMGUI_API __declspec( dllimport )
|
//#define IMGUI_API __declspec( dllimport )
|
||||||
|
|
||||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
|
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
||||||
#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions.
|
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
|
||||||
|
|
||||||
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||||
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||||
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
||||||
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
|
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
|
||||||
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowStackToolWindow() will be empty (this was called IMGUI_DISABLE_METRICS_WINDOW before 1.88).
|
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
|
||||||
|
|
||||||
//---- Don't implement some functions to reduce linkage requirements.
|
//---- Don't implement some functions to reduce linkage requirements.
|
||||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
|
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
|
||||||
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
|
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
|
||||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
|
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
|
||||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
|
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
|
||||||
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||||
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||||
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||||
|
@ -50,21 +50,24 @@
|
||||||
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||||
|
|
||||||
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||||
|
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
|
||||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||||
|
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
|
||||||
|
|
||||||
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
||||||
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||||
|
|
||||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||||
//#define IMGUI_USE_WCHAR32
|
//#define IMGUI_USE_WCHAR32
|
||||||
|
|
||||||
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
||||||
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
|
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
|
||||||
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
|
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
|
||||||
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
|
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
|
||||||
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if enabled
|
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
|
||||||
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
||||||
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
|
||||||
|
|
||||||
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
|
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
|
||||||
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
|
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
|
||||||
|
@ -75,6 +78,12 @@
|
||||||
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||||
//#define IMGUI_ENABLE_FREETYPE
|
//#define IMGUI_ENABLE_FREETYPE
|
||||||
|
|
||||||
|
//---- Use FreeType+lunasvg library to render OpenType SVG fonts (SVGinOT)
|
||||||
|
// Requires lunasvg headers to be available in the include path + program to be linked with the lunasvg library (not provided).
|
||||||
|
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
||||||
|
// (implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||||
|
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
|
||||||
//---- Use stb_truetype to build and rasterize the font atlas (default)
|
//---- Use stb_truetype to build and rasterize the font atlas (default)
|
||||||
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
|
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
|
||||||
//#define IMGUI_ENABLE_STB_TRUETYPE
|
//#define IMGUI_ENABLE_STB_TRUETYPE
|
||||||
|
@ -105,7 +114,7 @@
|
||||||
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
|
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
|
||||||
//#define ImDrawCallback MyImDrawCallback
|
//#define ImDrawCallback MyImDrawCallback
|
||||||
|
|
||||||
//---- Debug Tools: Macro to break in Debugger
|
//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
|
||||||
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
|
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
|
||||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||||
//#define IM_DEBUG_BREAK __debugbreak()
|
//#define IM_DEBUG_BREAK __debugbreak()
|
||||||
|
@ -113,10 +122,10 @@
|
||||||
//---- Debug Tools: Enable slower asserts
|
//---- Debug Tools: Enable slower asserts
|
||||||
//#define IMGUI_DEBUG_PARANOID
|
//#define IMGUI_DEBUG_PARANOID
|
||||||
|
|
||||||
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
|
//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
|
||||||
/*
|
/*
|
||||||
namespace ImGui
|
namespace ImGui
|
||||||
{
|
{
|
||||||
void MyFunction(const char* name, const MyMatrix44& v);
|
void MyFunction(const char* name, MyMatrix44* mtx);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
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)
|
// (drawing and font code)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -63,6 +63,7 @@ Index of this file:
|
||||||
#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier
|
#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier
|
||||||
#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
|
#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
|
||||||
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
|
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
|
||||||
|
#pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter
|
||||||
#elif defined(__GNUC__)
|
#elif defined(__GNUC__)
|
||||||
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
|
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
|
||||||
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
|
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
|
||||||
|
@ -134,7 +135,7 @@ namespace IMGUI_STB_NAMESPACE
|
||||||
#define STBTT_sqrt(x) ImSqrt(x)
|
#define STBTT_sqrt(x) ImSqrt(x)
|
||||||
#define STBTT_pow(x,y) ImPow(x,y)
|
#define STBTT_pow(x,y) ImPow(x,y)
|
||||||
#define STBTT_fabs(x) ImFabs(x)
|
#define STBTT_fabs(x) ImFabs(x)
|
||||||
#define STBTT_ifloor(x) ((int)ImFloorSigned(x))
|
#define STBTT_ifloor(x) ((int)ImFloor(x))
|
||||||
#define STBTT_iceil(x) ((int)ImCeil(x))
|
#define STBTT_iceil(x) ((int)ImCeil(x))
|
||||||
#define STBTT_STATIC
|
#define STBTT_STATIC
|
||||||
#define STB_TRUETYPE_IMPLEMENTATION
|
#define STB_TRUETYPE_IMPLEMENTATION
|
||||||
|
@ -213,6 +214,8 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
|
||||||
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||||||
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||||||
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
|
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
|
||||||
|
colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_HeaderActive] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f);
|
||||||
|
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
|
||||||
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
|
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
|
||||||
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
||||||
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||||
|
@ -273,6 +276,8 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
|
||||||
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||||||
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||||||
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
|
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
|
||||||
|
colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f);
|
||||||
|
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
|
||||||
colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
||||||
colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||||
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||||
|
@ -334,6 +339,8 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
|
||||||
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||||||
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||||||
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
|
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
|
||||||
|
colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f);
|
||||||
|
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
|
||||||
colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
|
colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
|
||||||
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
||||||
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||||
|
@ -385,9 +392,9 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
|
||||||
void ImDrawList::_ResetForNewFrame()
|
void ImDrawList::_ResetForNewFrame()
|
||||||
{
|
{
|
||||||
// Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory.
|
// Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory.
|
||||||
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0);
|
IM_STATIC_ASSERT(offsetof(ImDrawCmd, ClipRect) == 0);
|
||||||
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4));
|
IM_STATIC_ASSERT(offsetof(ImDrawCmd, TextureId) == sizeof(ImVec4));
|
||||||
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID));
|
IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID));
|
||||||
if (_Splitter._Count > 1)
|
if (_Splitter._Count > 1)
|
||||||
_Splitter.Merge(this);
|
_Splitter.Merge(this);
|
||||||
|
|
||||||
|
@ -474,7 +481,7 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare ClipRect, TextureId and VtxOffset with a single memcmp()
|
// Compare ClipRect, TextureId and VtxOffset with a single memcmp()
|
||||||
#define ImDrawCmd_HeaderSize (IM_OFFSETOF(ImDrawCmd, VtxOffset) + sizeof(unsigned int))
|
#define ImDrawCmd_HeaderSize (offsetof(ImDrawCmd, VtxOffset) + sizeof(unsigned int))
|
||||||
#define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset
|
#define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset
|
||||||
#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset
|
#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset
|
||||||
#define ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1) (CMD_0->IdxOffset + CMD_0->ElemCount == CMD_1->IdxOffset)
|
#define ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1) (CMD_0->IdxOffset + CMD_0->ElemCount == CMD_1->IdxOffset)
|
||||||
|
@ -560,7 +567,7 @@ int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const
|
||||||
{
|
{
|
||||||
// Automatic segment count
|
// Automatic segment count
|
||||||
const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy
|
const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy
|
||||||
if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts))
|
if (radius_idx >= 0 && radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts))
|
||||||
return _Data->CircleSegmentCounts[radius_idx]; // Use cached value
|
return _Data->CircleSegmentCounts[radius_idx]; // Use cached value
|
||||||
else
|
else
|
||||||
return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError);
|
return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError);
|
||||||
|
@ -640,7 +647,7 @@ void ImDrawList::PrimReserve(int idx_count, int vtx_count)
|
||||||
_IdxWritePtr = IdxBuffer.Data + idx_buffer_old_size;
|
_IdxWritePtr = IdxBuffer.Data + idx_buffer_old_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release the a number of reserved vertices/indices from the end of the last reservation made with PrimReserve().
|
// Release the number of reserved vertices/indices from the end of the last reservation made with PrimReserve().
|
||||||
void ImDrawList::PrimUnreserve(int idx_count, int vtx_count)
|
void ImDrawList::PrimUnreserve(int idx_count, int vtx_count)
|
||||||
{
|
{
|
||||||
IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0);
|
IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0);
|
||||||
|
@ -1190,8 +1197,8 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
|
||||||
const float a_min_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f);
|
const float a_min_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f);
|
||||||
const float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f);
|
const float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f);
|
||||||
|
|
||||||
const int a_min_sample = a_is_reverse ? (int)ImFloorSigned(a_min_sample_f) : (int)ImCeil(a_min_sample_f);
|
const int a_min_sample = a_is_reverse ? (int)ImFloor(a_min_sample_f) : (int)ImCeil(a_min_sample_f);
|
||||||
const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloorSigned(a_max_sample_f);
|
const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloor(a_max_sample_f);
|
||||||
const int a_mid_samples = a_is_reverse ? ImMax(a_min_sample - a_max_sample, 0) : ImMax(a_max_sample - a_min_sample, 0);
|
const int a_mid_samples = a_is_reverse ? ImMax(a_min_sample - a_max_sample, 0) : ImMax(a_max_sample - a_min_sample, 0);
|
||||||
|
|
||||||
const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
|
const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
|
||||||
|
@ -1216,6 +1223,27 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImDrawList::PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments)
|
||||||
|
{
|
||||||
|
if (num_segments <= 0)
|
||||||
|
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here.
|
||||||
|
|
||||||
|
_Path.reserve(_Path.Size + (num_segments + 1));
|
||||||
|
|
||||||
|
const float cos_rot = ImCos(rot);
|
||||||
|
const float sin_rot = ImSin(rot);
|
||||||
|
for (int i = 0; i <= num_segments; i++)
|
||||||
|
{
|
||||||
|
const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);
|
||||||
|
ImVec2 point(ImCos(a) * radius_x, ImSin(a) * radius_y);
|
||||||
|
const float rel_x = (point.x * cos_rot) - (point.y * sin_rot);
|
||||||
|
const float rel_y = (point.x * sin_rot) + (point.y * cos_rot);
|
||||||
|
point.x = rel_x + center.x;
|
||||||
|
point.y = rel_y + center.y;
|
||||||
|
_Path.push_back(point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t)
|
ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t)
|
||||||
{
|
{
|
||||||
float u = 1.0f - t;
|
float u = 1.0f - t;
|
||||||
|
@ -1311,33 +1339,22 @@ void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4));
|
|
||||||
static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags)
|
static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4));
|
||||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
// Obsoleted in 1.82 (from February 2021)
|
// Obsoleted in 1.82 (from February 2021). This code was stripped/simplified and mostly commented in 1.90 (from September 2023)
|
||||||
// Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All)
|
// - Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All)
|
||||||
// ~0 --> ImDrawFlags_RoundCornersAll or 0
|
if (flags == ~0) { return ImDrawFlags_RoundCornersAll; }
|
||||||
if (flags == ~0)
|
// - Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations). Read details in older version of this code.
|
||||||
return ImDrawFlags_RoundCornersAll;
|
if (flags >= 0x01 && flags <= 0x0F) { return (flags << 4); }
|
||||||
|
|
||||||
// Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations)
|
|
||||||
// 0x01 --> ImDrawFlags_RoundCornersTopLeft (VALUE 0x01 OVERLAPS ImDrawFlags_Closed but ImDrawFlags_Closed is never valid in this path!)
|
|
||||||
// 0x02 --> ImDrawFlags_RoundCornersTopRight
|
|
||||||
// 0x03 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight
|
|
||||||
// 0x04 --> ImDrawFlags_RoundCornersBotLeft
|
|
||||||
// 0x05 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBotLeft
|
|
||||||
// ...
|
|
||||||
// 0x0F --> ImDrawFlags_RoundCornersAll or 0
|
|
||||||
// (See all values in ImDrawCornerFlags_)
|
|
||||||
if (flags >= 0x01 && flags <= 0x0F)
|
|
||||||
return (flags << 4);
|
|
||||||
|
|
||||||
// We cannot support hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f'
|
// We cannot support hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f'
|
||||||
#endif
|
#endif
|
||||||
|
*/
|
||||||
// If this triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values.
|
// If this assert triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values.
|
||||||
// Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc...
|
// Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc. anyway.
|
||||||
|
// See details in 1.82 Changelog as well as 2021/03/12 and 2023/09/08 entries in "API BREAKING CHANGES" section.
|
||||||
IM_ASSERT((flags & 0x0F) == 0 && "Misuse of legacy hardcoded ImDrawCornerFlags values!");
|
IM_ASSERT((flags & 0x0F) == 0 && "Misuse of legacy hardcoded ImDrawCornerFlags values!");
|
||||||
|
|
||||||
if ((flags & ImDrawFlags_RoundCornersMask_) == 0)
|
if ((flags & ImDrawFlags_RoundCornersMask_) == 0)
|
||||||
|
@ -1348,10 +1365,12 @@ static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags)
|
||||||
|
|
||||||
void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDrawFlags flags)
|
void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDrawFlags flags)
|
||||||
{
|
{
|
||||||
flags = FixRectCornerFlags(flags);
|
if (rounding >= 0.5f)
|
||||||
rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f ) - 1.0f);
|
{
|
||||||
rounding = ImMin(rounding, ImFabs(b.y - a.y) * ( ((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f ) - 1.0f);
|
flags = FixRectCornerFlags(flags);
|
||||||
|
rounding = ImMin(rounding, ImFabs(b.x - a.x) * (((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f) - 1.0f);
|
||||||
|
rounding = ImMin(rounding, ImFabs(b.y - a.y) * (((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f) - 1.0f);
|
||||||
|
}
|
||||||
if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
|
if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
|
||||||
{
|
{
|
||||||
PathLineTo(a);
|
PathLineTo(a);
|
||||||
|
@ -1544,6 +1563,35 @@ void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, in
|
||||||
PathFillConvex(col);
|
PathFillConvex(col);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ellipse
|
||||||
|
void ImDrawList::AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot, int num_segments, float thickness)
|
||||||
|
{
|
||||||
|
if ((col & IM_COL32_A_MASK) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (num_segments <= 0)
|
||||||
|
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here.
|
||||||
|
|
||||||
|
// Because we are filling a closed shape we remove 1 from the count of segments/points
|
||||||
|
const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
|
||||||
|
PathEllipticalArcTo(center, radius_x, radius_y, rot, 0.0f, a_max, num_segments - 1);
|
||||||
|
PathStroke(col, true, thickness);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImDrawList::AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot, int num_segments)
|
||||||
|
{
|
||||||
|
if ((col & IM_COL32_A_MASK) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (num_segments <= 0)
|
||||||
|
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here.
|
||||||
|
|
||||||
|
// Because we are filling a closed shape we remove 1 from the count of segments/points
|
||||||
|
const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
|
||||||
|
PathEllipticalArcTo(center, radius_x, radius_y, rot, 0.0f, a_max, num_segments - 1);
|
||||||
|
PathFillConvex(col);
|
||||||
|
}
|
||||||
|
|
||||||
// Cubic Bezier takes 4 controls points
|
// Cubic Bezier takes 4 controls points
|
||||||
void ImDrawList::AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments)
|
void ImDrawList::AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments)
|
||||||
{
|
{
|
||||||
|
@ -1808,6 +1856,63 @@ void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx)
|
||||||
// [SECTION] ImDrawData
|
// [SECTION] ImDrawData
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void ImDrawData::Clear()
|
||||||
|
{
|
||||||
|
Valid = false;
|
||||||
|
CmdListsCount = TotalIdxCount = TotalVtxCount = 0;
|
||||||
|
CmdLists.resize(0); // The ImDrawList are NOT owned by ImDrawData but e.g. by ImGuiContext, so we don't clear them.
|
||||||
|
DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.0f, 0.0f);
|
||||||
|
OwnerViewport = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Important: 'out_list' is generally going to be draw_data->CmdLists, but may be another temporary list
|
||||||
|
// as long at it is expected that the result will be later merged into draw_data->CmdLists[].
|
||||||
|
void ImGui::AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
|
||||||
|
{
|
||||||
|
if (draw_list->CmdBuffer.Size == 0)
|
||||||
|
return;
|
||||||
|
if (draw_list->CmdBuffer.Size == 1 && draw_list->CmdBuffer[0].ElemCount == 0 && draw_list->CmdBuffer[0].UserCallback == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
|
||||||
|
// May trigger for you if you are using PrimXXX functions incorrectly.
|
||||||
|
IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
|
||||||
|
IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
|
||||||
|
if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
|
||||||
|
IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
|
||||||
|
|
||||||
|
// Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
|
||||||
|
// If this assert triggers because you are drawing lots of stuff manually:
|
||||||
|
// - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
|
||||||
|
// Be mindful that the lower-level ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents.
|
||||||
|
// - If you want large meshes with more than 64K vertices, you can either:
|
||||||
|
// (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
|
||||||
|
// Most example backends already support this from 1.71. Pre-1.71 backends won't.
|
||||||
|
// Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them.
|
||||||
|
// (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
|
||||||
|
// Most example backends already support this. For example, the OpenGL example code detect index size at compile-time:
|
||||||
|
// glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
|
||||||
|
// Your own engine or render API may use different parameters or function calls to specify index sizes.
|
||||||
|
// 2 and 4 bytes indices are generally supported by most graphics API.
|
||||||
|
// - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
|
||||||
|
// the 64K limit to split your draw commands in multiple draw lists.
|
||||||
|
if (sizeof(ImDrawIdx) == 2)
|
||||||
|
IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
|
||||||
|
|
||||||
|
// Add to output list + records state in ImDrawData
|
||||||
|
out_list->push_back(draw_list);
|
||||||
|
draw_data->CmdListsCount++;
|
||||||
|
draw_data->TotalVtxCount += draw_list->VtxBuffer.Size;
|
||||||
|
draw_data->TotalIdxCount += draw_list->IdxBuffer.Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImDrawData::AddDrawList(ImDrawList* draw_list)
|
||||||
|
{
|
||||||
|
IM_ASSERT(CmdLists.Size == CmdListsCount);
|
||||||
|
draw_list->_PopUnusedDrawCmd();
|
||||||
|
ImGui::AddDrawListToDrawDataEx(this, &CmdLists, draw_list);
|
||||||
|
}
|
||||||
|
|
||||||
// For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering!
|
// For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering!
|
||||||
void ImDrawData::DeIndexAllBuffers()
|
void ImDrawData::DeIndexAllBuffers()
|
||||||
{
|
{
|
||||||
|
@ -1832,15 +1937,9 @@ void ImDrawData::DeIndexAllBuffers()
|
||||||
// or if there is a difference between your window resolution and framebuffer resolution.
|
// or if there is a difference between your window resolution and framebuffer resolution.
|
||||||
void ImDrawData::ScaleClipRects(const ImVec2& fb_scale)
|
void ImDrawData::ScaleClipRects(const ImVec2& fb_scale)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < CmdListsCount; i++)
|
for (ImDrawList* draw_list : CmdLists)
|
||||||
{
|
for (ImDrawCmd& cmd : draw_list->CmdBuffer)
|
||||||
ImDrawList* cmd_list = CmdLists[i];
|
cmd.ClipRect = ImVec4(cmd.ClipRect.x * fb_scale.x, cmd.ClipRect.y * fb_scale.y, cmd.ClipRect.z * fb_scale.x, cmd.ClipRect.w * fb_scale.y);
|
||||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
|
||||||
{
|
|
||||||
ImDrawCmd* cmd = &cmd_list->CmdBuffer[cmd_i];
|
|
||||||
cmd->ClipRect = ImVec4(cmd->ClipRect.x * fb_scale.x, cmd->ClipRect.y * fb_scale.y, cmd->ClipRect.z * fb_scale.x, cmd->ClipRect.w * fb_scale.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -1896,6 +1995,14 @@ void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int ve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImGui::ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out)
|
||||||
|
{
|
||||||
|
ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx;
|
||||||
|
ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx;
|
||||||
|
for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex)
|
||||||
|
vertex->pos = ImRotate(vertex->pos- pivot_in, cos_a, sin_a) + pivot_out;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// [SECTION] ImFontConfig
|
// [SECTION] ImFontConfig
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -1904,10 +2011,11 @@ ImFontConfig::ImFontConfig()
|
||||||
{
|
{
|
||||||
memset(this, 0, sizeof(*this));
|
memset(this, 0, sizeof(*this));
|
||||||
FontDataOwnedByAtlas = true;
|
FontDataOwnedByAtlas = true;
|
||||||
OversampleH = 3; // FIXME: 2 may be a better default?
|
OversampleH = 2;
|
||||||
OversampleV = 1;
|
OversampleV = 1;
|
||||||
GlyphMaxAdvanceX = FLT_MAX;
|
GlyphMaxAdvanceX = FLT_MAX;
|
||||||
RasterizerMultiply = 1.0f;
|
RasterizerMultiply = 1.0f;
|
||||||
|
RasterizerDensity = 1.0f;
|
||||||
EllipsisChar = (ImWchar)-1;
|
EllipsisChar = (ImWchar)-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1981,19 +2089,19 @@ ImFontAtlas::~ImFontAtlas()
|
||||||
void ImFontAtlas::ClearInputData()
|
void ImFontAtlas::ClearInputData()
|
||||||
{
|
{
|
||||||
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
|
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
|
||||||
for (int i = 0; i < ConfigData.Size; i++)
|
for (ImFontConfig& font_cfg : ConfigData)
|
||||||
if (ConfigData[i].FontData && ConfigData[i].FontDataOwnedByAtlas)
|
if (font_cfg.FontData && font_cfg.FontDataOwnedByAtlas)
|
||||||
{
|
{
|
||||||
IM_FREE(ConfigData[i].FontData);
|
IM_FREE(font_cfg.FontData);
|
||||||
ConfigData[i].FontData = NULL;
|
font_cfg.FontData = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When clearing this we lose access to the font name and other information used to build the font.
|
// When clearing this we lose access to the font name and other information used to build the font.
|
||||||
for (int i = 0; i < Fonts.Size; i++)
|
for (ImFont* font : Fonts)
|
||||||
if (Fonts[i]->ConfigData >= ConfigData.Data && Fonts[i]->ConfigData < ConfigData.Data + ConfigData.Size)
|
if (font->ConfigData >= ConfigData.Data && font->ConfigData < ConfigData.Data + ConfigData.Size)
|
||||||
{
|
{
|
||||||
Fonts[i]->ConfigData = NULL;
|
font->ConfigData = NULL;
|
||||||
Fonts[i]->ConfigDataCount = 0;
|
font->ConfigDataCount = 0;
|
||||||
}
|
}
|
||||||
ConfigData.clear();
|
ConfigData.clear();
|
||||||
CustomRects.clear();
|
CustomRects.clear();
|
||||||
|
@ -2090,6 +2198,8 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
|
||||||
if (new_font_cfg.DstFont->EllipsisChar == (ImWchar)-1)
|
if (new_font_cfg.DstFont->EllipsisChar == (ImWchar)-1)
|
||||||
new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar;
|
new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar;
|
||||||
|
|
||||||
|
ImFontAtlasUpdateConfigDataPointers(this);
|
||||||
|
|
||||||
// Invalidate texture
|
// Invalidate texture
|
||||||
TexReady = false;
|
TexReady = false;
|
||||||
ClearTexData();
|
ClearTexData();
|
||||||
|
@ -2126,7 +2236,7 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
|
||||||
if (font_cfg.Name[0] == '\0')
|
if (font_cfg.Name[0] == '\0')
|
||||||
ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf, %dpx", (int)font_cfg.SizePixels);
|
ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf, %dpx", (int)font_cfg.SizePixels);
|
||||||
font_cfg.EllipsisChar = (ImWchar)0x0085;
|
font_cfg.EllipsisChar = (ImWchar)0x0085;
|
||||||
font_cfg.GlyphOffset.y = 1.0f * IM_FLOOR(font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units
|
font_cfg.GlyphOffset.y = 1.0f * IM_TRUNC(font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units
|
||||||
|
|
||||||
const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
|
const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
|
||||||
const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault();
|
const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault();
|
||||||
|
@ -2156,13 +2266,14 @@ ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels,
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build().
|
// NB: Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build().
|
||||||
ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
|
ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
|
||||||
{
|
{
|
||||||
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
|
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
|
||||||
ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
|
ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
|
||||||
IM_ASSERT(font_cfg.FontData == NULL);
|
IM_ASSERT(font_cfg.FontData == NULL);
|
||||||
font_cfg.FontData = ttf_data;
|
IM_ASSERT(font_data_size > 100 && "Incorrect value for font_data_size!"); // Heuristic to prevent accidentally passing a wrong value to font_data_size.
|
||||||
font_cfg.FontDataSize = ttf_size;
|
font_cfg.FontData = font_data;
|
||||||
|
font_cfg.FontDataSize = font_data_size;
|
||||||
font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels;
|
font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels;
|
||||||
if (glyph_ranges)
|
if (glyph_ranges)
|
||||||
font_cfg.GlyphRanges = glyph_ranges;
|
font_cfg.GlyphRanges = glyph_ranges;
|
||||||
|
@ -2377,7 +2488,10 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
|
||||||
const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo);
|
const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo);
|
||||||
IM_ASSERT(font_offset >= 0 && "FontData is incorrect, or FontNo cannot be found.");
|
IM_ASSERT(font_offset >= 0 && "FontData is incorrect, or FontNo cannot be found.");
|
||||||
if (!stbtt_InitFont(&src_tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset))
|
if (!stbtt_InitFont(&src_tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset))
|
||||||
|
{
|
||||||
|
IM_ASSERT(0 && "stbtt_InitFont(): failed to parse FontData. It is correct and complete? Check FontDataSize.");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Measure highest codepoints
|
// Measure highest codepoints
|
||||||
ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
|
ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
|
||||||
|
@ -2459,7 +2573,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
|
||||||
|
|
||||||
// Convert our ranges in the format stb_truetype wants
|
// Convert our ranges in the format stb_truetype wants
|
||||||
ImFontConfig& cfg = atlas->ConfigData[src_i];
|
ImFontConfig& cfg = atlas->ConfigData[src_i];
|
||||||
src_tmp.PackRange.font_size = cfg.SizePixels;
|
src_tmp.PackRange.font_size = cfg.SizePixels * cfg.RasterizerDensity;
|
||||||
src_tmp.PackRange.first_unicode_codepoint_in_range = 0;
|
src_tmp.PackRange.first_unicode_codepoint_in_range = 0;
|
||||||
src_tmp.PackRange.array_of_unicode_codepoints = src_tmp.GlyphsList.Data;
|
src_tmp.PackRange.array_of_unicode_codepoints = src_tmp.GlyphsList.Data;
|
||||||
src_tmp.PackRange.num_chars = src_tmp.GlyphsList.Size;
|
src_tmp.PackRange.num_chars = src_tmp.GlyphsList.Size;
|
||||||
|
@ -2468,7 +2582,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
|
||||||
src_tmp.PackRange.v_oversample = (unsigned char)cfg.OversampleV;
|
src_tmp.PackRange.v_oversample = (unsigned char)cfg.OversampleV;
|
||||||
|
|
||||||
// Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects)
|
// Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects)
|
||||||
const float scale = (cfg.SizePixels > 0) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels);
|
const float scale = (cfg.SizePixels > 0.0f) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels * cfg.RasterizerDensity) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels * cfg.RasterizerDensity);
|
||||||
const int padding = atlas->TexGlyphPadding;
|
const int padding = atlas->TexGlyphPadding;
|
||||||
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)
|
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)
|
||||||
{
|
{
|
||||||
|
@ -2564,12 +2678,14 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
|
||||||
int unscaled_ascent, unscaled_descent, unscaled_line_gap;
|
int unscaled_ascent, unscaled_descent, unscaled_line_gap;
|
||||||
stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);
|
stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);
|
||||||
|
|
||||||
const float ascent = ImFloor(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1));
|
const float ascent = ImTrunc(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1));
|
||||||
const float descent = ImFloor(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1));
|
const float descent = ImTrunc(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1));
|
||||||
ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
|
ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
|
||||||
const float font_off_x = cfg.GlyphOffset.x;
|
const float font_off_x = cfg.GlyphOffset.x;
|
||||||
const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent);
|
const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent);
|
||||||
|
|
||||||
|
const float inv_rasterization_scale = 1.0f / cfg.RasterizerDensity;
|
||||||
|
|
||||||
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
|
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
|
||||||
{
|
{
|
||||||
// Register glyph
|
// Register glyph
|
||||||
|
@ -2578,7 +2694,11 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
|
||||||
stbtt_aligned_quad q;
|
stbtt_aligned_quad q;
|
||||||
float unused_x = 0.0f, unused_y = 0.0f;
|
float unused_x = 0.0f, unused_y = 0.0f;
|
||||||
stbtt_GetPackedQuad(src_tmp.PackedChars, atlas->TexWidth, atlas->TexHeight, glyph_i, &unused_x, &unused_y, &q, 0);
|
stbtt_GetPackedQuad(src_tmp.PackedChars, atlas->TexWidth, atlas->TexHeight, glyph_i, &unused_x, &unused_y, &q, 0);
|
||||||
dst_font->AddGlyph(&cfg, (ImWchar)codepoint, q.x0 + font_off_x, q.y0 + font_off_y, q.x1 + font_off_x, q.y1 + font_off_y, q.s0, q.t0, q.s1, q.t1, pc.xadvance);
|
float x0 = q.x0 * inv_rasterization_scale + font_off_x;
|
||||||
|
float y0 = q.y0 * inv_rasterization_scale + font_off_y;
|
||||||
|
float x1 = q.x1 * inv_rasterization_scale + font_off_x;
|
||||||
|
float y1 = q.y1 * inv_rasterization_scale + font_off_y;
|
||||||
|
dst_font->AddGlyph(&cfg, (ImWchar)codepoint, x0, y0, x1, y1, q.s0, q.t0, q.s1, q.t1, pc.xadvance * inv_rasterization_scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2598,19 +2718,31 @@ const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype()
|
||||||
|
|
||||||
#endif // IMGUI_ENABLE_STB_TRUETYPE
|
#endif // IMGUI_ENABLE_STB_TRUETYPE
|
||||||
|
|
||||||
|
void ImFontAtlasUpdateConfigDataPointers(ImFontAtlas* atlas)
|
||||||
|
{
|
||||||
|
for (ImFontConfig& font_cfg : atlas->ConfigData)
|
||||||
|
{
|
||||||
|
ImFont* font = font_cfg.DstFont;
|
||||||
|
if (!font_cfg.MergeMode)
|
||||||
|
{
|
||||||
|
font->ConfigData = &font_cfg;
|
||||||
|
font->ConfigDataCount = 0;
|
||||||
|
}
|
||||||
|
font->ConfigDataCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent)
|
void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent)
|
||||||
{
|
{
|
||||||
if (!font_config->MergeMode)
|
if (!font_config->MergeMode)
|
||||||
{
|
{
|
||||||
font->ClearOutputData();
|
font->ClearOutputData();
|
||||||
font->FontSize = font_config->SizePixels;
|
font->FontSize = font_config->SizePixels;
|
||||||
font->ConfigData = font_config;
|
IM_ASSERT(font->ConfigData == font_config);
|
||||||
font->ConfigDataCount = 0;
|
|
||||||
font->ContainerAtlas = atlas;
|
font->ContainerAtlas = atlas;
|
||||||
font->Ascent = ascent;
|
font->Ascent = ascent;
|
||||||
font->Descent = descent;
|
font->Descent = descent;
|
||||||
}
|
}
|
||||||
font->ConfigDataCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque)
|
void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque)
|
||||||
|
@ -2757,6 +2889,13 @@ static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas)
|
||||||
// Note: this is called / shared by both the stb_truetype and the FreeType builder
|
// Note: this is called / shared by both the stb_truetype and the FreeType builder
|
||||||
void ImFontAtlasBuildInit(ImFontAtlas* atlas)
|
void ImFontAtlasBuildInit(ImFontAtlas* atlas)
|
||||||
{
|
{
|
||||||
|
// Round font size
|
||||||
|
// - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet.
|
||||||
|
// - Note that using io.FontGlobalScale or SetWindowFontScale(), with are legacy-ish, partially supported features, can still lead to unrounded sizes.
|
||||||
|
// - We may support it better later and remove this rounding.
|
||||||
|
for (ImFontConfig& cfg : atlas->ConfigData)
|
||||||
|
cfg.SizePixels = ImTrunc(cfg.SizePixels);
|
||||||
|
|
||||||
// Register texture region for mouse cursors or standard white pixels
|
// Register texture region for mouse cursors or standard white pixels
|
||||||
if (atlas->PackIdMouseCursors < 0)
|
if (atlas->PackIdMouseCursors < 0)
|
||||||
{
|
{
|
||||||
|
@ -2798,9 +2937,9 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build all fonts lookup tables
|
// Build all fonts lookup tables
|
||||||
for (int i = 0; i < atlas->Fonts.Size; i++)
|
for (ImFont* font : atlas->Fonts)
|
||||||
if (atlas->Fonts[i]->DirtyLookupTables)
|
if (font->DirtyLookupTables)
|
||||||
atlas->Fonts[i]->BuildLookupTable();
|
font->BuildLookupTable();
|
||||||
|
|
||||||
atlas->TexReady = true;
|
atlas->TexReady = true;
|
||||||
}
|
}
|
||||||
|
@ -3165,6 +3304,7 @@ void ImFont::BuildLookupTable()
|
||||||
max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint);
|
max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint);
|
||||||
|
|
||||||
// Build lookup table
|
// Build lookup table
|
||||||
|
IM_ASSERT(Glyphs.Size > 0 && "Font has not loaded glyph!");
|
||||||
IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved
|
IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved
|
||||||
IndexAdvanceX.clear();
|
IndexAdvanceX.clear();
|
||||||
IndexLookup.clear();
|
IndexLookup.clear();
|
||||||
|
@ -3281,7 +3421,7 @@ void ImFont::AddGlyph(const ImFontConfig* cfg, ImWchar codepoint, float x0, floa
|
||||||
advance_x = ImClamp(advance_x, cfg->GlyphMinAdvanceX, cfg->GlyphMaxAdvanceX);
|
advance_x = ImClamp(advance_x, cfg->GlyphMinAdvanceX, cfg->GlyphMaxAdvanceX);
|
||||||
if (advance_x != advance_x_original)
|
if (advance_x != advance_x_original)
|
||||||
{
|
{
|
||||||
float char_off_x = cfg->PixelSnapH ? ImFloor((advance_x - advance_x_original) * 0.5f) : (advance_x - advance_x_original) * 0.5f;
|
float char_off_x = cfg->PixelSnapH ? ImTrunc((advance_x - advance_x_original) * 0.5f) : (advance_x - advance_x_original) * 0.5f;
|
||||||
x0 += char_off_x;
|
x0 += char_off_x;
|
||||||
x1 += char_off_x;
|
x1 += char_off_x;
|
||||||
}
|
}
|
||||||
|
@ -3549,8 +3689,8 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im
|
||||||
if (glyph->Colored)
|
if (glyph->Colored)
|
||||||
col |= ~IM_COL32_A_MASK;
|
col |= ~IM_COL32_A_MASK;
|
||||||
float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f;
|
float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f;
|
||||||
float x = IM_FLOOR(pos.x);
|
float x = IM_TRUNC(pos.x);
|
||||||
float y = IM_FLOOR(pos.y);
|
float y = IM_TRUNC(pos.y);
|
||||||
draw_list->PrimReserve(6, 4);
|
draw_list->PrimReserve(6, 4);
|
||||||
draw_list->PrimRectUV(ImVec2(x + glyph->X0 * scale, y + glyph->Y0 * scale), ImVec2(x + glyph->X1 * scale, y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col);
|
draw_list->PrimRectUV(ImVec2(x + glyph->X0 * scale, y + glyph->Y0 * scale), ImVec2(x + glyph->X1 * scale, y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col);
|
||||||
}
|
}
|
||||||
|
@ -3562,8 +3702,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
|
||||||
text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls.
|
text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls.
|
||||||
|
|
||||||
// Align to be pixel perfect
|
// Align to be pixel perfect
|
||||||
float x = IM_FLOOR(pos.x);
|
float x = IM_TRUNC(pos.x);
|
||||||
float y = IM_FLOOR(pos.y);
|
float y = IM_TRUNC(pos.y);
|
||||||
if (y > clip_rect.w)
|
if (y > clip_rect.w)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -3747,6 +3887,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
|
||||||
// - RenderArrow()
|
// - RenderArrow()
|
||||||
// - RenderBullet()
|
// - RenderBullet()
|
||||||
// - RenderCheckMark()
|
// - RenderCheckMark()
|
||||||
|
// - RenderArrowDockMenu()
|
||||||
// - RenderArrowPointingAt()
|
// - RenderArrowPointingAt()
|
||||||
// - RenderRectFilledRangeH()
|
// - RenderRectFilledRangeH()
|
||||||
// - RenderRectFilledWithHole()
|
// - RenderRectFilledWithHole()
|
||||||
|
@ -3821,6 +3962,14 @@ void ImGui::RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is less wide than RenderArrow() and we use in dock nodes instead of the regular RenderArrow() to denote a change of functionality,
|
||||||
|
// and because the saved space means that the left-most tab label can stay at exactly the same position as the label of a loose window.
|
||||||
|
void ImGui::RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col)
|
||||||
|
{
|
||||||
|
draw_list->AddRectFilled(p_min + ImVec2(sz * 0.20f, sz * 0.15f), p_min + ImVec2(sz * 0.80f, sz * 0.30f), col);
|
||||||
|
RenderArrowPointingAt(draw_list, p_min + ImVec2(sz * 0.50f, sz * 0.85f), ImVec2(sz * 0.30f, sz * 0.40f), ImGuiDir_Down, col);
|
||||||
|
}
|
||||||
|
|
||||||
static inline float ImAcos01(float x)
|
static inline float ImAcos01(float x)
|
||||||
{
|
{
|
||||||
if (x <= 0.0f) return IM_PI * 0.5f;
|
if (x <= 0.0f) return IM_PI * 0.5f;
|
||||||
|
@ -3863,8 +4012,8 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding, IM_PI - arc0_e, IM_PI - arc0_b, 3); // BL
|
draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding, IM_PI - arc0_e, IM_PI - arc0_b); // BL
|
||||||
draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e, 3); // TR
|
draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e); // TR
|
||||||
}
|
}
|
||||||
if (p1.x > rect.Min.x + rounding)
|
if (p1.x > rect.Min.x + rounding)
|
||||||
{
|
{
|
||||||
|
@ -3883,8 +4032,8 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b, 3); // TR
|
draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b); // TR
|
||||||
draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e, 3); // BR
|
draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e); // BR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
draw_list->PathFillConvex(col);
|
draw_list->PathFillConvex(col);
|
||||||
|
@ -3906,6 +4055,17 @@ void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer,
|
||||||
if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomRight);
|
if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImDrawFlags ImGui::CalcRoundingFlagsForRectInRect(const ImRect& r_in, const ImRect& r_outer, float threshold)
|
||||||
|
{
|
||||||
|
bool round_l = r_in.Min.x <= r_outer.Min.x + threshold;
|
||||||
|
bool round_r = r_in.Max.x >= r_outer.Max.x - threshold;
|
||||||
|
bool round_t = r_in.Min.y <= r_outer.Min.y + threshold;
|
||||||
|
bool round_b = r_in.Max.y >= r_outer.Max.y - threshold;
|
||||||
|
return ImDrawFlags_RoundCornersNone
|
||||||
|
| ((round_t && round_l) ? ImDrawFlags_RoundCornersTopLeft : 0) | ((round_t && round_r) ? ImDrawFlags_RoundCornersTopRight : 0)
|
||||||
|
| ((round_b && round_l) ? ImDrawFlags_RoundCornersBottomLeft : 0) | ((round_b && round_r) ? ImDrawFlags_RoundCornersBottomRight : 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Helper for ColorPicker4()
|
// Helper for ColorPicker4()
|
||||||
// NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that.
|
// NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that.
|
||||||
// Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding altogether.
|
// Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding altogether.
|
||||||
|
@ -4071,8 +4231,8 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// ProggyClean.ttf
|
// ProggyClean.ttf
|
||||||
// Copyright (c) 2004, 2005 Tristan Grimmer
|
// Copyright (c) 2004, 2005 Tristan Grimmer
|
||||||
// MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip)
|
// MIT license (see License.txt in http://www.proggyfonts.net/index.php?menu=download)
|
||||||
// Download and more information at http://upperbounds.net
|
// Download and more information at http://www.proggyfonts.net or http://upperboundsinteractive.com/fonts.php
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// File: 'ProggyClean.ttf' (41208 bytes)
|
// File: 'ProggyClean.ttf' (41208 bytes)
|
||||||
// Exported using misc/fonts/binary_to_compressed_c.cpp (with compression + base85 string encoding).
|
// Exported using misc/fonts/binary_to_compressed_c.cpp (with compression + base85 string encoding).
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
// MIT License
|
// MIT License
|
||||||
|
|
||||||
// Copyright (c) 2022 Evan Pezent
|
// Copyright (c) 2023 Evan Pezent
|
||||||
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
// ImPlot v0.14
|
// ImPlot v0.17
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
@ -31,6 +31,9 @@ Below is a change-log of API breaking changes only. If you are using one of the
|
||||||
When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot files.
|
When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot files.
|
||||||
You can read releases logs https://github.com/epezent/implot/releases for more details.
|
You can read releases logs https://github.com/epezent/implot/releases for more details.
|
||||||
|
|
||||||
|
- 2023/08/20 (0.17) - ImPlotFlags_NoChild was removed as child windows are no longer needed to capture scroll. You can safely remove this flag if you were using it.
|
||||||
|
- 2023/06/26 (0.15) - Various build fixes related to updates in Dear ImGui internals.
|
||||||
|
- 2022/11/25 (0.15) - Make PlotText honor ImPlotItemFlags_NoFit.
|
||||||
- 2022/06/19 (0.14) - The signature of ColormapScale has changed to accommodate a new ImPlotColormapScaleFlags parameter
|
- 2022/06/19 (0.14) - The signature of ColormapScale has changed to accommodate a new ImPlotColormapScaleFlags parameter
|
||||||
- 2022/06/17 (0.14) - **IMPORTANT** All PlotX functions now take an ImPlotX_Flags `flags` parameter. Where applicable, it is located before the existing `offset` and `stride` parameters.
|
- 2022/06/17 (0.14) - **IMPORTANT** All PlotX functions now take an ImPlotX_Flags `flags` parameter. Where applicable, it is located before the existing `offset` and `stride` parameters.
|
||||||
If you were providing offset and stride values, you will need to update your function call to include a `flags` value. If you fail to do this, you will likely see
|
If you were providing offset and stride values, you will need to update your function call to include a `flags` value. If you fail to do this, you will likely see
|
||||||
|
@ -133,6 +136,11 @@ You can read releases logs https://github.com/epezent/implot/releases for more d
|
||||||
#define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All
|
#define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Support for pre-1.89.7 versions.
|
||||||
|
#if (IMGUI_VERSION_NUM < 18966)
|
||||||
|
#define ImGuiButtonFlags_AllowOverlap ImGuiButtonFlags_AllowItemOverlap
|
||||||
|
#endif
|
||||||
|
|
||||||
// Visual Studio warnings
|
// Visual Studio warnings
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
|
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
|
||||||
|
@ -285,35 +293,35 @@ struct ImPlotStyleVarInfo {
|
||||||
|
|
||||||
static const ImPlotStyleVarInfo GPlotStyleVarInfo[] =
|
static const ImPlotStyleVarInfo GPlotStyleVarInfo[] =
|
||||||
{
|
{
|
||||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, LineWeight) }, // ImPlotStyleVar_LineWeight
|
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, LineWeight) }, // ImPlotStyleVar_LineWeight
|
||||||
{ ImGuiDataType_S32, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker
|
{ ImGuiDataType_S32, 1, (ImU32)offsetof(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker
|
||||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerSize) }, // ImPlotStyleVar_MarkerSize
|
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MarkerSize) }, // ImPlotStyleVar_MarkerSize
|
||||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight
|
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight
|
||||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, FillAlpha) }, // ImPlotStyleVar_FillAlpha
|
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, FillAlpha) }, // ImPlotStyleVar_FillAlpha
|
||||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize
|
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize
|
||||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarWeight) }, // ImPlotStyleVar_ErrorBarWeight
|
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, ErrorBarWeight) }, // ImPlotStyleVar_ErrorBarWeight
|
||||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitHeight) }, // ImPlotStyleVar_DigitalBitHeight
|
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, DigitalBitHeight) }, // ImPlotStyleVar_DigitalBitHeight
|
||||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitGap) }, // ImPlotStyleVar_DigitalBitGap
|
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, DigitalBitGap) }, // ImPlotStyleVar_DigitalBitGap
|
||||||
|
|
||||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotBorderSize) }, // ImPlotStyleVar_PlotBorderSize
|
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, PlotBorderSize) }, // ImPlotStyleVar_PlotBorderSize
|
||||||
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorAlpha) }, // ImPlotStyleVar_MinorAlpha
|
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MinorAlpha) }, // ImPlotStyleVar_MinorAlpha
|
||||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorTickLen) }, // ImPlotStyleVar_MajorTickLen
|
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorTickLen) }, // ImPlotStyleVar_MajorTickLen
|
||||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorTickLen) }, // ImPlotStyleVar_MinorTickLen
|
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorTickLen) }, // ImPlotStyleVar_MinorTickLen
|
||||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorTickSize) }, // ImPlotStyleVar_MajorTickSize
|
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorTickSize) }, // ImPlotStyleVar_MajorTickSize
|
||||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorTickSize) }, // ImPlotStyleVar_MinorTickSize
|
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorTickSize) }, // ImPlotStyleVar_MinorTickSize
|
||||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorGridSize) }, // ImPlotStyleVar_MajorGridSize
|
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorGridSize) }, // ImPlotStyleVar_MajorGridSize
|
||||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorGridSize) }, // ImPlotStyleVar_MinorGridSize
|
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorGridSize) }, // ImPlotStyleVar_MinorGridSize
|
||||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotPadding) }, // ImPlotStyleVar_PlotPadding
|
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotPadding) }, // ImPlotStyleVar_PlotPadding
|
||||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LabelPadding) }, // ImPlotStyleVar_LabelPaddine
|
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LabelPadding) }, // ImPlotStyleVar_LabelPaddine
|
||||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendPadding) }, // ImPlotStyleVar_LegendPadding
|
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendPadding) }, // ImPlotStyleVar_LegendPadding
|
||||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendInnerPadding) }, // ImPlotStyleVar_LegendInnerPadding
|
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendInnerPadding) }, // ImPlotStyleVar_LegendInnerPadding
|
||||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendSpacing) }, // ImPlotStyleVar_LegendSpacing
|
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendSpacing) }, // ImPlotStyleVar_LegendSpacing
|
||||||
|
|
||||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MousePosPadding) }, // ImPlotStyleVar_MousePosPadding
|
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MousePosPadding) }, // ImPlotStyleVar_MousePosPadding
|
||||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, AnnotationPadding) }, // ImPlotStyleVar_AnnotationPadding
|
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, AnnotationPadding) }, // ImPlotStyleVar_AnnotationPadding
|
||||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, FitPadding) }, // ImPlotStyleVar_FitPadding
|
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, FitPadding) }, // ImPlotStyleVar_FitPadding
|
||||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotDefaultSize) }, // ImPlotStyleVar_PlotDefaultSize
|
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotDefaultSize) }, // ImPlotStyleVar_PlotDefaultSize
|
||||||
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotMinSize) } // ImPlotStyleVar_PlotMinSize
|
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotMinSize) } // ImPlotStyleVar_PlotMinSize
|
||||||
};
|
};
|
||||||
|
|
||||||
static const ImPlotStyleVarInfo* GetPlotStyleVarInfo(ImPlotStyleVar idx) {
|
static const ImPlotStyleVarInfo* GetPlotStyleVarInfo(ImPlotStyleVar idx) {
|
||||||
|
@ -333,8 +341,8 @@ void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char *te
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *GImGui;
|
||||||
ImFont* font = g.Font;
|
ImFont* font = g.Font;
|
||||||
// Align to be pixel perfect
|
// Align to be pixel perfect
|
||||||
pos.x = IM_FLOOR(pos.x);
|
pos.x = ImFloor(pos.x);
|
||||||
pos.y = IM_FLOOR(pos.y);
|
pos.y = ImFloor(pos.y);
|
||||||
const float scale = g.FontSize / font->FontSize;
|
const float scale = g.FontSize / font->FontSize;
|
||||||
const char* s = text_begin;
|
const char* s = text_begin;
|
||||||
int chars_exp = (int)(text_end - s);
|
int chars_exp = (int)(text_end - s);
|
||||||
|
@ -485,10 +493,6 @@ void Initialize(ImPlotContext* ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResetCtxForNextPlot(ImPlotContext* ctx) {
|
void ResetCtxForNextPlot(ImPlotContext* ctx) {
|
||||||
// end child window if it was made
|
|
||||||
if (ctx->ChildWindowMade)
|
|
||||||
ImGui::EndChild();
|
|
||||||
ctx->ChildWindowMade = false;
|
|
||||||
// reset the next plot/item data
|
// reset the next plot/item data
|
||||||
ctx->NextPlotData.Reset();
|
ctx->NextPlotData.Reset();
|
||||||
ctx->NextItemData.Reset();
|
ctx->NextItemData.Reset();
|
||||||
|
@ -582,6 +586,28 @@ ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& s
|
||||||
return legend_size;
|
return legend_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ClampLegendRect(ImRect& legend_rect, const ImRect& outer_rect, const ImVec2& pad) {
|
||||||
|
bool clamped = false;
|
||||||
|
ImRect outer_rect_pad(outer_rect.Min + pad, outer_rect.Max - pad);
|
||||||
|
if (legend_rect.Min.x < outer_rect_pad.Min.x) {
|
||||||
|
legend_rect.Min.x = outer_rect_pad.Min.x;
|
||||||
|
clamped = true;
|
||||||
|
}
|
||||||
|
if (legend_rect.Min.y < outer_rect_pad.Min.y) {
|
||||||
|
legend_rect.Min.y = outer_rect_pad.Min.y;
|
||||||
|
clamped = true;
|
||||||
|
}
|
||||||
|
if (legend_rect.Max.x > outer_rect_pad.Max.x) {
|
||||||
|
legend_rect.Max.x = outer_rect_pad.Max.x;
|
||||||
|
clamped = true;
|
||||||
|
}
|
||||||
|
if (legend_rect.Max.y > outer_rect_pad.Max.y) {
|
||||||
|
legend_rect.Max.y = outer_rect_pad.Max.y;
|
||||||
|
clamped = true;
|
||||||
|
}
|
||||||
|
return clamped;
|
||||||
|
}
|
||||||
|
|
||||||
int LegendSortingComp(const void* _a, const void* _b) {
|
int LegendSortingComp(const void* _a, const void* _b) {
|
||||||
ImPlotItemGroup* items = GImPlot->SortItems;
|
ImPlotItemGroup* items = GImPlot->SortItems;
|
||||||
const int a = *(const int*)_a;
|
const int a = *(const int*)_a;
|
||||||
|
@ -1296,12 +1322,12 @@ bool DragFloat(const char*, F*, float, F, F) {
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
bool DragFloat<double>(const char* label, double* v, float v_speed, double v_min, double v_max) {
|
bool DragFloat<double>(const char* label, double* v, float v_speed, double v_min, double v_max) {
|
||||||
return ImGui::DragScalar(label, ImGuiDataType_Double, v, v_speed, &v_min, &v_max, "%.3f", 1);
|
return ImGui::DragScalar(label, ImGuiDataType_Double, v, v_speed, &v_min, &v_max, "%.3g", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
bool DragFloat<float>(const char* label, float* v, float v_speed, float v_min, float v_max) {
|
bool DragFloat<float>(const char* label, float* v, float v_speed, float v_min, float v_max) {
|
||||||
return ImGui::DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, "%.3f", 1);
|
return ImGui::DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, "%.3g", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void BeginDisabledControls(bool cond) {
|
inline void BeginDisabledControls(bool cond) {
|
||||||
|
@ -1548,7 +1574,7 @@ void ShowPlotContextMenu(ImPlotPlot& plot) {
|
||||||
ImFlipFlag(plot.Flags, ImPlotFlags_Crosshairs);
|
ImFlipFlag(plot.Flags, ImPlotFlags_Crosshairs);
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
if (gp.CurrentSubplot != nullptr && !ImHasFlag(gp.CurrentPlot->Flags, ImPlotSubplotFlags_NoMenus)) {
|
if (gp.CurrentSubplot != nullptr && !ImHasFlag(gp.CurrentSubplot->Flags, ImPlotSubplotFlags_NoMenus)) {
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
if ((ImGui::BeginMenu("Subplots"))) {
|
if ((ImGui::BeginMenu("Subplots"))) {
|
||||||
ShowSubplotsContextMenu(*gp.CurrentSubplot);
|
ShowSubplotsContextMenu(*gp.CurrentSubplot);
|
||||||
|
@ -1808,7 +1834,7 @@ bool UpdateInput(ImPlotPlot& plot) {
|
||||||
|
|
||||||
// BUTTON STATE -----------------------------------------------------------
|
// BUTTON STATE -----------------------------------------------------------
|
||||||
|
|
||||||
const ImGuiButtonFlags plot_button_flags = ImGuiButtonFlags_AllowItemOverlap
|
const ImGuiButtonFlags plot_button_flags = ImGuiButtonFlags_AllowOverlap
|
||||||
| ImGuiButtonFlags_PressedOnClick
|
| ImGuiButtonFlags_PressedOnClick
|
||||||
| ImGuiButtonFlags_PressedOnDoubleClick
|
| ImGuiButtonFlags_PressedOnDoubleClick
|
||||||
| ImGuiButtonFlags_MouseButtonLeft
|
| ImGuiButtonFlags_MouseButtonLeft
|
||||||
|
@ -1818,7 +1844,9 @@ bool UpdateInput(ImPlotPlot& plot) {
|
||||||
| plot_button_flags;
|
| plot_button_flags;
|
||||||
|
|
||||||
const bool plot_clicked = ImGui::ButtonBehavior(plot.PlotRect,plot.ID,&plot.Hovered,&plot.Held,plot_button_flags);
|
const bool plot_clicked = ImGui::ButtonBehavior(plot.PlotRect,plot.ID,&plot.Hovered,&plot.Held,plot_button_flags);
|
||||||
ImGui::SetItemAllowOverlap();
|
#if (IMGUI_VERSION_NUM < 18966)
|
||||||
|
ImGui::SetItemAllowOverlap(); // Handled by ButtonBehavior()
|
||||||
|
#endif
|
||||||
|
|
||||||
if (plot_clicked) {
|
if (plot_clicked) {
|
||||||
if (!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect) && IO.MouseClicked[gp.InputMap.Select] && ImHasFlag(IO.KeyMods, gp.InputMap.SelectMod)) {
|
if (!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect) && IO.MouseClicked[gp.InputMap.Select] && ImHasFlag(IO.KeyMods, gp.InputMap.SelectMod)) {
|
||||||
|
@ -1951,10 +1979,12 @@ bool UpdateInput(ImPlotPlot& plot) {
|
||||||
|
|
||||||
// SCROLL INPUT -----------------------------------------------------------
|
// SCROLL INPUT -----------------------------------------------------------
|
||||||
|
|
||||||
if (any_hov && IO.MouseWheel != 0 && ImHasFlag(IO.KeyMods, gp.InputMap.ZoomMod)) {
|
if (any_hov && ImHasFlag(IO.KeyMods, gp.InputMap.ZoomMod)) {
|
||||||
|
|
||||||
float zoom_rate = gp.InputMap.ZoomRate;
|
float zoom_rate = gp.InputMap.ZoomRate;
|
||||||
if (IO.MouseWheel > 0)
|
if (IO.MouseWheel == 0.0f)
|
||||||
|
zoom_rate = 0;
|
||||||
|
else if (IO.MouseWheel > 0)
|
||||||
zoom_rate = (-zoom_rate) / (1.0f + (2.0f * zoom_rate));
|
zoom_rate = (-zoom_rate) / (1.0f + (2.0f * zoom_rate));
|
||||||
ImVec2 rect_size = plot.PlotRect.GetSize();
|
ImVec2 rect_size = plot.PlotRect.GetSize();
|
||||||
float tx = ImRemap(IO.MousePos.x, plot.PlotRect.Min.x, plot.PlotRect.Max.x, 0.0f, 1.0f);
|
float tx = ImRemap(IO.MousePos.x, plot.PlotRect.Min.x, plot.PlotRect.Max.x, 0.0f, 1.0f);
|
||||||
|
@ -1965,14 +1995,17 @@ bool UpdateInput(ImPlotPlot& plot) {
|
||||||
const bool equal_zoom = axis_equal && x_axis.OrthoAxis != nullptr;
|
const bool equal_zoom = axis_equal && x_axis.OrthoAxis != nullptr;
|
||||||
const bool equal_locked = (equal_zoom != false) && x_axis.OrthoAxis->IsInputLocked();
|
const bool equal_locked = (equal_zoom != false) && x_axis.OrthoAxis->IsInputLocked();
|
||||||
if (x_hov[i] && !x_axis.IsInputLocked() && !equal_locked) {
|
if (x_hov[i] && !x_axis.IsInputLocked() && !equal_locked) {
|
||||||
float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f;
|
ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID);
|
||||||
const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - rect_size.x * tx * zoom_rate * correction);
|
if (zoom_rate != 0.0f) {
|
||||||
const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x + rect_size.x * (1 - tx) * zoom_rate * correction);
|
float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f;
|
||||||
x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l);
|
const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - rect_size.x * tx * zoom_rate * correction);
|
||||||
x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r);
|
const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x + rect_size.x * (1 - tx) * zoom_rate * correction);
|
||||||
if (axis_equal && x_axis.OrthoAxis != nullptr)
|
x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l);
|
||||||
x_axis.OrthoAxis->SetAspect(x_axis.GetAspect());
|
x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r);
|
||||||
changed = true;
|
if (axis_equal && x_axis.OrthoAxis != nullptr)
|
||||||
|
x_axis.OrthoAxis->SetAspect(x_axis.GetAspect());
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
|
for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
|
||||||
|
@ -1980,14 +2013,17 @@ bool UpdateInput(ImPlotPlot& plot) {
|
||||||
const bool equal_zoom = axis_equal && y_axis.OrthoAxis != nullptr;
|
const bool equal_zoom = axis_equal && y_axis.OrthoAxis != nullptr;
|
||||||
const bool equal_locked = equal_zoom && y_axis.OrthoAxis->IsInputLocked();
|
const bool equal_locked = equal_zoom && y_axis.OrthoAxis->IsInputLocked();
|
||||||
if (y_hov[i] && !y_axis.IsInputLocked() && !equal_locked) {
|
if (y_hov[i] && !y_axis.IsInputLocked() && !equal_locked) {
|
||||||
float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f;
|
ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID);
|
||||||
const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - rect_size.y * ty * zoom_rate * correction);
|
if (zoom_rate != 0.0f) {
|
||||||
const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y + rect_size.y * (1 - ty) * zoom_rate * correction);
|
float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f;
|
||||||
y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b);
|
const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - rect_size.y * ty * zoom_rate * correction);
|
||||||
y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t);
|
const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y + rect_size.y * (1 - ty) * zoom_rate * correction);
|
||||||
if (axis_equal && y_axis.OrthoAxis != nullptr)
|
y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b);
|
||||||
y_axis.OrthoAxis->SetAspect(y_axis.GetAspect());
|
y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t);
|
||||||
changed = true;
|
if (axis_equal && y_axis.OrthoAxis != nullptr)
|
||||||
|
y_axis.OrthoAxis->SetAspect(y_axis.GetAspect());
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2269,19 +2305,19 @@ void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImP
|
||||||
|
|
||||||
void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags) {
|
void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags) {
|
||||||
ImPlotContext& gp = *GImPlot;
|
ImPlotContext& gp = *GImPlot;
|
||||||
IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,
|
IM_ASSERT_USER_ERROR((gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked) || (gp.CurrentSubplot != nullptr && gp.CurrentPlot == nullptr),
|
||||||
"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!");
|
"Setup needs to be called after BeginPlot or BeginSubplots and before any setup locking functions (e.g. PlotX)!");
|
||||||
IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr,
|
if (gp.CurrentItems) {
|
||||||
"SetupLegend() needs to be called within an itemized context!");
|
ImPlotLegend& legend = gp.CurrentItems->Legend;
|
||||||
ImPlotLegend& legend = gp.CurrentItems->Legend;
|
// check and set location
|
||||||
// check and set location
|
if (location != legend.PreviousLocation)
|
||||||
if (location != legend.PreviousLocation)
|
legend.Location = location;
|
||||||
legend.Location = location;
|
legend.PreviousLocation = location;
|
||||||
legend.PreviousLocation = location;
|
// check and set flags
|
||||||
// check and set flags
|
if (flags != legend.PreviousFlags)
|
||||||
if (flags != legend.PreviousFlags)
|
legend.Flags = flags;
|
||||||
legend.Flags = flags;
|
legend.PreviousFlags = flags;
|
||||||
legend.PreviousFlags = flags;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags) {
|
void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags) {
|
||||||
|
@ -2393,22 +2429,6 @@ bool BeginPlot(const char* title_id, const ImVec2& size, ImPlotFlags flags) {
|
||||||
for (int i = 0; i < ImAxis_COUNT; ++i)
|
for (int i = 0; i < ImAxis_COUNT; ++i)
|
||||||
ApplyNextPlotData(i);
|
ApplyNextPlotData(i);
|
||||||
|
|
||||||
// capture scroll with a child region
|
|
||||||
if (!ImHasFlag(plot.Flags, ImPlotFlags_NoChild)) {
|
|
||||||
ImVec2 child_size;
|
|
||||||
if (gp.CurrentSubplot != nullptr)
|
|
||||||
child_size = gp.CurrentSubplot->CellSize;
|
|
||||||
else
|
|
||||||
child_size = ImVec2(size.x == 0 ? gp.Style.PlotDefaultSize.x : size.x, size.y == 0 ? gp.Style.PlotDefaultSize.y : size.y);
|
|
||||||
ImGui::BeginChild(title_id, child_size, false, ImGuiWindowFlags_NoScrollbar);
|
|
||||||
Window = ImGui::GetCurrentWindow();
|
|
||||||
Window->ScrollMax.y = 1.0f;
|
|
||||||
gp.ChildWindowMade = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
gp.ChildWindowMade = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear text buffers
|
// clear text buffers
|
||||||
plot.ClearTextBuffer();
|
plot.ClearTextBuffer();
|
||||||
plot.SetTitle(title_id);
|
plot.SetTitle(title_id);
|
||||||
|
@ -2550,7 +2570,7 @@ void SetupFinish() {
|
||||||
// (2) get y tick labels (needed for left/right pad)
|
// (2) get y tick labels (needed for left/right pad)
|
||||||
for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
|
for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
|
||||||
ImPlotAxis& axis = plot.YAxis(i);
|
ImPlotAxis& axis = plot.YAxis(i);
|
||||||
if (axis.WillRender() && axis.ShowDefaultTicks) {
|
if (axis.WillRender() && axis.ShowDefaultTicks && plot_height > 0) {
|
||||||
axis.Locator(axis.Ticker, axis.Range, plot_height, true, axis.Formatter, axis.FormatterData);
|
axis.Locator(axis.Ticker, axis.Range, plot_height, true, axis.Formatter, axis.FormatterData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2563,7 +2583,7 @@ void SetupFinish() {
|
||||||
// (4) get x ticks
|
// (4) get x ticks
|
||||||
for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {
|
for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {
|
||||||
ImPlotAxis& axis = plot.XAxis(i);
|
ImPlotAxis& axis = plot.XAxis(i);
|
||||||
if (axis.WillRender() && axis.ShowDefaultTicks) {
|
if (axis.WillRender() && axis.ShowDefaultTicks && plot_width > 0) {
|
||||||
axis.Locator(axis.Ticker, axis.Range, plot_width, false, axis.Formatter, axis.FormatterData);
|
axis.Locator(axis.Ticker, axis.Range, plot_width, false, axis.Formatter, axis.FormatterData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2758,7 +2778,7 @@ void EndPlot() {
|
||||||
|
|
||||||
// FINAL RENDER -----------------------------------------------------------
|
// FINAL RENDER -----------------------------------------------------------
|
||||||
|
|
||||||
const bool render_border = gp.Style.PlotBorderSize > 0 && gp.Style.Colors[ImPlotCol_PlotBorder].w > 0;
|
const bool render_border = gp.Style.PlotBorderSize > 0 && GetStyleColorVec4(ImPlotCol_PlotBorder).w > 0;
|
||||||
const bool any_x_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES);
|
const bool any_x_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES);
|
||||||
const bool any_y_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES);
|
const bool any_y_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES);
|
||||||
|
|
||||||
|
@ -3023,24 +3043,57 @@ void EndPlot() {
|
||||||
legend.Location,
|
legend.Location,
|
||||||
legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding);
|
legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding);
|
||||||
legend.Rect = ImRect(legend_pos, legend_pos + legend_size);
|
legend.Rect = ImRect(legend_pos, legend_pos + legend_size);
|
||||||
// test hover
|
legend.RectClamped = legend.Rect;
|
||||||
legend.Hovered = ImGui::IsWindowHovered() && legend.Rect.Contains(IO.MousePos);
|
const bool legend_scrollable = ClampLegendRect(legend.RectClamped,
|
||||||
|
legend_out ? plot.FrameRect : plot.PlotRect,
|
||||||
|
legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding
|
||||||
|
);
|
||||||
|
const ImGuiButtonFlags legend_button_flags = ImGuiButtonFlags_AllowOverlap
|
||||||
|
| ImGuiButtonFlags_PressedOnClick
|
||||||
|
| ImGuiButtonFlags_PressedOnDoubleClick
|
||||||
|
| ImGuiButtonFlags_MouseButtonLeft
|
||||||
|
| ImGuiButtonFlags_MouseButtonRight
|
||||||
|
| ImGuiButtonFlags_MouseButtonMiddle
|
||||||
|
| ImGuiButtonFlags_FlattenChildren;
|
||||||
|
ImGui::KeepAliveID(plot.Items.ID);
|
||||||
|
ImGui::ButtonBehavior(legend.RectClamped, plot.Items.ID, &legend.Hovered, &legend.Held, legend_button_flags);
|
||||||
|
legend.Hovered = legend.Hovered || (ImGui::IsWindowHovered() && legend.RectClamped.Contains(IO.MousePos));
|
||||||
|
|
||||||
if (legend_out)
|
if (legend_scrollable) {
|
||||||
ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true);
|
if (legend.Hovered) {
|
||||||
else
|
ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.Items.ID);
|
||||||
PushPlotClipRect();
|
if (IO.MouseWheel != 0.0f) {
|
||||||
ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg);
|
ImVec2 max_step = legend.Rect.GetSize() * 0.67f;
|
||||||
ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder);
|
float font_size = ImGui::GetCurrentWindow()->CalcFontSize();
|
||||||
DrawList.AddRectFilled(legend.Rect.Min, legend.Rect.Max, col_bg);
|
float scroll_step = ImFloor(ImMin(2 * font_size, max_step.x));
|
||||||
DrawList.AddRect(legend.Rect.Min, legend.Rect.Max, col_bd);
|
legend.Scroll.x += scroll_step * IO.MouseWheel;
|
||||||
|
legend.Scroll.y += scroll_step * IO.MouseWheel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const ImVec2 min_scroll_offset = legend.RectClamped.GetSize() - legend.Rect.GetSize();
|
||||||
|
legend.Scroll.x = ImClamp(legend.Scroll.x, min_scroll_offset.x, 0.0f);
|
||||||
|
legend.Scroll.y = ImClamp(legend.Scroll.y, min_scroll_offset.y, 0.0f);
|
||||||
|
const ImVec2 scroll_offset = legend_horz ? ImVec2(legend.Scroll.x, 0) : ImVec2(0, legend.Scroll.y);
|
||||||
|
ImVec2 legend_offset = legend.RectClamped.Min - legend.Rect.Min + scroll_offset;
|
||||||
|
legend.Rect.Min += legend_offset;
|
||||||
|
legend.Rect.Max += legend_offset;
|
||||||
|
} else {
|
||||||
|
legend.Scroll = ImVec2(0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg);
|
||||||
|
const ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder);
|
||||||
|
ImGui::PushClipRect(legend.RectClamped.Min, legend.RectClamped.Max, true);
|
||||||
|
DrawList.AddRectFilled(legend.RectClamped.Min, legend.RectClamped.Max, col_bg);
|
||||||
bool legend_contextable = ShowLegendEntries(plot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList)
|
bool legend_contextable = ShowLegendEntries(plot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList)
|
||||||
&& !ImHasFlag(legend.Flags, ImPlotLegendFlags_NoMenus);
|
&& !ImHasFlag(legend.Flags, ImPlotLegendFlags_NoMenus);
|
||||||
|
DrawList.AddRect(legend.RectClamped.Min, legend.RectClamped.Max, col_bd);
|
||||||
|
ImGui::PopClipRect();
|
||||||
|
|
||||||
// main ctx menu
|
// main ctx menu
|
||||||
if (gp.OpenContextThisFrame && legend_contextable && !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus))
|
if (gp.OpenContextThisFrame && legend_contextable && !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus))
|
||||||
ImGui::OpenPopup("##LegendContext");
|
ImGui::OpenPopup("##LegendContext");
|
||||||
ImGui::PopClipRect();
|
|
||||||
if (ImGui::BeginPopup("##LegendContext")) {
|
if (ImGui::BeginPopup("##LegendContext")) {
|
||||||
ImGui::Text("Legend"); ImGui::Separator();
|
ImGui::Text("Legend"); ImGui::Separator();
|
||||||
if (ShowLegendContextMenu(legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend)))
|
if (ShowLegendContextMenu(legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend)))
|
||||||
|
@ -3350,7 +3403,7 @@ bool BeginSubplots(const char* title, int rows, int cols, const ImVec2& size, Im
|
||||||
subplot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size);
|
subplot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size);
|
||||||
subplot.GridRect.Min = subplot.FrameRect.Min + half_pad + ImVec2(0,pad_top);
|
subplot.GridRect.Min = subplot.FrameRect.Min + half_pad + ImVec2(0,pad_top);
|
||||||
subplot.GridRect.Max = subplot.FrameRect.Max - half_pad;
|
subplot.GridRect.Max = subplot.FrameRect.Max - half_pad;
|
||||||
subplot.FrameHovered = subplot.FrameRect.Contains(ImGui::GetMousePos()) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows);
|
subplot.FrameHovered = subplot.FrameRect.Contains(ImGui::GetMousePos()) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows|ImGuiHoveredFlags_AllowWhenBlockedByActiveItem);
|
||||||
|
|
||||||
// outside legend adjustments (TODO: make function)
|
// outside legend adjustments (TODO: make function)
|
||||||
const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems);
|
const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems);
|
||||||
|
@ -3397,7 +3450,7 @@ bool BeginSubplots(const char* title, int rows, int cols, const ImVec2& size, Im
|
||||||
ImGui::KeepAliveID(sep_id);
|
ImGui::KeepAliveID(sep_id);
|
||||||
const ImRect sep_bb = ImRect(subplot.GridRect.Min.x, ypos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.x, ypos+SUBPLOT_SPLITTER_HALF_THICKNESS);
|
const ImRect sep_bb = ImRect(subplot.GridRect.Min.x, ypos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.x, ypos+SUBPLOT_SPLITTER_HALF_THICKNESS);
|
||||||
bool sep_hov = false, sep_hld = false;
|
bool sep_hov = false, sep_hld = false;
|
||||||
const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick);
|
const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick);
|
||||||
if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) {
|
if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) {
|
||||||
if (sep_clk && ImGui::IsMouseDoubleClicked(0)) {
|
if (sep_clk && ImGui::IsMouseDoubleClicked(0)) {
|
||||||
float p = (subplot.RowRatios[r] + subplot.RowRatios[r+1])/2;
|
float p = (subplot.RowRatios[r] + subplot.RowRatios[r+1])/2;
|
||||||
|
@ -3427,7 +3480,7 @@ bool BeginSubplots(const char* title, int rows, int cols, const ImVec2& size, Im
|
||||||
ImGui::KeepAliveID(sep_id);
|
ImGui::KeepAliveID(sep_id);
|
||||||
const ImRect sep_bb = ImRect(xpos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Min.y, xpos+SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.y);
|
const ImRect sep_bb = ImRect(xpos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Min.y, xpos+SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.y);
|
||||||
bool sep_hov = false, sep_hld = false;
|
bool sep_hov = false, sep_hld = false;
|
||||||
const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick);
|
const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick);
|
||||||
if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) {
|
if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) {
|
||||||
if (sep_clk && ImGui::IsMouseDoubleClicked(0)) {
|
if (sep_clk && ImGui::IsMouseDoubleClicked(0)) {
|
||||||
float p = (subplot.ColRatios[c] + subplot.ColRatios[c+1])/2;
|
float p = (subplot.ColRatios[c] + subplot.ColRatios[c+1])/2;
|
||||||
|
@ -3488,6 +3541,7 @@ void EndSubplots() {
|
||||||
ImPlotContext& gp = *GImPlot;
|
ImPlotContext& gp = *GImPlot;
|
||||||
IM_ASSERT_USER_ERROR(gp.CurrentSubplot != nullptr, "Mismatched BeginSubplots()/EndSubplots()!");
|
IM_ASSERT_USER_ERROR(gp.CurrentSubplot != nullptr, "Mismatched BeginSubplots()/EndSubplots()!");
|
||||||
ImPlotSubplot& subplot = *gp.CurrentSubplot;
|
ImPlotSubplot& subplot = *gp.CurrentSubplot;
|
||||||
|
const ImGuiIO& IO = ImGui::GetIO();
|
||||||
// set alignments
|
// set alignments
|
||||||
for (int r = 0; r < subplot.Rows; ++r)
|
for (int r = 0; r < subplot.Rows; ++r)
|
||||||
subplot.RowAlignmentData[r].End();
|
subplot.RowAlignmentData[r].End();
|
||||||
|
@ -3506,24 +3560,60 @@ void EndSubplots() {
|
||||||
const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems);
|
const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems);
|
||||||
ImDrawList& DrawList = *ImGui::GetWindowDrawList();
|
ImDrawList& DrawList = *ImGui::GetWindowDrawList();
|
||||||
if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) {
|
if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) {
|
||||||
const bool legend_horz = ImHasFlag(subplot.Items.Legend.Flags, ImPlotLegendFlags_Horizontal);
|
ImPlotLegend& legend = subplot.Items.Legend;
|
||||||
|
const bool legend_horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal);
|
||||||
const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz);
|
const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz);
|
||||||
const ImVec2 legend_pos = GetLocationPos(subplot.FrameRect, legend_size, subplot.Items.Legend.Location, gp.Style.PlotPadding);
|
const ImVec2 legend_pos = GetLocationPos(subplot.FrameRect, legend_size, legend.Location, gp.Style.PlotPadding);
|
||||||
subplot.Items.Legend.Rect = ImRect(legend_pos, legend_pos + legend_size);
|
legend.Rect = ImRect(legend_pos, legend_pos + legend_size);
|
||||||
subplot.Items.Legend.Hovered = subplot.FrameHovered && subplot.Items.Legend.Rect.Contains(ImGui::GetIO().MousePos);
|
legend.RectClamped = legend.Rect;
|
||||||
ImGui::PushClipRect(subplot.FrameRect.Min, subplot.FrameRect.Max, true);
|
const bool legend_scrollable = ClampLegendRect(legend.RectClamped,subplot.FrameRect, gp.Style.PlotPadding);
|
||||||
ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg);
|
const ImGuiButtonFlags legend_button_flags = ImGuiButtonFlags_AllowOverlap
|
||||||
ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder);
|
| ImGuiButtonFlags_PressedOnClick
|
||||||
DrawList.AddRectFilled(subplot.Items.Legend.Rect.Min, subplot.Items.Legend.Rect.Max, col_bg);
|
| ImGuiButtonFlags_PressedOnDoubleClick
|
||||||
DrawList.AddRect(subplot.Items.Legend.Rect.Min, subplot.Items.Legend.Rect.Max, col_bd);
|
| ImGuiButtonFlags_MouseButtonLeft
|
||||||
bool legend_contextable = ShowLegendEntries(subplot.Items, subplot.Items.Legend.Rect, subplot.Items.Legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList)
|
| ImGuiButtonFlags_MouseButtonRight
|
||||||
&& !ImHasFlag(subplot.Items.Legend.Flags, ImPlotLegendFlags_NoMenus);
|
| ImGuiButtonFlags_MouseButtonMiddle
|
||||||
|
| ImGuiButtonFlags_FlattenChildren;
|
||||||
|
ImGui::KeepAliveID(subplot.Items.ID);
|
||||||
|
ImGui::ButtonBehavior(legend.RectClamped, subplot.Items.ID, &legend.Hovered, &legend.Held, legend_button_flags);
|
||||||
|
legend.Hovered = legend.Hovered || (subplot.FrameHovered && legend.RectClamped.Contains(ImGui::GetIO().MousePos));
|
||||||
|
|
||||||
|
if (legend_scrollable) {
|
||||||
|
if (legend.Hovered) {
|
||||||
|
ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, subplot.Items.ID);
|
||||||
|
if (IO.MouseWheel != 0.0f) {
|
||||||
|
ImVec2 max_step = legend.Rect.GetSize() * 0.67f;
|
||||||
|
float font_size = ImGui::GetCurrentWindow()->CalcFontSize();
|
||||||
|
float scroll_step = ImFloor(ImMin(2 * font_size, max_step.x));
|
||||||
|
legend.Scroll.x += scroll_step * IO.MouseWheel;
|
||||||
|
legend.Scroll.y += scroll_step * IO.MouseWheel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const ImVec2 min_scroll_offset = legend.RectClamped.GetSize() - legend.Rect.GetSize();
|
||||||
|
legend.Scroll.x = ImClamp(legend.Scroll.x, min_scroll_offset.x, 0.0f);
|
||||||
|
legend.Scroll.y = ImClamp(legend.Scroll.y, min_scroll_offset.y, 0.0f);
|
||||||
|
const ImVec2 scroll_offset = legend_horz ? ImVec2(legend.Scroll.x, 0) : ImVec2(0, legend.Scroll.y);
|
||||||
|
ImVec2 legend_offset = legend.RectClamped.Min - legend.Rect.Min + scroll_offset;
|
||||||
|
legend.Rect.Min += legend_offset;
|
||||||
|
legend.Rect.Max += legend_offset;
|
||||||
|
} else {
|
||||||
|
legend.Scroll = ImVec2(0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg);
|
||||||
|
const ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder);
|
||||||
|
ImGui::PushClipRect(legend.RectClamped.Min, legend.RectClamped.Max, true);
|
||||||
|
DrawList.AddRectFilled(legend.RectClamped.Min, legend.RectClamped.Max, col_bg);
|
||||||
|
bool legend_contextable = ShowLegendEntries(subplot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList)
|
||||||
|
&& !ImHasFlag(legend.Flags, ImPlotLegendFlags_NoMenus);
|
||||||
|
DrawList.AddRect(legend.RectClamped.Min, legend.RectClamped.Max, col_bd);
|
||||||
|
ImGui::PopClipRect();
|
||||||
|
|
||||||
if (legend_contextable && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoMenus) && ImGui::GetIO().MouseReleased[gp.InputMap.Menu])
|
if (legend_contextable && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoMenus) && ImGui::GetIO().MouseReleased[gp.InputMap.Menu])
|
||||||
ImGui::OpenPopup("##LegendContext");
|
ImGui::OpenPopup("##LegendContext");
|
||||||
ImGui::PopClipRect();
|
|
||||||
if (ImGui::BeginPopup("##LegendContext")) {
|
if (ImGui::BeginPopup("##LegendContext")) {
|
||||||
ImGui::Text("Legend"); ImGui::Separator();
|
ImGui::Text("Legend"); ImGui::Separator();
|
||||||
if (ShowLegendContextMenu(subplot.Items.Legend, !ImHasFlag(subplot.Flags, ImPlotFlags_NoLegend)))
|
if (ShowLegendContextMenu(legend, !ImHasFlag(subplot.Flags, ImPlotFlags_NoLegend)))
|
||||||
ImFlipFlag(subplot.Flags, ImPlotFlags_NoLegend);
|
ImFlipFlag(subplot.Flags, ImPlotFlags_NoLegend);
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
@ -3804,7 +3894,7 @@ IMPLOT_API void TagYV(double y, const ImVec4& color, const char* fmt, va_list ar
|
||||||
|
|
||||||
static const float DRAG_GRAB_HALF_SIZE = 4.0f;
|
static const float DRAG_GRAB_HALF_SIZE = 4.0f;
|
||||||
|
|
||||||
bool DragPoint(int n_id, double* x, double* y, const ImVec4& col, float radius, ImPlotDragToolFlags flags) {
|
bool DragPoint(int n_id, double* x, double* y, const ImVec4& col, float radius, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {
|
||||||
ImGui::PushID("#IMPLOT_DRAG_POINT");
|
ImGui::PushID("#IMPLOT_DRAG_POINT");
|
||||||
IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragPoint() needs to be called between BeginPlot() and EndPlot()!");
|
IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragPoint() needs to be called between BeginPlot() and EndPlot()!");
|
||||||
SetupLock();
|
SetupLock();
|
||||||
|
@ -3826,30 +3916,34 @@ bool DragPoint(int n_id, double* x, double* y, const ImVec4& col, float radius,
|
||||||
bool hovered = false, held = false;
|
bool hovered = false, held = false;
|
||||||
|
|
||||||
ImGui::KeepAliveID(id);
|
ImGui::KeepAliveID(id);
|
||||||
if (input)
|
if (input) {
|
||||||
ImGui::ButtonBehavior(rect,id,&hovered,&held);
|
bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held);
|
||||||
|
if (out_clicked) *out_clicked = clicked;
|
||||||
|
if (out_hovered) *out_hovered = hovered;
|
||||||
|
if (out_held) *out_held = held;
|
||||||
|
}
|
||||||
|
|
||||||
bool dragging = false;
|
bool modified = false;
|
||||||
if (held && ImGui::IsMouseDragging(0)) {
|
if (held && ImGui::IsMouseDragging(0)) {
|
||||||
*x = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
|
*x = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
|
||||||
*y = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
|
*y = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
|
||||||
dragging = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PushPlotClipRect();
|
PushPlotClipRect();
|
||||||
ImDrawList& DrawList = *GetPlotDrawList();
|
ImDrawList& DrawList = *GetPlotDrawList();
|
||||||
if ((hovered || held) && show_curs)
|
if ((hovered || held) && show_curs)
|
||||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||||
if (dragging && no_delay)
|
if (modified && no_delay)
|
||||||
pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO);
|
pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO);
|
||||||
DrawList.AddCircleFilled(pos, radius, col32);
|
DrawList.AddCircleFilled(pos, radius, col32);
|
||||||
PopPlotClipRect();
|
PopPlotClipRect();
|
||||||
|
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
return dragging;
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags) {
|
bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {
|
||||||
// ImGui::PushID("#IMPLOT_DRAG_LINE_X");
|
// ImGui::PushID("#IMPLOT_DRAG_LINE_X");
|
||||||
ImPlotContext& gp = *GImPlot;
|
ImPlotContext& gp = *GImPlot;
|
||||||
IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "DragLineX() needs to be called between BeginPlot() and EndPlot()!");
|
IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "DragLineX() needs to be called between BeginPlot() and EndPlot()!");
|
||||||
|
@ -3871,8 +3965,12 @@ bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPl
|
||||||
bool hovered = false, held = false;
|
bool hovered = false, held = false;
|
||||||
|
|
||||||
ImGui::KeepAliveID(id);
|
ImGui::KeepAliveID(id);
|
||||||
if (input)
|
if (input) {
|
||||||
ImGui::ButtonBehavior(rect,id,&hovered,&held);
|
bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held);
|
||||||
|
if (out_clicked) *out_clicked = clicked;
|
||||||
|
if (out_hovered) *out_hovered = hovered;
|
||||||
|
if (out_held) *out_held = held;
|
||||||
|
}
|
||||||
|
|
||||||
if ((hovered || held) && show_curs)
|
if ((hovered || held) && show_curs)
|
||||||
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
|
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
|
||||||
|
@ -3881,15 +3979,15 @@ bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPl
|
||||||
ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col;
|
ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col;
|
||||||
ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color);
|
ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color);
|
||||||
|
|
||||||
bool dragging = false;
|
bool modified = false;
|
||||||
if (held && ImGui::IsMouseDragging(0)) {
|
if (held && ImGui::IsMouseDragging(0)) {
|
||||||
*value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
|
*value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
|
||||||
dragging = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PushPlotClipRect();
|
PushPlotClipRect();
|
||||||
ImDrawList& DrawList = *GetPlotDrawList();
|
ImDrawList& DrawList = *GetPlotDrawList();
|
||||||
if (dragging && no_delay)
|
if (modified && no_delay)
|
||||||
x = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x);
|
x = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x);
|
||||||
DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yb), col32, thickness);
|
DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yb), col32, thickness);
|
||||||
DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yt+len), col32, 3*thickness);
|
DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yt+len), col32, 3*thickness);
|
||||||
|
@ -3897,10 +3995,10 @@ bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPl
|
||||||
PopPlotClipRect();
|
PopPlotClipRect();
|
||||||
|
|
||||||
// ImGui::PopID();
|
// ImGui::PopID();
|
||||||
return dragging;
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags) {
|
bool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {
|
||||||
ImGui::PushID("#IMPLOT_DRAG_LINE_Y");
|
ImGui::PushID("#IMPLOT_DRAG_LINE_Y");
|
||||||
ImPlotContext& gp = *GImPlot;
|
ImPlotContext& gp = *GImPlot;
|
||||||
IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "DragLineY() needs to be called between BeginPlot() and EndPlot()!");
|
IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "DragLineY() needs to be called between BeginPlot() and EndPlot()!");
|
||||||
|
@ -3923,8 +4021,12 @@ bool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPl
|
||||||
bool hovered = false, held = false;
|
bool hovered = false, held = false;
|
||||||
|
|
||||||
ImGui::KeepAliveID(id);
|
ImGui::KeepAliveID(id);
|
||||||
if (input)
|
if (input) {
|
||||||
ImGui::ButtonBehavior(rect,id,&hovered,&held);
|
bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held);
|
||||||
|
if (out_clicked) *out_clicked = clicked;
|
||||||
|
if (out_hovered) *out_hovered = hovered;
|
||||||
|
if (out_held) *out_held = held;
|
||||||
|
}
|
||||||
|
|
||||||
if ((hovered || held) && show_curs)
|
if ((hovered || held) && show_curs)
|
||||||
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS);
|
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS);
|
||||||
|
@ -3933,15 +4035,15 @@ bool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPl
|
||||||
ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col;
|
ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col;
|
||||||
ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color);
|
ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color);
|
||||||
|
|
||||||
bool dragging = false;
|
bool modified = false;
|
||||||
if (held && ImGui::IsMouseDragging(0)) {
|
if (held && ImGui::IsMouseDragging(0)) {
|
||||||
*value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
|
*value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
|
||||||
dragging = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PushPlotClipRect();
|
PushPlotClipRect();
|
||||||
ImDrawList& DrawList = *GetPlotDrawList();
|
ImDrawList& DrawList = *GetPlotDrawList();
|
||||||
if (dragging && no_delay)
|
if (modified && no_delay)
|
||||||
y = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y);
|
y = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y);
|
||||||
DrawList.AddLine(ImVec2(xl,y), ImVec2(xr,y), col32, thickness);
|
DrawList.AddLine(ImVec2(xl,y), ImVec2(xr,y), col32, thickness);
|
||||||
DrawList.AddLine(ImVec2(xl,y), ImVec2(xl+len,y), col32, 3*thickness);
|
DrawList.AddLine(ImVec2(xl,y), ImVec2(xl+len,y), col32, 3*thickness);
|
||||||
|
@ -3949,10 +4051,10 @@ bool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPl
|
||||||
PopPlotClipRect();
|
PopPlotClipRect();
|
||||||
|
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
return dragging;
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_max, const ImVec4& col, ImPlotDragToolFlags flags) {
|
bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_max, const ImVec4& col, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {
|
||||||
ImGui::PushID("#IMPLOT_DRAG_RECT");
|
ImGui::PushID("#IMPLOT_DRAG_RECT");
|
||||||
IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragRect() needs to be called between BeginPlot() and EndPlot()!");
|
IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragRect() needs to be called between BeginPlot() and EndPlot()!");
|
||||||
SetupLock();
|
SetupLock();
|
||||||
|
@ -3989,13 +4091,18 @@ bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_m
|
||||||
ImU32 col32_a = ImGui::ColorConvertFloat4ToU32(color);
|
ImU32 col32_a = ImGui::ColorConvertFloat4ToU32(color);
|
||||||
const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id);
|
const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id);
|
||||||
|
|
||||||
bool dragging = false;
|
bool modified = false;
|
||||||
bool hovered = false, held = false;
|
bool clicked = false, hovered = false, held = false;
|
||||||
ImRect b_rect(pc.x-DRAG_GRAB_HALF_SIZE,pc.y-DRAG_GRAB_HALF_SIZE,pc.x+DRAG_GRAB_HALF_SIZE,pc.y+DRAG_GRAB_HALF_SIZE);
|
ImRect b_rect(pc.x-DRAG_GRAB_HALF_SIZE,pc.y-DRAG_GRAB_HALF_SIZE,pc.x+DRAG_GRAB_HALF_SIZE,pc.y+DRAG_GRAB_HALF_SIZE);
|
||||||
|
|
||||||
ImGui::KeepAliveID(id);
|
ImGui::KeepAliveID(id);
|
||||||
if (input)
|
if (input) {
|
||||||
ImGui::ButtonBehavior(b_rect,id,&hovered,&held);
|
// middle point
|
||||||
|
clicked = ImGui::ButtonBehavior(b_rect,id,&hovered,&held);
|
||||||
|
if (out_clicked) *out_clicked = clicked;
|
||||||
|
if (out_hovered) *out_hovered = hovered;
|
||||||
|
if (out_held) *out_held = held;
|
||||||
|
}
|
||||||
|
|
||||||
if ((hovered || held) && show_curs)
|
if ((hovered || held) && show_curs)
|
||||||
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll);
|
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll);
|
||||||
|
@ -4005,7 +4112,7 @@ bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_m
|
||||||
*y[i] = pp.y;
|
*y[i] = pp.y;
|
||||||
*x[i] = pp.x;
|
*x[i] = pp.x;
|
||||||
}
|
}
|
||||||
dragging = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
@ -4013,15 +4120,19 @@ bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_m
|
||||||
b_rect = ImRect(p[i].x-DRAG_GRAB_HALF_SIZE,p[i].y-DRAG_GRAB_HALF_SIZE,p[i].x+DRAG_GRAB_HALF_SIZE,p[i].y+DRAG_GRAB_HALF_SIZE);
|
b_rect = ImRect(p[i].x-DRAG_GRAB_HALF_SIZE,p[i].y-DRAG_GRAB_HALF_SIZE,p[i].x+DRAG_GRAB_HALF_SIZE,p[i].y+DRAG_GRAB_HALF_SIZE);
|
||||||
ImGuiID p_id = id + i + 1;
|
ImGuiID p_id = id + i + 1;
|
||||||
ImGui::KeepAliveID(p_id);
|
ImGui::KeepAliveID(p_id);
|
||||||
if (input)
|
if (input) {
|
||||||
ImGui::ButtonBehavior(b_rect,p_id,&hovered,&held);
|
clicked = ImGui::ButtonBehavior(b_rect,p_id,&hovered,&held);
|
||||||
|
if (out_clicked) *out_clicked = *out_clicked || clicked;
|
||||||
|
if (out_hovered) *out_hovered = *out_hovered || hovered;
|
||||||
|
if (out_held) *out_held = *out_held || held;
|
||||||
|
}
|
||||||
if ((hovered || held) && show_curs)
|
if ((hovered || held) && show_curs)
|
||||||
ImGui::SetMouseCursor(cur[i]);
|
ImGui::SetMouseCursor(cur[i]);
|
||||||
|
|
||||||
if (held && ImGui::IsMouseDragging(0)) {
|
if (held && ImGui::IsMouseDragging(0)) {
|
||||||
*x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
|
*x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
|
||||||
*y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
|
*y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
|
||||||
dragging = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// edges
|
// edges
|
||||||
|
@ -4031,8 +4142,12 @@ bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_m
|
||||||
: ImRect(e_min.x - DRAG_GRAB_HALF_SIZE, e_min.y + DRAG_GRAB_HALF_SIZE, e_max.x + DRAG_GRAB_HALF_SIZE, e_max.y - DRAG_GRAB_HALF_SIZE);
|
: ImRect(e_min.x - DRAG_GRAB_HALF_SIZE, e_min.y + DRAG_GRAB_HALF_SIZE, e_max.x + DRAG_GRAB_HALF_SIZE, e_max.y - DRAG_GRAB_HALF_SIZE);
|
||||||
ImGuiID e_id = id + i + 5;
|
ImGuiID e_id = id + i + 5;
|
||||||
ImGui::KeepAliveID(e_id);
|
ImGui::KeepAliveID(e_id);
|
||||||
if (input)
|
if (input) {
|
||||||
ImGui::ButtonBehavior(b_rect,e_id,&hovered,&held);
|
clicked = ImGui::ButtonBehavior(b_rect,e_id,&hovered,&held);
|
||||||
|
if (out_clicked) *out_clicked = *out_clicked || clicked;
|
||||||
|
if (out_hovered) *out_hovered = *out_hovered || hovered;
|
||||||
|
if (out_held) *out_held = *out_held || held;
|
||||||
|
}
|
||||||
if ((hovered || held) && show_curs)
|
if ((hovered || held) && show_curs)
|
||||||
h[i] ? ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
|
h[i] ? ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
|
||||||
if (held && ImGui::IsMouseDragging(0)) {
|
if (held && ImGui::IsMouseDragging(0)) {
|
||||||
|
@ -4040,7 +4155,7 @@ bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_m
|
||||||
*y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
|
*y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
|
||||||
else
|
else
|
||||||
*x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
|
*x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
|
||||||
dragging = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
if (hovered && ImGui::IsMouseDoubleClicked(0))
|
if (hovered && ImGui::IsMouseDoubleClicked(0))
|
||||||
{
|
{
|
||||||
|
@ -4049,14 +4164,22 @@ bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_m
|
||||||
*y[i] = ((y[i] == y_min && *y_min < *y_max) || (y[i] == y_max && *y_max < *y_min)) ? b.Y.Min : b.Y.Max;
|
*y[i] = ((y[i] == y_min && *y_min < *y_max) || (y[i] == y_max && *y_max < *y_min)) ? b.Y.Min : b.Y.Max;
|
||||||
else
|
else
|
||||||
*x[i] = ((x[i] == x_min && *x_min < *x_max) || (x[i] == x_max && *x_max < *x_min)) ? b.X.Min : b.X.Max;
|
*x[i] = ((x[i] == x_min && *x_min < *x_max) || (x[i] == x_max && *x_max < *x_min)) ? b.X.Min : b.X.Max;
|
||||||
dragging = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool mouse_inside = rect_grab.Contains(ImGui::GetMousePos());
|
||||||
|
const bool mouse_clicked = ImGui::IsMouseClicked(0);
|
||||||
|
const bool mouse_down = ImGui::IsMouseDown(0);
|
||||||
|
if (input && mouse_inside) {
|
||||||
|
if (out_clicked) *out_clicked = *out_clicked || mouse_clicked;
|
||||||
|
if (out_hovered) *out_hovered = true;
|
||||||
|
if (out_held) *out_held = *out_held || mouse_down;
|
||||||
|
}
|
||||||
|
|
||||||
PushPlotClipRect();
|
PushPlotClipRect();
|
||||||
ImDrawList& DrawList = *GetPlotDrawList();
|
ImDrawList& DrawList = *GetPlotDrawList();
|
||||||
if (dragging && no_delay) {
|
if (modified && no_delay) {
|
||||||
for (int i = 0; i < 4; ++i)
|
for (int i = 0; i < 4; ++i)
|
||||||
p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO);
|
p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO);
|
||||||
pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO);
|
pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO);
|
||||||
|
@ -4064,18 +4187,18 @@ bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_m
|
||||||
}
|
}
|
||||||
DrawList.AddRectFilled(rect.Min, rect.Max, col32_a);
|
DrawList.AddRectFilled(rect.Min, rect.Max, col32_a);
|
||||||
DrawList.AddRect(rect.Min, rect.Max, col32);
|
DrawList.AddRect(rect.Min, rect.Max, col32);
|
||||||
if (input && (dragging || rect_grab.Contains(ImGui::GetMousePos()))) {
|
if (input && (modified || mouse_inside)) {
|
||||||
DrawList.AddCircleFilled(pc,DRAG_GRAB_HALF_SIZE,col32);
|
DrawList.AddCircleFilled(pc,DRAG_GRAB_HALF_SIZE,col32);
|
||||||
for (int i = 0; i < 4; ++i)
|
for (int i = 0; i < 4; ++i)
|
||||||
DrawList.AddCircleFilled(p[i],DRAG_GRAB_HALF_SIZE,col32);
|
DrawList.AddCircleFilled(p[i],DRAG_GRAB_HALF_SIZE,col32);
|
||||||
}
|
}
|
||||||
PopPlotClipRect();
|
PopPlotClipRect();
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
return dragging;
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DragRect(int id, ImPlotRect* bounds, const ImVec4& col, ImPlotDragToolFlags flags) {
|
bool DragRect(int id, ImPlotRect* bounds, const ImVec4& col, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {
|
||||||
return DragRect(id, &bounds->X.Min, &bounds->Y.Min,&bounds->X.Max, &bounds->Y.Max, col, flags);
|
return DragRect(id, &bounds->X.Min, &bounds->Y.Min,&bounds->X.Max, &bounds->Y.Max, col, flags, out_clicked, out_hovered, out_held);
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -4171,7 +4294,7 @@ bool BeginDragDropTargetAxis(ImAxis axis) {
|
||||||
bool BeginDragDropTargetLegend() {
|
bool BeginDragDropTargetLegend() {
|
||||||
SetupLock();
|
SetupLock();
|
||||||
ImPlotItemGroup& items = *GImPlot->CurrentItems;
|
ImPlotItemGroup& items = *GImPlot->CurrentItems;
|
||||||
ImRect rect = items.Legend.Rect;
|
ImRect rect = items.Legend.RectClamped;
|
||||||
return ImGui::BeginDragDropTargetCustom(rect, items.ID);
|
return ImGui::BeginDragDropTargetCustom(rect, items.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5110,6 +5233,7 @@ void ShowMetricsWindow(bool* p_popen) {
|
||||||
static bool show_frame_rects = false;
|
static bool show_frame_rects = false;
|
||||||
static bool show_subplot_frame_rects = false;
|
static bool show_subplot_frame_rects = false;
|
||||||
static bool show_subplot_grid_rects = false;
|
static bool show_subplot_grid_rects = false;
|
||||||
|
static bool show_legend_rects = false;
|
||||||
|
|
||||||
ImDrawList& fg = *ImGui::GetForegroundDrawList();
|
ImDrawList& fg = *ImGui::GetForegroundDrawList();
|
||||||
|
|
||||||
|
@ -5134,6 +5258,7 @@ void ShowMetricsWindow(bool* p_popen) {
|
||||||
ImGui::Checkbox("Show Axis Rects", &show_axis_rects);
|
ImGui::Checkbox("Show Axis Rects", &show_axis_rects);
|
||||||
ImGui::Checkbox("Show Subplot Frame Rects", &show_subplot_frame_rects);
|
ImGui::Checkbox("Show Subplot Frame Rects", &show_subplot_frame_rects);
|
||||||
ImGui::Checkbox("Show Subplot Grid Rects", &show_subplot_grid_rects);
|
ImGui::Checkbox("Show Subplot Grid Rects", &show_subplot_grid_rects);
|
||||||
|
ImGui::Checkbox("Show Legend Rects", &show_legend_rects);
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
const int n_plots = gp.Plots.GetBufSize();
|
const int n_plots = gp.Plots.GetBufSize();
|
||||||
|
@ -5155,6 +5280,10 @@ void ShowMetricsWindow(bool* p_popen) {
|
||||||
fg.AddRect(plot->Axes[i].HoverRect.Min, plot->Axes[i].HoverRect.Max, IM_COL32(0,255,0,255));
|
fg.AddRect(plot->Axes[i].HoverRect.Min, plot->Axes[i].HoverRect.Max, IM_COL32(0,255,0,255));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (show_legend_rects && plot->Items.GetLegendCount() > 0) {
|
||||||
|
fg.AddRect(plot->Items.Legend.Rect.Min, plot->Items.Legend.Rect.Max, IM_COL32(255,192,0,255));
|
||||||
|
fg.AddRect(plot->Items.Legend.RectClamped.Min, plot->Items.Legend.RectClamped.Max, IM_COL32(255,128,0,255));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (int p = 0; p < n_subplots; ++p) {
|
for (int p = 0; p < n_subplots; ++p) {
|
||||||
ImPlotSubplot* subplot = gp.Subplots.GetByIndex(p);
|
ImPlotSubplot* subplot = gp.Subplots.GetByIndex(p);
|
||||||
|
@ -5162,6 +5291,10 @@ void ShowMetricsWindow(bool* p_popen) {
|
||||||
fg.AddRect(subplot->FrameRect.Min, subplot->FrameRect.Max, IM_COL32(255,0,0,255));
|
fg.AddRect(subplot->FrameRect.Min, subplot->FrameRect.Max, IM_COL32(255,0,0,255));
|
||||||
if (show_subplot_grid_rects)
|
if (show_subplot_grid_rects)
|
||||||
fg.AddRect(subplot->GridRect.Min, subplot->GridRect.Max, IM_COL32(0,0,255,255));
|
fg.AddRect(subplot->GridRect.Min, subplot->GridRect.Max, IM_COL32(0,0,255,255));
|
||||||
|
if (show_legend_rects && subplot->Items.GetLegendCount() > 0) {
|
||||||
|
fg.AddRect(subplot->Items.Legend.Rect.Min, subplot->Items.Legend.Rect.Max, IM_COL32(255,192,0,255));
|
||||||
|
fg.AddRect(subplot->Items.Legend.RectClamped.Min, subplot->Items.Legend.RectClamped.Max, IM_COL32(255,128,0,255));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ImGui::TreeNode("Plots","Plots (%d)", n_plots)) {
|
if (ImGui::TreeNode("Plots","Plots (%d)", n_plots)) {
|
||||||
for (int p = 0; p < n_plots; ++p) {
|
for (int p = 0; p < n_plots; ++p) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// MIT License
|
// MIT License
|
||||||
|
|
||||||
// Copyright (c) 2022 Evan Pezent
|
// Copyright (c) 2023 Evan Pezent
|
||||||
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
// ImPlot v0.14
|
// ImPlot v0.17
|
||||||
|
|
||||||
// Table of Contents:
|
// Table of Contents:
|
||||||
//
|
//
|
||||||
|
@ -60,7 +60,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// ImPlot version string.
|
// ImPlot version string.
|
||||||
#define IMPLOT_VERSION "0.14"
|
#define IMPLOT_VERSION "0.17"
|
||||||
// Indicates variable should deduced automatically.
|
// Indicates variable should deduced automatically.
|
||||||
#define IMPLOT_AUTO -1
|
#define IMPLOT_AUTO -1
|
||||||
// Special color used to indicate that a color should be deduced automatically.
|
// Special color used to indicate that a color should be deduced automatically.
|
||||||
|
@ -135,10 +135,9 @@ enum ImPlotFlags_ {
|
||||||
ImPlotFlags_NoInputs = 1 << 3, // the user will not be able to interact with the plot
|
ImPlotFlags_NoInputs = 1 << 3, // the user will not be able to interact with the plot
|
||||||
ImPlotFlags_NoMenus = 1 << 4, // the user will not be able to open context menus
|
ImPlotFlags_NoMenus = 1 << 4, // the user will not be able to open context menus
|
||||||
ImPlotFlags_NoBoxSelect = 1 << 5, // the user will not be able to box-select
|
ImPlotFlags_NoBoxSelect = 1 << 5, // the user will not be able to box-select
|
||||||
ImPlotFlags_NoChild = 1 << 6, // a child window region will not be used to capture mouse scroll (can boost performance for single ImGui window applications)
|
ImPlotFlags_NoFrame = 1 << 6, // the ImGui frame will not be rendered
|
||||||
ImPlotFlags_NoFrame = 1 << 7, // the ImGui frame will not be rendered
|
ImPlotFlags_Equal = 1 << 7, // x and y axes pairs will be constrained to have the same units/pixel
|
||||||
ImPlotFlags_Equal = 1 << 8, // x and y axes pairs will be constrained to have the same units/pixel
|
ImPlotFlags_Crosshairs = 1 << 8, // the default mouse cursor will be replaced with a crosshair when hovered
|
||||||
ImPlotFlags_Crosshairs = 1 << 9, // the default mouse cursor will be replaced with a crosshair when hovered
|
|
||||||
ImPlotFlags_CanvasOnly = ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoMouseText
|
ImPlotFlags_CanvasOnly = ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoMouseText
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -287,8 +286,9 @@ enum ImPlotInfLinesFlags_ {
|
||||||
|
|
||||||
// Flags for PlotPieChart
|
// Flags for PlotPieChart
|
||||||
enum ImPlotPieChartFlags_ {
|
enum ImPlotPieChartFlags_ {
|
||||||
ImPlotPieChartFlags_None = 0, // default
|
ImPlotPieChartFlags_None = 0, // default
|
||||||
ImPlotPieChartFlags_Normalize = 1 << 10 // force normalization of pie chart values (i.e. always make a full circle if sum < 0)
|
ImPlotPieChartFlags_Normalize = 1 << 10, // force normalization of pie chart values (i.e. always make a full circle if sum < 0)
|
||||||
|
ImPlotPieChartFlags_IgnoreHidden = 1 << 11 // ignore hidden slices when drawing the pie chart (as if they were not there)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Flags for PlotHeatmap
|
// Flags for PlotHeatmap
|
||||||
|
@ -464,41 +464,43 @@ enum ImPlotBin_ {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Double precision version of ImVec2 used by ImPlot. Extensible by end users.
|
// Double precision version of ImVec2 used by ImPlot. Extensible by end users.
|
||||||
|
IM_MSVC_RUNTIME_CHECKS_OFF
|
||||||
struct ImPlotPoint {
|
struct ImPlotPoint {
|
||||||
double x, y;
|
double x, y;
|
||||||
ImPlotPoint() { x = y = 0.0; }
|
constexpr ImPlotPoint() : x(0.0), y(0.0) { }
|
||||||
ImPlotPoint(double _x, double _y) { x = _x; y = _y; }
|
constexpr ImPlotPoint(double _x, double _y) : x(_x), y(_y) { }
|
||||||
ImPlotPoint(const ImVec2& p) { x = p.x; y = p.y; }
|
constexpr ImPlotPoint(const ImVec2& p) : x((double)p.x), y((double)p.y) { }
|
||||||
double operator[] (size_t idx) const { return (&x)[idx]; }
|
double& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1); return ((double*)(void*)(char*)this)[idx]; }
|
||||||
double& operator[] (size_t idx) { return (&x)[idx]; }
|
double operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1); return ((const double*)(const void*)(const char*)this)[idx]; }
|
||||||
#ifdef IMPLOT_POINT_CLASS_EXTRA
|
#ifdef IMPLOT_POINT_CLASS_EXTRA
|
||||||
IMPLOT_POINT_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h
|
IMPLOT_POINT_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h
|
||||||
// to convert back and forth between your math types and ImPlotPoint.
|
// to convert back and forth between your math types and ImPlotPoint.
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
IM_MSVC_RUNTIME_CHECKS_RESTORE
|
||||||
|
|
||||||
// Range defined by a min/max value.
|
// Range defined by a min/max value.
|
||||||
struct ImPlotRange {
|
struct ImPlotRange {
|
||||||
double Min, Max;
|
double Min, Max;
|
||||||
ImPlotRange() { Min = 0; Max = 0; }
|
constexpr ImPlotRange() : Min(0.0), Max(0.0) { }
|
||||||
ImPlotRange(double _min, double _max) { Min = _min; Max = _max; }
|
constexpr ImPlotRange(double _min, double _max) : Min(_min), Max(_max) { }
|
||||||
bool Contains(double value) const { return value >= Min && value <= Max; }
|
bool Contains(double value) const { return value >= Min && value <= Max; }
|
||||||
double Size() const { return Max - Min; }
|
double Size() const { return Max - Min; }
|
||||||
double Clamp(double value) const { return (value < Min) ? Min : (value > Max) ? Max : value; }
|
double Clamp(double value) const { return (value < Min) ? Min : (value > Max) ? Max : value; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Combination of two range limits for X and Y axes. Also an AABB defined by Min()/Max().
|
// Combination of two range limits for X and Y axes. Also an AABB defined by Min()/Max().
|
||||||
struct ImPlotRect {
|
struct ImPlotRect {
|
||||||
ImPlotRange X, Y;
|
ImPlotRange X, Y;
|
||||||
ImPlotRect() { }
|
constexpr ImPlotRect() : X(0.0,0.0), Y(0.0,0.0) { }
|
||||||
ImPlotRect(double x_min, double x_max, double y_min, double y_max) { X.Min = x_min; X.Max = x_max; Y.Min = y_min; Y.Max = y_max; }
|
constexpr ImPlotRect(double x_min, double x_max, double y_min, double y_max) : X(x_min, x_max), Y(y_min, y_max) { }
|
||||||
bool Contains(const ImPlotPoint& p) const { return Contains(p.x, p.y); }
|
bool Contains(const ImPlotPoint& p) const { return Contains(p.x, p.y); }
|
||||||
bool Contains(double x, double y) const { return X.Contains(x) && Y.Contains(y); }
|
bool Contains(double x, double y) const { return X.Contains(x) && Y.Contains(y); }
|
||||||
ImPlotPoint Size() const { return ImPlotPoint(X.Size(), Y.Size()); }
|
ImPlotPoint Size() const { return ImPlotPoint(X.Size(), Y.Size()); }
|
||||||
ImPlotPoint Clamp(const ImPlotPoint& p) { return Clamp(p.x, p.y); }
|
ImPlotPoint Clamp(const ImPlotPoint& p) { return Clamp(p.x, p.y); }
|
||||||
ImPlotPoint Clamp(double x, double y) { return ImPlotPoint(X.Clamp(x),Y.Clamp(y)); }
|
ImPlotPoint Clamp(double x, double y) { return ImPlotPoint(X.Clamp(x),Y.Clamp(y)); }
|
||||||
ImPlotPoint Min() const { return ImPlotPoint(X.Min, Y.Min); }
|
ImPlotPoint Min() const { return ImPlotPoint(X.Min, Y.Min); }
|
||||||
ImPlotPoint Max() const { return ImPlotPoint(X.Max, Y.Max); }
|
ImPlotPoint Max() const { return ImPlotPoint(X.Max, Y.Max); }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Plot style structure
|
// Plot style structure
|
||||||
|
@ -728,7 +730,7 @@ IMPLOT_API void EndSubplots();
|
||||||
|
|
||||||
// Enables an axis or sets the label and/or flags for an existing axis. Leave #label = nullptr for no label.
|
// Enables an axis or sets the label and/or flags for an existing axis. Leave #label = nullptr for no label.
|
||||||
IMPLOT_API void SetupAxis(ImAxis axis, const char* label=nullptr, ImPlotAxisFlags flags=0);
|
IMPLOT_API void SetupAxis(ImAxis axis, const char* label=nullptr, ImPlotAxisFlags flags=0);
|
||||||
// Sets an axis range limits. If ImPlotCond_Always is used, the axes limits will be locked.
|
// Sets an axis range limits. If ImPlotCond_Always is used, the axes limits will be locked. Inversion with v_min > v_max is not supported; use SetupAxisLimits instead.
|
||||||
IMPLOT_API void SetupAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond = ImPlotCond_Once);
|
IMPLOT_API void SetupAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond = ImPlotCond_Once);
|
||||||
// Links an axis range limits to external values. Set to nullptr for no linkage. The pointer data must remain valid until EndPlot.
|
// Links an axis range limits to external values. Set to nullptr for no linkage. The pointer data must remain valid until EndPlot.
|
||||||
IMPLOT_API void SetupAxisLinks(ImAxis axis, double* link_min, double* link_max);
|
IMPLOT_API void SetupAxisLinks(ImAxis axis, double* link_min, double* link_max);
|
||||||
|
@ -754,7 +756,7 @@ IMPLOT_API void SetupAxes(const char* x_label, const char* y_label, ImPlotAxisFl
|
||||||
// Sets the primary X and Y axes range limits. If ImPlotCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits).
|
// Sets the primary X and Y axes range limits. If ImPlotCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits).
|
||||||
IMPLOT_API void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond = ImPlotCond_Once);
|
IMPLOT_API void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond = ImPlotCond_Once);
|
||||||
|
|
||||||
// Sets up the plot legend.
|
// Sets up the plot legend. This can also be called immediately after BeginSubplots when using ImPlotSubplotFlags_ShareItems.
|
||||||
IMPLOT_API void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags=0);
|
IMPLOT_API void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags=0);
|
||||||
// Set the location of the current plot's mouse position text (default = South|East).
|
// Set the location of the current plot's mouse position text (default = South|East).
|
||||||
IMPLOT_API void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags=0);
|
IMPLOT_API void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags=0);
|
||||||
|
@ -891,6 +893,7 @@ IMPLOT_TMP void PlotStems(const char* label_id, const T* xs, const T* ys, int co
|
||||||
IMPLOT_TMP void PlotInfLines(const char* label_id, const T* values, int count, ImPlotInfLinesFlags flags=0, int offset=0, int stride=sizeof(T));
|
IMPLOT_TMP void PlotInfLines(const char* label_id, const T* values, int count, ImPlotInfLinesFlags flags=0, int offset=0, int stride=sizeof(T));
|
||||||
|
|
||||||
// Plots a pie chart. Center and radius are in plot units. #label_fmt can be set to nullptr for no labels.
|
// Plots a pie chart. Center and radius are in plot units. #label_fmt can be set to nullptr for no labels.
|
||||||
|
IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, ImPlotFormatter fmt, void* fmt_data=nullptr, double angle0=90, ImPlotPieChartFlags flags=0);
|
||||||
IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* label_fmt="%.1f", double angle0=90, ImPlotPieChartFlags flags=0);
|
IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* label_fmt="%.1f", double angle0=90, ImPlotPieChartFlags flags=0);
|
||||||
|
|
||||||
// Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels.
|
// Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels.
|
||||||
|
@ -923,16 +926,18 @@ IMPLOT_API void PlotDummy(const char* label_id, ImPlotDummyFlags flags=0);
|
||||||
|
|
||||||
// The following can be used to render interactive elements and/or annotations.
|
// The following can be used to render interactive elements and/or annotations.
|
||||||
// Like the item plotting functions above, they apply to the current x and y
|
// Like the item plotting functions above, they apply to the current x and y
|
||||||
// axes, which can be changed with `SetAxis/SetAxes`.
|
// axes, which can be changed with `SetAxis/SetAxes`. These functions return true
|
||||||
|
// when user interaction causes the provided coordinates to change. Additional
|
||||||
|
// user interactions can be retrieved through the optional output parameters.
|
||||||
|
|
||||||
// Shows a draggable point at x,y. #col defaults to ImGuiCol_Text.
|
// Shows a draggable point at x,y. #col defaults to ImGuiCol_Text.
|
||||||
IMPLOT_API bool DragPoint(int id, double* x, double* y, const ImVec4& col, float size = 4, ImPlotDragToolFlags flags=0);
|
IMPLOT_API bool DragPoint(int id, double* x, double* y, const ImVec4& col, float size = 4, ImPlotDragToolFlags flags = 0, bool* out_clicked = nullptr, bool* out_hovered = nullptr, bool* held = nullptr);
|
||||||
// Shows a draggable vertical guide line at an x-value. #col defaults to ImGuiCol_Text.
|
// Shows a draggable vertical guide line at an x-value. #col defaults to ImGuiCol_Text.
|
||||||
IMPLOT_API bool DragLineX(int id, double* x, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags=0);
|
IMPLOT_API bool DragLineX(int id, double* x, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags = 0, bool* out_clicked = nullptr, bool* out_hovered = nullptr, bool* held = nullptr);
|
||||||
// Shows a draggable horizontal guide line at a y-value. #col defaults to ImGuiCol_Text.
|
// Shows a draggable horizontal guide line at a y-value. #col defaults to ImGuiCol_Text.
|
||||||
IMPLOT_API bool DragLineY(int id, double* y, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags=0);
|
IMPLOT_API bool DragLineY(int id, double* y, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags = 0, bool* out_clicked = nullptr, bool* out_hovered = nullptr, bool* held = nullptr);
|
||||||
// Shows a draggable and resizeable rectangle.
|
// Shows a draggable and resizeable rectangle.
|
||||||
IMPLOT_API bool DragRect(int id, double* x1, double* y1, double* x2, double* y2, const ImVec4& col, ImPlotDragToolFlags flags=0);
|
IMPLOT_API bool DragRect(int id, double* x1, double* y1, double* x2, double* y2, const ImVec4& col, ImPlotDragToolFlags flags = 0, bool* out_clicked = nullptr, bool* out_hovered = nullptr, bool* held = nullptr);
|
||||||
|
|
||||||
// Shows an annotation callout at a chosen point. Clamping keeps annotations in the plot area. Annotations are always rendered on top.
|
// Shows an annotation callout at a chosen point. Clamping keeps annotations in the plot area. Annotations are always rendered on top.
|
||||||
IMPLOT_API void Annotation(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, bool round = false);
|
IMPLOT_API void Annotation(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, bool round = false);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// MIT License
|
// MIT License
|
||||||
|
|
||||||
// Copyright (c) 2022 Evan Pezent
|
// Copyright (c) 2023 Evan Pezent
|
||||||
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
// ImPlot v0.14
|
// ImPlot v0.17
|
||||||
|
|
||||||
// We define this so that the demo does not accidentally use deprecated API
|
// We define this so that the demo does not accidentally use deprecated API
|
||||||
#ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS
|
#ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
|
@ -610,10 +610,8 @@ void Demo_PieCharts() {
|
||||||
static ImPlotPieChartFlags flags = 0;
|
static ImPlotPieChartFlags flags = 0;
|
||||||
ImGui::SetNextItemWidth(250);
|
ImGui::SetNextItemWidth(250);
|
||||||
ImGui::DragFloat4("Values", data1, 0.01f, 0, 1);
|
ImGui::DragFloat4("Values", data1, 0.01f, 0, 1);
|
||||||
if ((data1[0] + data1[1] + data1[2] + data1[3]) < 1) {
|
CHECKBOX_FLAG(flags, ImPlotPieChartFlags_Normalize);
|
||||||
ImGui::SameLine();
|
CHECKBOX_FLAG(flags, ImPlotPieChartFlags_IgnoreHidden);
|
||||||
CHECKBOX_FLAG(flags,ImPlotPieChartFlags_Normalize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImPlot::BeginPlot("##Pie1", ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) {
|
if (ImPlot::BeginPlot("##Pie1", ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) {
|
||||||
ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations);
|
ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations);
|
||||||
|
@ -1147,7 +1145,7 @@ void Demo_MultipleAxes() {
|
||||||
ImPlot::SetAxes(ImAxis_X1, ImAxis_Y2);
|
ImPlot::SetAxes(ImAxis_X1, ImAxis_Y2);
|
||||||
ImPlot::PlotLine("f(x) = cos(x)*.2+.5", xs, ys2, 1001);
|
ImPlot::PlotLine("f(x) = cos(x)*.2+.5", xs, ys2, 1001);
|
||||||
}
|
}
|
||||||
if (y3_axis) {
|
if (x2_axis && y3_axis) {
|
||||||
ImPlot::SetAxes(ImAxis_X2, ImAxis_Y3);
|
ImPlot::SetAxes(ImAxis_X2, ImAxis_Y3);
|
||||||
ImPlot::PlotLine("f(x) = sin(x+.5)*100+200 ", xs2, ys3, 1001);
|
ImPlot::PlotLine("f(x) = sin(x+.5)*100+200 ", xs2, ys3, 1001);
|
||||||
}
|
}
|
||||||
|
@ -1264,7 +1262,7 @@ ImPlotPoint SinewaveGetter(int i, void* data) {
|
||||||
|
|
||||||
void Demo_SubplotsSizing() {
|
void Demo_SubplotsSizing() {
|
||||||
|
|
||||||
static ImPlotSubplotFlags flags = ImPlotSubplotFlags_None;
|
static ImPlotSubplotFlags flags = ImPlotSubplotFlags_ShareItems|ImPlotSubplotFlags_NoLegend;
|
||||||
ImGui::CheckboxFlags("ImPlotSubplotFlags_NoResize", (unsigned int*)&flags, ImPlotSubplotFlags_NoResize);
|
ImGui::CheckboxFlags("ImPlotSubplotFlags_NoResize", (unsigned int*)&flags, ImPlotSubplotFlags_NoResize);
|
||||||
ImGui::CheckboxFlags("ImPlotSubplotFlags_NoTitle", (unsigned int*)&flags, ImPlotSubplotFlags_NoTitle);
|
ImGui::CheckboxFlags("ImPlotSubplotFlags_NoTitle", (unsigned int*)&flags, ImPlotSubplotFlags_NoTitle);
|
||||||
|
|
||||||
|
@ -1272,17 +1270,26 @@ void Demo_SubplotsSizing() {
|
||||||
static int cols = 3;
|
static int cols = 3;
|
||||||
ImGui::SliderInt("Rows",&rows,1,5);
|
ImGui::SliderInt("Rows",&rows,1,5);
|
||||||
ImGui::SliderInt("Cols",&cols,1,5);
|
ImGui::SliderInt("Cols",&cols,1,5);
|
||||||
|
if (rows < 1 || cols < 1) {
|
||||||
|
ImGui::TextColored(ImVec4(1,0,0,1), "Nice try, but the number of rows and columns must be greater than 0!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
static float rratios[] = {5,1,1,1,1,1};
|
static float rratios[] = {5,1,1,1,1,1};
|
||||||
static float cratios[] = {5,1,1,1,1,1};
|
static float cratios[] = {5,1,1,1,1,1};
|
||||||
ImGui::DragScalarN("Row Ratios",ImGuiDataType_Float,rratios,rows,0.01f,nullptr);
|
ImGui::DragScalarN("Row Ratios",ImGuiDataType_Float,rratios,rows,0.01f,nullptr);
|
||||||
ImGui::DragScalarN("Col Ratios",ImGuiDataType_Float,cratios,cols,0.01f,nullptr);
|
ImGui::DragScalarN("Col Ratios",ImGuiDataType_Float,cratios,cols,0.01f,nullptr);
|
||||||
if (ImPlot::BeginSubplots("My Subplots", rows, cols, ImVec2(-1,400), flags, rratios, cratios)) {
|
if (ImPlot::BeginSubplots("My Subplots", rows, cols, ImVec2(-1,400), flags, rratios, cratios)) {
|
||||||
|
int id = 0;
|
||||||
for (int i = 0; i < rows*cols; ++i) {
|
for (int i = 0; i < rows*cols; ++i) {
|
||||||
if (ImPlot::BeginPlot("",ImVec2(),ImPlotFlags_NoLegend)) {
|
if (ImPlot::BeginPlot("",ImVec2(),ImPlotFlags_NoLegend)) {
|
||||||
ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations);
|
ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations);
|
||||||
float fi = 0.01f * (i+1);
|
float fi = 0.01f * (i+1);
|
||||||
ImPlot::SetNextLineStyle(SampleColormap((float)i/(float)(rows*cols-1),ImPlotColormap_Jet));
|
if (rows*cols > 1) {
|
||||||
ImPlot::PlotLineG("data",SinewaveGetter,&fi,1000);
|
ImPlot::SetNextLineStyle(SampleColormap((float)i/(float)(rows*cols-1),ImPlotColormap_Jet));
|
||||||
|
}
|
||||||
|
char label[16];
|
||||||
|
snprintf(label, sizeof(label), "data%d", id++);
|
||||||
|
ImPlot::PlotLineG(label,SinewaveGetter,&fi,1000);
|
||||||
ImPlot::EndPlot();
|
ImPlot::EndPlot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1302,6 +1309,7 @@ void Demo_SubplotItemSharing() {
|
||||||
static int id[] = {0,1,2,3,4,5};
|
static int id[] = {0,1,2,3,4,5};
|
||||||
static int curj = -1;
|
static int curj = -1;
|
||||||
if (ImPlot::BeginSubplots("##ItemSharing", rows, cols, ImVec2(-1,400), flags)) {
|
if (ImPlot::BeginSubplots("##ItemSharing", rows, cols, ImVec2(-1,400), flags)) {
|
||||||
|
ImPlot::SetupLegend(ImPlotLocation_South, ImPlotLegendFlags_Sort|ImPlotLegendFlags_Horizontal);
|
||||||
for (int i = 0; i < rows*cols; ++i) {
|
for (int i = 0; i < rows*cols; ++i) {
|
||||||
if (ImPlot::BeginPlot("")) {
|
if (ImPlot::BeginPlot("")) {
|
||||||
float fc = 0.01f;
|
float fc = 0.01f;
|
||||||
|
@ -1376,6 +1384,9 @@ void Demo_LegendOptions() {
|
||||||
ImGui::SliderFloat2("LegendInnerPadding", (float*)&GetStyle().LegendInnerPadding, 0.0f, 10.0f, "%.0f");
|
ImGui::SliderFloat2("LegendInnerPadding", (float*)&GetStyle().LegendInnerPadding, 0.0f, 10.0f, "%.0f");
|
||||||
ImGui::SliderFloat2("LegendSpacing", (float*)&GetStyle().LegendSpacing, 0.0f, 5.0f, "%.0f");
|
ImGui::SliderFloat2("LegendSpacing", (float*)&GetStyle().LegendSpacing, 0.0f, 5.0f, "%.0f");
|
||||||
|
|
||||||
|
static int num_dummy_items = 25;
|
||||||
|
ImGui::SliderInt("Num Dummy Items (Demo Scrolling)", &num_dummy_items, 0, 100);
|
||||||
|
|
||||||
if (ImPlot::BeginPlot("##Legend",ImVec2(-1,0))) {
|
if (ImPlot::BeginPlot("##Legend",ImVec2(-1,0))) {
|
||||||
ImPlot::SetupLegend(loc, flags);
|
ImPlot::SetupLegend(loc, flags);
|
||||||
static MyImPlot::WaveData data1(0.001, 0.2, 4, 0.2);
|
static MyImPlot::WaveData data1(0.001, 0.2, 4, 0.2);
|
||||||
|
@ -1384,12 +1395,17 @@ void Demo_LegendOptions() {
|
||||||
static MyImPlot::WaveData data4(0.001, 0.2, 4, 0.8);
|
static MyImPlot::WaveData data4(0.001, 0.2, 4, 0.8);
|
||||||
static MyImPlot::WaveData data5(0.001, 0.2, 4, 1.0);
|
static MyImPlot::WaveData data5(0.001, 0.2, 4, 1.0);
|
||||||
|
|
||||||
ImPlot::PlotLineG("Item B", MyImPlot::SawWave, &data1, 1000); // "Item B" added to legend
|
ImPlot::PlotLineG("Item 002", MyImPlot::SawWave, &data1, 1000); // "Item B" added to legend
|
||||||
ImPlot::PlotLineG("Item A##IDText", MyImPlot::SawWave, &data2, 1000); // "Item A" added to legend, text after ## used for ID only
|
ImPlot::PlotLineG("Item 001##IDText", MyImPlot::SawWave, &data2, 1000); // "Item A" added to legend, text after ## used for ID only
|
||||||
ImPlot::PlotLineG("##NotListed", MyImPlot::SawWave, &data3, 1000); // plotted, but not added to legend
|
ImPlot::PlotLineG("##NotListed", MyImPlot::SawWave, &data3, 1000); // plotted, but not added to legend
|
||||||
ImPlot::PlotLineG("Item C", MyImPlot::SawWave, &data4, 1000); // "Item C" added to legend
|
ImPlot::PlotLineG("Item 003", MyImPlot::SawWave, &data4, 1000); // "Item C" added to legend
|
||||||
ImPlot::PlotLineG("Item C", MyImPlot::SawWave, &data5, 1000); // combined with previous "Item C"
|
ImPlot::PlotLineG("Item 003", MyImPlot::SawWave, &data5, 1000); // combined with previous "Item C"
|
||||||
|
|
||||||
|
for (int i = 0; i < num_dummy_items; ++i) {
|
||||||
|
char label[16];
|
||||||
|
snprintf(label, sizeof(label), "Item %03d", i+4);
|
||||||
|
ImPlot::PlotDummy(label);
|
||||||
|
}
|
||||||
ImPlot::EndPlot();
|
ImPlot::EndPlot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1403,15 +1419,18 @@ void Demo_DragPoints() {
|
||||||
ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine();
|
ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine();
|
||||||
ImGui::CheckboxFlags("NoInput", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs);
|
ImGui::CheckboxFlags("NoInput", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs);
|
||||||
ImPlotAxisFlags ax_flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoTickMarks;
|
ImPlotAxisFlags ax_flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoTickMarks;
|
||||||
|
bool clicked[4] = {false, false, false, false};
|
||||||
|
bool hovered[4] = {false, false, false, false};
|
||||||
|
bool held[4] = {false, false, false, false};
|
||||||
if (ImPlot::BeginPlot("##Bezier",ImVec2(-1,0),ImPlotFlags_CanvasOnly)) {
|
if (ImPlot::BeginPlot("##Bezier",ImVec2(-1,0),ImPlotFlags_CanvasOnly)) {
|
||||||
ImPlot::SetupAxes(nullptr,nullptr,ax_flags,ax_flags);
|
ImPlot::SetupAxes(nullptr,nullptr,ax_flags,ax_flags);
|
||||||
ImPlot::SetupAxesLimits(0,1,0,1);
|
ImPlot::SetupAxesLimits(0,1,0,1);
|
||||||
static ImPlotPoint P[] = {ImPlotPoint(.05f,.05f), ImPlotPoint(0.2,0.4), ImPlotPoint(0.8,0.6), ImPlotPoint(.95f,.95f)};
|
static ImPlotPoint P[] = {ImPlotPoint(.05f,.05f), ImPlotPoint(0.2,0.4), ImPlotPoint(0.8,0.6), ImPlotPoint(.95f,.95f)};
|
||||||
|
|
||||||
ImPlot::DragPoint(0,&P[0].x,&P[0].y, ImVec4(0,0.9f,0,1),4,flags);
|
ImPlot::DragPoint(0,&P[0].x,&P[0].y, ImVec4(0,0.9f,0,1),4,flags, &clicked[0], &hovered[0], &held[0]);
|
||||||
ImPlot::DragPoint(1,&P[1].x,&P[1].y, ImVec4(1,0.5f,1,1),4,flags);
|
ImPlot::DragPoint(1,&P[1].x,&P[1].y, ImVec4(1,0.5f,1,1),4,flags, &clicked[1], &hovered[1], &held[1]);
|
||||||
ImPlot::DragPoint(2,&P[2].x,&P[2].y, ImVec4(0,0.5f,1,1),4,flags);
|
ImPlot::DragPoint(2,&P[2].x,&P[2].y, ImVec4(0,0.5f,1,1),4,flags, &clicked[2], &hovered[2], &held[2]);
|
||||||
ImPlot::DragPoint(3,&P[3].x,&P[3].y, ImVec4(0,0.9f,0,1),4,flags);
|
ImPlot::DragPoint(3,&P[3].x,&P[3].y, ImVec4(0,0.9f,0,1),4,flags, &clicked[3], &hovered[3], &held[3]);
|
||||||
|
|
||||||
static ImPlotPoint B[100];
|
static ImPlotPoint B[100];
|
||||||
for (int i = 0; i < 100; ++i) {
|
for (int i = 0; i < 100; ++i) {
|
||||||
|
@ -1424,14 +1443,12 @@ void Demo_DragPoints() {
|
||||||
B[i] = ImPlotPoint(w1*P[0].x + w2*P[1].x + w3*P[2].x + w4*P[3].x, w1*P[0].y + w2*P[1].y + w3*P[2].y + w4*P[3].y);
|
B[i] = ImPlotPoint(w1*P[0].x + w2*P[1].x + w3*P[2].x + w4*P[3].x, w1*P[0].y + w2*P[1].y + w3*P[2].y + w4*P[3].y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImPlot::SetNextLineStyle(ImVec4(1,0.5f,1,1),hovered[1]||held[1] ? 2.0f : 1.0f);
|
||||||
ImPlot::SetNextLineStyle(ImVec4(1,0.5f,1,1));
|
|
||||||
ImPlot::PlotLine("##h1",&P[0].x, &P[0].y, 2, 0, 0, sizeof(ImPlotPoint));
|
ImPlot::PlotLine("##h1",&P[0].x, &P[0].y, 2, 0, 0, sizeof(ImPlotPoint));
|
||||||
ImPlot::SetNextLineStyle(ImVec4(0,0.5f,1,1));
|
ImPlot::SetNextLineStyle(ImVec4(0,0.5f,1,1), hovered[2]||held[2] ? 2.0f : 1.0f);
|
||||||
ImPlot::PlotLine("##h2",&P[2].x, &P[2].y, 2, 0, 0, sizeof(ImPlotPoint));
|
ImPlot::PlotLine("##h2",&P[2].x, &P[2].y, 2, 0, 0, sizeof(ImPlotPoint));
|
||||||
ImPlot::SetNextLineStyle(ImVec4(0,0.9f,0,1), 2);
|
ImPlot::SetNextLineStyle(ImVec4(0,0.9f,0,1), hovered[0]||held[0]||hovered[3]||held[3] ? 3.0f : 2.0f);
|
||||||
ImPlot::PlotLine("##bez",&B[0].x, &B[0].y, 100, 0, 0, sizeof(ImPlotPoint));
|
ImPlot::PlotLine("##bez",&B[0].x, &B[0].y, 100, 0, 0, sizeof(ImPlotPoint));
|
||||||
|
|
||||||
ImPlot::EndPlot();
|
ImPlot::EndPlot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1445,6 +1462,9 @@ void Demo_DragLines() {
|
||||||
static double y1 = 0.25;
|
static double y1 = 0.25;
|
||||||
static double y2 = 0.75;
|
static double y2 = 0.75;
|
||||||
static double f = 0.1;
|
static double f = 0.1;
|
||||||
|
bool clicked = false;
|
||||||
|
bool hovered = false;
|
||||||
|
bool held = false;
|
||||||
static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None;
|
static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None;
|
||||||
ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine();
|
ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine();
|
||||||
ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine();
|
ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine();
|
||||||
|
@ -1460,8 +1480,9 @@ void Demo_DragLines() {
|
||||||
xs[i] = (x2+x1)/2+fabs(x2-x1)*(i/1000.0f - 0.5f);
|
xs[i] = (x2+x1)/2+fabs(x2-x1)*(i/1000.0f - 0.5f);
|
||||||
ys[i] = (y1+y2)/2+fabs(y2-y1)/2*sin(f*i/10);
|
ys[i] = (y1+y2)/2+fabs(y2-y1)/2*sin(f*i/10);
|
||||||
}
|
}
|
||||||
|
ImPlot::DragLineY(120482,&f,ImVec4(1,0.5f,1,1),1,flags, &clicked, &hovered, &held);
|
||||||
|
ImPlot::SetNextLineStyle(IMPLOT_AUTO_COL, hovered||held ? 2.0f : 1.0f);
|
||||||
ImPlot::PlotLine("Interactive Data", xs, ys, 1000);
|
ImPlot::PlotLine("Interactive Data", xs, ys, 1000);
|
||||||
ImPlot::DragLineY(120482,&f,ImVec4(1,0.5f,1,1),1,flags);
|
|
||||||
ImPlot::EndPlot();
|
ImPlot::EndPlot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1476,6 +1497,9 @@ void Demo_DragRects() {
|
||||||
static float y_data3[512];
|
static float y_data3[512];
|
||||||
static float sampling_freq = 44100;
|
static float sampling_freq = 44100;
|
||||||
static float freq = 500;
|
static float freq = 500;
|
||||||
|
bool clicked = false;
|
||||||
|
bool hovered = false;
|
||||||
|
bool held = false;
|
||||||
for (size_t i = 0; i < 512; ++i) {
|
for (size_t i = 0; i < 512; ++i) {
|
||||||
const float t = i / sampling_freq;
|
const float t = i / sampling_freq;
|
||||||
x_data[i] = t;
|
x_data[i] = t;
|
||||||
|
@ -1485,6 +1509,7 @@ void Demo_DragRects() {
|
||||||
y_data3[i] = y_data2[i] * -0.6f + sinf(3 * arg) * 0.4f;
|
y_data3[i] = y_data2[i] * -0.6f + sinf(3 * arg) * 0.4f;
|
||||||
}
|
}
|
||||||
ImGui::BulletText("Click and drag the edges, corners, and center of the rect.");
|
ImGui::BulletText("Click and drag the edges, corners, and center of the rect.");
|
||||||
|
ImGui::BulletText("Double click edges to expand rect to plot extents.");
|
||||||
static ImPlotRect rect(0.0025,0.0045,0,0.5);
|
static ImPlotRect rect(0.0025,0.0045,0,0.5);
|
||||||
static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None;
|
static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None;
|
||||||
ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine();
|
ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine();
|
||||||
|
@ -1497,9 +1522,11 @@ void Demo_DragRects() {
|
||||||
ImPlot::PlotLine("Signal 1", x_data, y_data1, 512);
|
ImPlot::PlotLine("Signal 1", x_data, y_data1, 512);
|
||||||
ImPlot::PlotLine("Signal 2", x_data, y_data2, 512);
|
ImPlot::PlotLine("Signal 2", x_data, y_data2, 512);
|
||||||
ImPlot::PlotLine("Signal 3", x_data, y_data3, 512);
|
ImPlot::PlotLine("Signal 3", x_data, y_data3, 512);
|
||||||
ImPlot::DragRect(0,&rect.X.Min,&rect.Y.Min,&rect.X.Max,&rect.Y.Max,ImVec4(1,0,1,1),flags);
|
ImPlot::DragRect(0,&rect.X.Min,&rect.Y.Min,&rect.X.Max,&rect.Y.Max,ImVec4(1,0,1,1),flags, &clicked, &hovered, &held);
|
||||||
ImPlot::EndPlot();
|
ImPlot::EndPlot();
|
||||||
}
|
}
|
||||||
|
ImVec4 bg_col = held ? ImVec4(0.5f,0,0.5f,1) : (hovered ? ImVec4(0.25f,0,0.25f,1) : ImPlot::GetStyle().Colors[ImPlotCol_PlotBg]);
|
||||||
|
ImPlot::PushStyleColor(ImPlotCol_PlotBg, bg_col);
|
||||||
if (ImPlot::BeginPlot("##rect",ImVec2(-1,150), ImPlotFlags_CanvasOnly)) {
|
if (ImPlot::BeginPlot("##rect",ImVec2(-1,150), ImPlotFlags_CanvasOnly)) {
|
||||||
ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations);
|
ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations);
|
||||||
ImPlot::SetupAxesLimits(rect.X.Min, rect.X.Max, rect.Y.Min, rect.Y.Max, ImGuiCond_Always);
|
ImPlot::SetupAxesLimits(rect.X.Min, rect.X.Max, rect.Y.Min, rect.Y.Max, ImGuiCond_Always);
|
||||||
|
@ -1508,6 +1535,8 @@ void Demo_DragRects() {
|
||||||
ImPlot::PlotLine("Signal 3", x_data, y_data3, 512);
|
ImPlot::PlotLine("Signal 3", x_data, y_data3, 512);
|
||||||
ImPlot::EndPlot();
|
ImPlot::EndPlot();
|
||||||
}
|
}
|
||||||
|
ImPlot::PopStyleColor();
|
||||||
|
ImGui::Text("Rect is %sclicked, %shovered, %sheld", clicked ? "" : "not ", hovered ? "" : "not ", held ? "" : "not ");
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -2299,7 +2328,7 @@ ImPlotPoint Spiral(int idx, void*) {
|
||||||
|
|
||||||
void Sparkline(const char* id, const float* values, int count, float min_v, float max_v, int offset, const ImVec4& col, const ImVec2& size) {
|
void Sparkline(const char* id, const float* values, int count, float min_v, float max_v, int offset, const ImVec4& col, const ImVec2& size) {
|
||||||
ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, ImVec2(0,0));
|
ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, ImVec2(0,0));
|
||||||
if (ImPlot::BeginPlot(id,size,ImPlotFlags_CanvasOnly|ImPlotFlags_NoChild)) {
|
if (ImPlot::BeginPlot(id,size,ImPlotFlags_CanvasOnly)) {
|
||||||
ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations);
|
ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations);
|
||||||
ImPlot::SetupAxesLimits(0, count - 1, min_v, max_v, ImGuiCond_Always);
|
ImPlot::SetupAxesLimits(0, count - 1, min_v, max_v, ImGuiCond_Always);
|
||||||
ImPlot::SetNextLineStyle(col);
|
ImPlot::SetNextLineStyle(col);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// MIT License
|
// MIT License
|
||||||
|
|
||||||
// Copyright (c) 2022 Evan Pezent
|
// Copyright (c) 2023 Evan Pezent
|
||||||
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
// ImPlot v0.14
|
// ImPlot v0.17
|
||||||
|
|
||||||
// You may use this file to debug, understand or extend ImPlot features but we
|
// You may use this file to debug, understand or extend ImPlot features but we
|
||||||
// don't provide any guarantee of forward compatibility!
|
// don't provide any guarantee of forward compatibility!
|
||||||
|
@ -907,8 +907,9 @@ struct ImPlotAxis
|
||||||
}
|
}
|
||||||
|
|
||||||
void PullLinks() {
|
void PullLinks() {
|
||||||
if (LinkedMin) { SetMin(*LinkedMin,true); }
|
if (LinkedMin && LinkedMax) { SetRange(*LinkedMin, *LinkedMax); }
|
||||||
if (LinkedMax) { SetMax(*LinkedMax,true); }
|
else if (LinkedMin) { SetMin(*LinkedMin,true); }
|
||||||
|
else if (LinkedMax) { SetMax(*LinkedMax,true); }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -965,9 +966,11 @@ struct ImPlotLegend
|
||||||
ImPlotLegendFlags PreviousFlags;
|
ImPlotLegendFlags PreviousFlags;
|
||||||
ImPlotLocation Location;
|
ImPlotLocation Location;
|
||||||
ImPlotLocation PreviousLocation;
|
ImPlotLocation PreviousLocation;
|
||||||
|
ImVec2 Scroll;
|
||||||
ImVector<int> Indices;
|
ImVector<int> Indices;
|
||||||
ImGuiTextBuffer Labels;
|
ImGuiTextBuffer Labels;
|
||||||
ImRect Rect;
|
ImRect Rect;
|
||||||
|
ImRect RectClamped;
|
||||||
bool Hovered;
|
bool Hovered;
|
||||||
bool Held;
|
bool Held;
|
||||||
bool CanGoInside;
|
bool CanGoInside;
|
||||||
|
@ -977,6 +980,7 @@ struct ImPlotLegend
|
||||||
CanGoInside = true;
|
CanGoInside = true;
|
||||||
Hovered = Held = false;
|
Hovered = Held = false;
|
||||||
Location = PreviousLocation = ImPlotLocation_NorthWest;
|
Location = PreviousLocation = ImPlotLocation_NorthWest;
|
||||||
|
Scroll = ImVec2(0,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reset() { Indices.shrink(0); Labels.Buf.shrink(0); }
|
void Reset() { Indices.shrink(0); Labels.Buf.shrink(0); }
|
||||||
|
@ -1136,7 +1140,6 @@ struct ImPlotSubplot {
|
||||||
ID = 0;
|
ID = 0;
|
||||||
Flags = PreviousFlags = ImPlotSubplotFlags_None;
|
Flags = PreviousFlags = ImPlotSubplotFlags_None;
|
||||||
Rows = Cols = CurrentIdx = 0;
|
Rows = Cols = CurrentIdx = 0;
|
||||||
FrameHovered = false;
|
|
||||||
Items.Legend.Location = ImPlotLocation_North;
|
Items.Legend.Location = ImPlotLocation_North;
|
||||||
Items.Legend.Flags = ImPlotLegendFlags_Horizontal|ImPlotLegendFlags_Outside;
|
Items.Legend.Flags = ImPlotLegendFlags_Horizontal|ImPlotLegendFlags_Outside;
|
||||||
Items.Legend.CanGoInside = false;
|
Items.Legend.CanGoInside = false;
|
||||||
|
@ -1215,9 +1218,6 @@ struct ImPlotContext {
|
||||||
ImPlotAnnotationCollection Annotations;
|
ImPlotAnnotationCollection Annotations;
|
||||||
ImPlotTagCollection Tags;
|
ImPlotTagCollection Tags;
|
||||||
|
|
||||||
// Flags
|
|
||||||
bool ChildWindowMade;
|
|
||||||
|
|
||||||
// Style and Colormaps
|
// Style and Colormaps
|
||||||
ImPlotStyle Style;
|
ImPlotStyle Style;
|
||||||
ImVector<ImGuiColorMod> ColorModifiers;
|
ImVector<ImGuiColorMod> ColorModifiers;
|
||||||
|
@ -1414,13 +1414,15 @@ IMPLOT_API void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bo
|
||||||
|
|
||||||
// Gets the position of an inner rect that is located inside of an outer rect according to an ImPlotLocation and padding amount.
|
// Gets the position of an inner rect that is located inside of an outer rect according to an ImPlotLocation and padding amount.
|
||||||
IMPLOT_API ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation location, const ImVec2& pad = ImVec2(0,0));
|
IMPLOT_API ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation location, const ImVec2& pad = ImVec2(0,0));
|
||||||
// Calculates the bounding box size of a legend
|
// Calculates the bounding box size of a legend _before_ clipping.
|
||||||
IMPLOT_API ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical);
|
IMPLOT_API ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical);
|
||||||
|
// Clips calculated legend size
|
||||||
|
IMPLOT_API bool ClampLegendRect(ImRect& legend_rect, const ImRect& outer_rect, const ImVec2& pad);
|
||||||
// Renders legend entries into a bounding box
|
// Renders legend entries into a bounding box
|
||||||
IMPLOT_API bool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool interactable, const ImVec2& pad, const ImVec2& spacing, bool vertical, ImDrawList& DrawList);
|
IMPLOT_API bool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool interactable, const ImVec2& pad, const ImVec2& spacing, bool vertical, ImDrawList& DrawList);
|
||||||
// Shows an alternate legend for the plot identified by #title_id, outside of the plot frame (can be called before or after of Begin/EndPlot but must occur in the same ImGui window!).
|
// Shows an alternate legend for the plot identified by #title_id, outside of the plot frame (can be called before or after of Begin/EndPlot but must occur in the same ImGui window! This is not thoroughly tested nor scrollable!).
|
||||||
IMPLOT_API void ShowAltLegend(const char* title_id, bool vertical = true, const ImVec2 size = ImVec2(0,0), bool interactable = true);
|
IMPLOT_API void ShowAltLegend(const char* title_id, bool vertical = true, const ImVec2 size = ImVec2(0,0), bool interactable = true);
|
||||||
// Shows an legends's context menu.
|
// Shows a legend's context menu.
|
||||||
IMPLOT_API bool ShowLegendContextMenu(ImPlotLegend& legend, bool visible);
|
IMPLOT_API bool ShowLegendContextMenu(ImPlotLegend& legend, bool visible);
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// MIT License
|
// MIT License
|
||||||
|
|
||||||
// Copyright (c) 2020 Evan Pezent
|
// Copyright (c) 2023 Evan Pezent
|
||||||
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
// ImPlot v0.14
|
// ImPlot v0.17
|
||||||
|
|
||||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||||
#include "implot.h"
|
#include "implot.h"
|
||||||
|
@ -98,11 +98,11 @@ static IMPLOT_INLINE float ImInvSqrt(float x) { return 1.0f / sqrtf(x); }
|
||||||
#define _CAT(x, y) _CAT_(x, y)
|
#define _CAT(x, y) _CAT_(x, y)
|
||||||
#define _CAT_(x,y) x ## y
|
#define _CAT_(x,y) x ## y
|
||||||
#define _INSTANTIATE_FOR_NUMERIC_TYPES(chain) _CAT(_INSTANTIATE_FOR_NUMERIC_TYPES_1 chain, _END)
|
#define _INSTANTIATE_FOR_NUMERIC_TYPES(chain) _CAT(_INSTANTIATE_FOR_NUMERIC_TYPES_1 chain, _END)
|
||||||
#define _INSTANTIATE_FOR_NUMERIC_TYPES_1(T) INSTANTIATE_MACRO(T); _INSTANTIATE_FOR_NUMERIC_TYPES_2
|
#define _INSTANTIATE_FOR_NUMERIC_TYPES_1(T) INSTANTIATE_MACRO(T) _INSTANTIATE_FOR_NUMERIC_TYPES_2
|
||||||
#define _INSTANTIATE_FOR_NUMERIC_TYPES_2(T) INSTANTIATE_MACRO(T); _INSTANTIATE_FOR_NUMERIC_TYPES_1
|
#define _INSTANTIATE_FOR_NUMERIC_TYPES_2(T) INSTANTIATE_MACRO(T) _INSTANTIATE_FOR_NUMERIC_TYPES_1
|
||||||
#define _INSTANTIATE_FOR_NUMERIC_TYPES_1_END
|
#define _INSTANTIATE_FOR_NUMERIC_TYPES_1_END
|
||||||
#define _INSTANTIATE_FOR_NUMERIC_TYPES_2_END
|
#define _INSTANTIATE_FOR_NUMERIC_TYPES_2_END
|
||||||
#define CALL_INSTANTIATE_FOR_NUMERIC_TYPES() _INSTANTIATE_FOR_NUMERIC_TYPES(IMPLOT_NUMERIC_TYPES);
|
#define CALL_INSTANTIATE_FOR_NUMERIC_TYPES() _INSTANTIATE_FOR_NUMERIC_TYPES(IMPLOT_NUMERIC_TYPES)
|
||||||
|
|
||||||
namespace ImPlot {
|
namespace ImPlot {
|
||||||
|
|
||||||
|
@ -1287,7 +1287,7 @@ struct RendererShaded : RendererBase {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const int intersect = (P11.y > P12.y && P22.y > P21.y) || (P12.y > P11.y && P21.y > P22.y);
|
const int intersect = (P11.y > P12.y && P22.y > P21.y) || (P12.y > P11.y && P21.y > P22.y);
|
||||||
ImVec2 intersection = Intersection(P11,P21,P12,P22);
|
const ImVec2 intersection = intersect == 0 ? ImVec2(0,0) : Intersection(P11,P21,P12,P22);
|
||||||
draw_list._VtxWritePtr[0].pos = P11;
|
draw_list._VtxWritePtr[0].pos = P11;
|
||||||
draw_list._VtxWritePtr[0].uv = UV;
|
draw_list._VtxWritePtr[0].uv = UV;
|
||||||
draw_list._VtxWritePtr[0].col = Col;
|
draw_list._VtxWritePtr[0].col = Col;
|
||||||
|
@ -1569,6 +1569,10 @@ void RenderMarkers(const _Getter& getter, ImPlotMarker marker, float size, bool
|
||||||
template <typename _Getter>
|
template <typename _Getter>
|
||||||
void PlotLineEx(const char* label_id, const _Getter& getter, ImPlotLineFlags flags) {
|
void PlotLineEx(const char* label_id, const _Getter& getter, ImPlotLineFlags flags) {
|
||||||
if (BeginItemEx(label_id, Fitter1<_Getter>(getter), flags, ImPlotCol_Line)) {
|
if (BeginItemEx(label_id, Fitter1<_Getter>(getter), flags, ImPlotCol_Line)) {
|
||||||
|
if (getter.Count <= 0) {
|
||||||
|
EndItem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const ImPlotNextItemData& s = GetItemData();
|
const ImPlotNextItemData& s = GetItemData();
|
||||||
if (getter.Count > 1) {
|
if (getter.Count > 1) {
|
||||||
if (ImHasFlag(flags, ImPlotLineFlags_Shaded) && s.RenderFill) {
|
if (ImHasFlag(flags, ImPlotLineFlags_Shaded) && s.RenderFill) {
|
||||||
|
@ -1640,6 +1644,10 @@ void PlotLineG(const char* label_id, ImPlotGetter getter_func, void* data, int c
|
||||||
template <typename Getter>
|
template <typename Getter>
|
||||||
void PlotScatterEx(const char* label_id, const Getter& getter, ImPlotScatterFlags flags) {
|
void PlotScatterEx(const char* label_id, const Getter& getter, ImPlotScatterFlags flags) {
|
||||||
if (BeginItemEx(label_id, Fitter1<Getter>(getter), flags, ImPlotCol_MarkerOutline)) {
|
if (BeginItemEx(label_id, Fitter1<Getter>(getter), flags, ImPlotCol_MarkerOutline)) {
|
||||||
|
if (getter.Count <= 0) {
|
||||||
|
EndItem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const ImPlotNextItemData& s = GetItemData();
|
const ImPlotNextItemData& s = GetItemData();
|
||||||
ImPlotMarker marker = s.Marker == ImPlotMarker_None ? ImPlotMarker_Circle: s.Marker;
|
ImPlotMarker marker = s.Marker == ImPlotMarker_None ? ImPlotMarker_Circle: s.Marker;
|
||||||
if (marker != ImPlotMarker_None) {
|
if (marker != ImPlotMarker_None) {
|
||||||
|
@ -1686,8 +1694,12 @@ void PlotScatterG(const char* label_id, ImPlotGetter getter_func, void* data, in
|
||||||
template <typename Getter>
|
template <typename Getter>
|
||||||
void PlotStairsEx(const char* label_id, const Getter& getter, ImPlotStairsFlags flags) {
|
void PlotStairsEx(const char* label_id, const Getter& getter, ImPlotStairsFlags flags) {
|
||||||
if (BeginItemEx(label_id, Fitter1<Getter>(getter), flags, ImPlotCol_Line)) {
|
if (BeginItemEx(label_id, Fitter1<Getter>(getter), flags, ImPlotCol_Line)) {
|
||||||
|
if (getter.Count <= 0) {
|
||||||
|
EndItem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const ImPlotNextItemData& s = GetItemData();
|
const ImPlotNextItemData& s = GetItemData();
|
||||||
if (getter.Count > 1 ) {
|
if (getter.Count > 1) {
|
||||||
if (s.RenderFill && ImHasFlag(flags,ImPlotStairsFlags_Shaded)) {
|
if (s.RenderFill && ImHasFlag(flags,ImPlotStairsFlags_Shaded)) {
|
||||||
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);
|
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);
|
||||||
if (ImHasFlag(flags, ImPlotStairsFlags_PreStep))
|
if (ImHasFlag(flags, ImPlotStairsFlags_PreStep))
|
||||||
|
@ -1746,6 +1758,10 @@ void PlotStairsG(const char* label_id, ImPlotGetter getter_func, void* data, int
|
||||||
template <typename Getter1, typename Getter2>
|
template <typename Getter1, typename Getter2>
|
||||||
void PlotShadedEx(const char* label_id, const Getter1& getter1, const Getter2& getter2, ImPlotShadedFlags flags) {
|
void PlotShadedEx(const char* label_id, const Getter1& getter1, const Getter2& getter2, ImPlotShadedFlags flags) {
|
||||||
if (BeginItemEx(label_id, Fitter2<Getter1,Getter2>(getter1,getter2), flags, ImPlotCol_Fill)) {
|
if (BeginItemEx(label_id, Fitter2<Getter1,Getter2>(getter1,getter2), flags, ImPlotCol_Fill)) {
|
||||||
|
if (getter1.Count <= 0 || getter2.Count <= 0) {
|
||||||
|
EndItem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const ImPlotNextItemData& s = GetItemData();
|
const ImPlotNextItemData& s = GetItemData();
|
||||||
if (s.RenderFill) {
|
if (s.RenderFill) {
|
||||||
const ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);
|
const ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);
|
||||||
|
@ -1806,6 +1822,10 @@ void PlotShadedG(const char* label_id, ImPlotGetter getter_func1, void* data1, I
|
||||||
template <typename Getter1, typename Getter2>
|
template <typename Getter1, typename Getter2>
|
||||||
void PlotBarsVEx(const char* label_id, const Getter1& getter1, const Getter2 getter2, double width, ImPlotBarsFlags flags) {
|
void PlotBarsVEx(const char* label_id, const Getter1& getter1, const Getter2 getter2, double width, ImPlotBarsFlags flags) {
|
||||||
if (BeginItemEx(label_id, FitterBarV<Getter1,Getter2>(getter1,getter2,width), flags, ImPlotCol_Fill)) {
|
if (BeginItemEx(label_id, FitterBarV<Getter1,Getter2>(getter1,getter2,width), flags, ImPlotCol_Fill)) {
|
||||||
|
if (getter1.Count <= 0 || getter2.Count <= 0) {
|
||||||
|
EndItem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const ImPlotNextItemData& s = GetItemData();
|
const ImPlotNextItemData& s = GetItemData();
|
||||||
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);
|
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);
|
||||||
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
|
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
|
||||||
|
@ -1826,6 +1846,10 @@ void PlotBarsVEx(const char* label_id, const Getter1& getter1, const Getter2 get
|
||||||
template <typename Getter1, typename Getter2>
|
template <typename Getter1, typename Getter2>
|
||||||
void PlotBarsHEx(const char* label_id, const Getter1& getter1, const Getter2& getter2, double height, ImPlotBarsFlags flags) {
|
void PlotBarsHEx(const char* label_id, const Getter1& getter1, const Getter2& getter2, double height, ImPlotBarsFlags flags) {
|
||||||
if (BeginItemEx(label_id, FitterBarH<Getter1,Getter2>(getter1,getter2,height), flags, ImPlotCol_Fill)) {
|
if (BeginItemEx(label_id, FitterBarH<Getter1,Getter2>(getter1,getter2,height), flags, ImPlotCol_Fill)) {
|
||||||
|
if (getter1.Count <= 0 || getter2.Count <= 0) {
|
||||||
|
EndItem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const ImPlotNextItemData& s = GetItemData();
|
const ImPlotNextItemData& s = GetItemData();
|
||||||
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);
|
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);
|
||||||
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
|
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
|
||||||
|
@ -1982,6 +2006,10 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
|
||||||
template <typename _GetterPos, typename _GetterNeg>
|
template <typename _GetterPos, typename _GetterNeg>
|
||||||
void PlotErrorBarsVEx(const char* label_id, const _GetterPos& getter_pos, const _GetterNeg& getter_neg, ImPlotErrorBarsFlags flags) {
|
void PlotErrorBarsVEx(const char* label_id, const _GetterPos& getter_pos, const _GetterNeg& getter_neg, ImPlotErrorBarsFlags flags) {
|
||||||
if (BeginItemEx(label_id, Fitter2<_GetterPos,_GetterNeg>(getter_pos, getter_neg), flags, IMPLOT_AUTO)) {
|
if (BeginItemEx(label_id, Fitter2<_GetterPos,_GetterNeg>(getter_pos, getter_neg), flags, IMPLOT_AUTO)) {
|
||||||
|
if (getter_pos.Count <= 0 || getter_neg.Count <= 0) {
|
||||||
|
EndItem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const ImPlotNextItemData& s = GetItemData();
|
const ImPlotNextItemData& s = GetItemData();
|
||||||
ImDrawList& draw_list = *GetPlotDrawList();
|
ImDrawList& draw_list = *GetPlotDrawList();
|
||||||
const ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_ErrorBar]);
|
const ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_ErrorBar]);
|
||||||
|
@ -2003,6 +2031,10 @@ void PlotErrorBarsVEx(const char* label_id, const _GetterPos& getter_pos, const
|
||||||
template <typename _GetterPos, typename _GetterNeg>
|
template <typename _GetterPos, typename _GetterNeg>
|
||||||
void PlotErrorBarsHEx(const char* label_id, const _GetterPos& getter_pos, const _GetterNeg& getter_neg, ImPlotErrorBarsFlags flags) {
|
void PlotErrorBarsHEx(const char* label_id, const _GetterPos& getter_pos, const _GetterNeg& getter_neg, ImPlotErrorBarsFlags flags) {
|
||||||
if (BeginItemEx(label_id, Fitter2<_GetterPos,_GetterNeg>(getter_pos, getter_neg), flags, IMPLOT_AUTO)) {
|
if (BeginItemEx(label_id, Fitter2<_GetterPos,_GetterNeg>(getter_pos, getter_neg), flags, IMPLOT_AUTO)) {
|
||||||
|
if (getter_pos.Count <= 0 || getter_neg.Count <= 0) {
|
||||||
|
EndItem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const ImPlotNextItemData& s = GetItemData();
|
const ImPlotNextItemData& s = GetItemData();
|
||||||
ImDrawList& draw_list = *GetPlotDrawList();
|
ImDrawList& draw_list = *GetPlotDrawList();
|
||||||
const ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_ErrorBar]);
|
const ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_ErrorBar]);
|
||||||
|
@ -2060,13 +2092,17 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
template <typename _GetterM, typename _GetterB>
|
template <typename _GetterM, typename _GetterB>
|
||||||
void PlotStemsEx(const char* label_id, const _GetterM& get_mark, const _GetterB& get_base, ImPlotStemsFlags flags) {
|
void PlotStemsEx(const char* label_id, const _GetterM& getter_mark, const _GetterB& getter_base, ImPlotStemsFlags flags) {
|
||||||
if (BeginItemEx(label_id, Fitter2<_GetterM,_GetterB>(get_mark,get_base), flags, ImPlotCol_Line)) {
|
if (BeginItemEx(label_id, Fitter2<_GetterM,_GetterB>(getter_mark,getter_base), flags, ImPlotCol_Line)) {
|
||||||
|
if (getter_mark.Count <= 0 || getter_base.Count <= 0) {
|
||||||
|
EndItem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const ImPlotNextItemData& s = GetItemData();
|
const ImPlotNextItemData& s = GetItemData();
|
||||||
// render stems
|
// render stems
|
||||||
if (s.RenderLine) {
|
if (s.RenderLine) {
|
||||||
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
|
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
|
||||||
RenderPrimitives2<RendererLineSegments2>(get_mark, get_base, col_line, s.LineWeight);
|
RenderPrimitives2<RendererLineSegments2>(getter_mark, getter_base, col_line, s.LineWeight);
|
||||||
}
|
}
|
||||||
// render markers
|
// render markers
|
||||||
if (s.Marker != ImPlotMarker_None) {
|
if (s.Marker != ImPlotMarker_None) {
|
||||||
|
@ -2074,7 +2110,7 @@ void PlotStemsEx(const char* label_id, const _GetterM& get_mark, const _GetterB&
|
||||||
PushPlotClipRect(s.MarkerSize);
|
PushPlotClipRect(s.MarkerSize);
|
||||||
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]);
|
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]);
|
||||||
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerFill]);
|
const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerFill]);
|
||||||
RenderMarkers<_GetterM>(get_mark, s.Marker, s.MarkerSize, s.RenderMarkerFill, col_fill, s.RenderMarkerLine, col_line, s.MarkerWeight);
|
RenderMarkers<_GetterM>(getter_mark, s.Marker, s.MarkerSize, s.RenderMarkerFill, col_fill, s.RenderMarkerLine, col_line, s.MarkerWeight);
|
||||||
}
|
}
|
||||||
EndItem();
|
EndItem();
|
||||||
}
|
}
|
||||||
|
@ -2123,13 +2159,17 @@ template <typename T>
|
||||||
void PlotInfLines(const char* label_id, const T* values, int count, ImPlotInfLinesFlags flags, int offset, int stride) {
|
void PlotInfLines(const char* label_id, const T* values, int count, ImPlotInfLinesFlags flags, int offset, int stride) {
|
||||||
const ImPlotRect lims = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO);
|
const ImPlotRect lims = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO);
|
||||||
if (ImHasFlag(flags, ImPlotInfLinesFlags_Horizontal)) {
|
if (ImHasFlag(flags, ImPlotInfLinesFlags_Horizontal)) {
|
||||||
GetterXY<IndexerConst,IndexerIdx<T>> get_min(IndexerConst(lims.X.Min),IndexerIdx<T>(values,count,offset,stride),count);
|
GetterXY<IndexerConst,IndexerIdx<T>> getter_min(IndexerConst(lims.X.Min),IndexerIdx<T>(values,count,offset,stride),count);
|
||||||
GetterXY<IndexerConst,IndexerIdx<T>> get_max(IndexerConst(lims.X.Max),IndexerIdx<T>(values,count,offset,stride),count);
|
GetterXY<IndexerConst,IndexerIdx<T>> getter_max(IndexerConst(lims.X.Max),IndexerIdx<T>(values,count,offset,stride),count);
|
||||||
if (BeginItemEx(label_id, FitterY<GetterXY<IndexerConst,IndexerIdx<T>>>(get_min), flags, ImPlotCol_Line)) {
|
if (BeginItemEx(label_id, FitterY<GetterXY<IndexerConst,IndexerIdx<T>>>(getter_min), flags, ImPlotCol_Line)) {
|
||||||
|
if (count <= 0) {
|
||||||
|
EndItem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const ImPlotNextItemData& s = GetItemData();
|
const ImPlotNextItemData& s = GetItemData();
|
||||||
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
|
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
|
||||||
if (s.RenderLine)
|
if (s.RenderLine)
|
||||||
RenderPrimitives2<RendererLineSegments2>(get_min, get_max, col_line, s.LineWeight);
|
RenderPrimitives2<RendererLineSegments2>(getter_min, getter_max, col_line, s.LineWeight);
|
||||||
EndItem();
|
EndItem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2137,6 +2177,10 @@ void PlotInfLines(const char* label_id, const T* values, int count, ImPlotInfLin
|
||||||
GetterXY<IndexerIdx<T>,IndexerConst> get_min(IndexerIdx<T>(values,count,offset,stride),IndexerConst(lims.Y.Min),count);
|
GetterXY<IndexerIdx<T>,IndexerConst> get_min(IndexerIdx<T>(values,count,offset,stride),IndexerConst(lims.Y.Min),count);
|
||||||
GetterXY<IndexerIdx<T>,IndexerConst> get_max(IndexerIdx<T>(values,count,offset,stride),IndexerConst(lims.Y.Max),count);
|
GetterXY<IndexerIdx<T>,IndexerConst> get_max(IndexerIdx<T>(values,count,offset,stride),IndexerConst(lims.Y.Max),count);
|
||||||
if (BeginItemEx(label_id, FitterX<GetterXY<IndexerIdx<T>,IndexerConst>>(get_min), flags, ImPlotCol_Line)) {
|
if (BeginItemEx(label_id, FitterX<GetterXY<IndexerIdx<T>,IndexerConst>>(get_min), flags, ImPlotCol_Line)) {
|
||||||
|
if (count <= 0) {
|
||||||
|
EndItem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const ImPlotNextItemData& s = GetItemData();
|
const ImPlotNextItemData& s = GetItemData();
|
||||||
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
|
const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);
|
||||||
if (s.RenderLine)
|
if (s.RenderLine)
|
||||||
|
@ -2172,57 +2216,121 @@ IMPLOT_INLINE void RenderPieSlice(ImDrawList& draw_list, const ImPlotPoint& cent
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* fmt, double angle0, ImPlotPieChartFlags flags) {
|
double PieChartSum(const T* values, int count, bool ignore_hidden) {
|
||||||
IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "PlotPieChart() needs to be called between BeginPlot() and EndPlot()!");
|
|
||||||
ImDrawList & draw_list = *GetPlotDrawList();
|
|
||||||
double sum = 0;
|
double sum = 0;
|
||||||
for (int i = 0; i < count; ++i)
|
if (ignore_hidden) {
|
||||||
sum += (double)values[i];
|
ImPlotContext& gp = *GImPlot;
|
||||||
const bool normalize = ImHasFlag(flags,ImPlotPieChartFlags_Normalize) || sum > 1.0;
|
ImPlotItemGroup& Items = *gp.CurrentItems;
|
||||||
ImPlotPoint center(x,y);
|
for (int i = 0; i < count; ++i) {
|
||||||
PushPlotClipRect();
|
if (i >= Items.GetItemCount())
|
||||||
|
break;
|
||||||
|
|
||||||
|
ImPlotItem* item = Items.GetItemByIndex(i);
|
||||||
|
IM_ASSERT(item != nullptr);
|
||||||
|
if (item->Show) {
|
||||||
|
sum += (double)values[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
sum += (double)values[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void PlotPieChartEx(const char* const label_ids[], const T* values, int count, ImPlotPoint center, double radius, double angle0, ImPlotPieChartFlags flags) {
|
||||||
|
ImDrawList& draw_list = *GetPlotDrawList();
|
||||||
|
|
||||||
|
const bool ignore_hidden = ImHasFlag(flags, ImPlotPieChartFlags_IgnoreHidden);
|
||||||
|
const double sum = PieChartSum(values, count, ignore_hidden);
|
||||||
|
const bool normalize = ImHasFlag(flags, ImPlotPieChartFlags_Normalize) || sum > 1.0;
|
||||||
|
|
||||||
double a0 = angle0 * 2 * IM_PI / 360.0;
|
double a0 = angle0 * 2 * IM_PI / 360.0;
|
||||||
double a1 = angle0 * 2 * IM_PI / 360.0;
|
double a1 = angle0 * 2 * IM_PI / 360.0;
|
||||||
ImPlotPoint Pmin = ImPlotPoint(x-radius,y-radius);
|
ImPlotPoint Pmin = ImPlotPoint(center.x - radius, center.y - radius);
|
||||||
ImPlotPoint Pmax = ImPlotPoint(x+radius,y+radius);
|
ImPlotPoint Pmax = ImPlotPoint(center.x + radius, center.y + radius);
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
double percent = normalize ? (double)values[i] / sum : (double)values[i];
|
ImPlotItem* item = GetItem(label_ids[i]);
|
||||||
a1 = a0 + 2 * IM_PI * percent;
|
|
||||||
if (BeginItemEx(label_ids[i], FitterRect(Pmin,Pmax))) {
|
const double percent = normalize ? (double)values[i] / sum : (double)values[i];
|
||||||
ImU32 col = GetCurrentItem()->Color;
|
const bool skip = sum <= 0.0 || (ignore_hidden && item != nullptr && !item->Show);
|
||||||
if (percent < 0.5) {
|
if (!skip)
|
||||||
RenderPieSlice(draw_list, center, radius, a0, a1, col);
|
a1 = a0 + 2 * IM_PI * percent;
|
||||||
}
|
|
||||||
else {
|
if (BeginItemEx(label_ids[i], FitterRect(Pmin, Pmax))) {
|
||||||
RenderPieSlice(draw_list, center, radius, a0, a0 + (a1 - a0) * 0.5, col);
|
if (sum > 0.0) {
|
||||||
RenderPieSlice(draw_list, center, radius, a0 + (a1 - a0) * 0.5, a1, col);
|
ImU32 col = GetCurrentItem()->Color;
|
||||||
|
if (percent < 0.5) {
|
||||||
|
RenderPieSlice(draw_list, center, radius, a0, a1, col);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RenderPieSlice(draw_list, center, radius, a0, a0 + (a1 - a0) * 0.5, col);
|
||||||
|
RenderPieSlice(draw_list, center, radius, a0 + (a1 - a0) * 0.5, a1, col);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
EndItem();
|
EndItem();
|
||||||
}
|
}
|
||||||
a0 = a1;
|
if (!skip)
|
||||||
|
a0 = a1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int PieChartFormatter(double value, char* buff, int size, void* data) {
|
||||||
|
const char* fmt = (const char*)data;
|
||||||
|
return snprintf(buff, size, fmt, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* fmt, double angle0, ImPlotPieChartFlags flags) {
|
||||||
|
PlotPieChart<T>(label_ids, values, count, x, y, radius, PieChartFormatter, (void*)fmt, angle0, flags);
|
||||||
|
}
|
||||||
|
#define INSTANTIATE_MACRO(T) template IMPLOT_API void PlotPieChart<T>(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* fmt, double angle0, ImPlotPieChartFlags flags);
|
||||||
|
CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
|
||||||
|
#undef INSTANTIATE_MACRO
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, ImPlotFormatter fmt, void* fmt_data, double angle0, ImPlotPieChartFlags flags) {
|
||||||
|
IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "PlotPieChart() needs to be called between BeginPlot() and EndPlot()!");
|
||||||
|
ImDrawList& draw_list = *GetPlotDrawList();
|
||||||
|
|
||||||
|
const bool ignore_hidden = ImHasFlag(flags, ImPlotPieChartFlags_IgnoreHidden);
|
||||||
|
const double sum = PieChartSum(values, count, ignore_hidden);
|
||||||
|
const bool normalize = ImHasFlag(flags, ImPlotPieChartFlags_Normalize) || sum > 1.0;
|
||||||
|
ImPlotPoint center(x, y);
|
||||||
|
|
||||||
|
PushPlotClipRect();
|
||||||
|
PlotPieChartEx(label_ids, values, count, center, radius, angle0, flags);
|
||||||
if (fmt != nullptr) {
|
if (fmt != nullptr) {
|
||||||
a0 = angle0 * 2 * IM_PI / 360.0;
|
double a0 = angle0 * 2 * IM_PI / 360.0;
|
||||||
a1 = angle0 * 2 * IM_PI / 360.0;
|
double a1 = angle0 * 2 * IM_PI / 360.0;
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
ImPlotItem* item = GetItem(label_ids[i]);
|
ImPlotItem* item = GetItem(label_ids[i]);
|
||||||
double percent = normalize ? (double)values[i] / sum : (double)values[i];
|
IM_ASSERT(item != nullptr);
|
||||||
a1 = a0 + 2 * IM_PI * percent;
|
|
||||||
if (item->Show) {
|
const double percent = normalize ? (double)values[i] / sum : (double)values[i];
|
||||||
ImFormatString(buffer, 32, fmt, (double)values[i]);
|
const bool skip = ignore_hidden && item != nullptr && !item->Show;
|
||||||
ImVec2 size = ImGui::CalcTextSize(buffer);
|
|
||||||
double angle = a0 + (a1 - a0) * 0.5;
|
if (!skip) {
|
||||||
ImVec2 pos = PlotToPixels(center.x + 0.5 * radius * cos(angle), center.y + 0.5 * radius * sin(angle),IMPLOT_AUTO,IMPLOT_AUTO);
|
a1 = a0 + 2 * IM_PI * percent;
|
||||||
ImU32 col = CalcTextColor(ImGui::ColorConvertU32ToFloat4(item->Color));
|
if (item->Show) {
|
||||||
draw_list.AddText(pos - size * 0.5f, col, buffer);
|
fmt((double)values[i], buffer, 32, fmt_data);
|
||||||
|
ImVec2 size = ImGui::CalcTextSize(buffer);
|
||||||
|
double angle = a0 + (a1 - a0) * 0.5;
|
||||||
|
ImVec2 pos = PlotToPixels(center.x + 0.5 * radius * cos(angle), center.y + 0.5 * radius * sin(angle), IMPLOT_AUTO, IMPLOT_AUTO);
|
||||||
|
ImU32 col = CalcTextColor(ImGui::ColorConvertU32ToFloat4(item->Color));
|
||||||
|
draw_list.AddText(pos - size * 0.5f, col, buffer);
|
||||||
|
}
|
||||||
|
a0 = a1;
|
||||||
}
|
}
|
||||||
a0 = a1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PopPlotClipRect();
|
PopPlotClipRect();
|
||||||
}
|
}
|
||||||
#define INSTANTIATE_MACRO(T) template IMPLOT_API void PlotPieChart<T>(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* fmt, double angle0, ImPlotPieChartFlags flags);
|
#define INSTANTIATE_MACRO(T) template IMPLOT_API void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, ImPlotFormatter fmt, void* fmt_data, double angle0, ImPlotPieChartFlags flags);
|
||||||
CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
|
CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
|
||||||
#undef INSTANTIATE_MACRO
|
#undef INSTANTIATE_MACRO
|
||||||
|
|
||||||
|
@ -2283,8 +2391,8 @@ struct GetterHeatmapColMaj {
|
||||||
{ }
|
{ }
|
||||||
template <typename I> IMPLOT_INLINE RectC operator()(I idx) const {
|
template <typename I> IMPLOT_INLINE RectC operator()(I idx) const {
|
||||||
double val = (double)Values[idx];
|
double val = (double)Values[idx];
|
||||||
const int r = idx % Cols;
|
const int r = idx % Rows;
|
||||||
const int c = idx / Cols;
|
const int c = idx / Rows;
|
||||||
const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height));
|
const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height));
|
||||||
RectC rect;
|
RectC rect;
|
||||||
rect.Pos = p;
|
rect.Pos = p;
|
||||||
|
@ -2375,6 +2483,10 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, ImPlotHeatmapFlags flags) {
|
void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, ImPlotHeatmapFlags flags) {
|
||||||
if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max))) {
|
if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max))) {
|
||||||
|
if (rows <= 0 || cols <= 0) {
|
||||||
|
EndItem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
ImDrawList& draw_list = *GetPlotDrawList();
|
ImDrawList& draw_list = *GetPlotDrawList();
|
||||||
const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor);
|
const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor);
|
||||||
RenderHeatmap(draw_list, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true, col_maj);
|
RenderHeatmap(draw_list, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true, col_maj);
|
||||||
|
@ -2539,6 +2651,10 @@ double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BeginItemEx(label_id, FitterRect(range))) {
|
if (BeginItemEx(label_id, FitterRect(range))) {
|
||||||
|
if (y_bins <= 0 || x_bins <= 0) {
|
||||||
|
EndItem();
|
||||||
|
return max_count;
|
||||||
|
}
|
||||||
ImDrawList& draw_list = *GetPlotDrawList();
|
ImDrawList& draw_list = *GetPlotDrawList();
|
||||||
RenderHeatmap(draw_list, &bin_counts.Data[0], y_bins, x_bins, 0, max_count, nullptr, range.Min(), range.Max(), false, col_maj);
|
RenderHeatmap(draw_list, &bin_counts.Data[0], y_bins, x_bins, 0, max_count, nullptr, range.Min(), range.Max(), false, col_maj);
|
||||||
EndItem();
|
EndItem();
|
||||||
|
@ -2597,8 +2713,8 @@ void PlotDigitalEx(const char* label_id, Getter getter, ImPlotDigitalFlags flags
|
||||||
//do not extend plot outside plot range
|
//do not extend plot outside plot range
|
||||||
if (pMin.x < x_axis.PixelMin) pMin.x = x_axis.PixelMin;
|
if (pMin.x < x_axis.PixelMin) pMin.x = x_axis.PixelMin;
|
||||||
if (pMax.x < x_axis.PixelMin) pMax.x = x_axis.PixelMin;
|
if (pMax.x < x_axis.PixelMin) pMax.x = x_axis.PixelMin;
|
||||||
if (pMin.x > x_axis.PixelMax) pMin.x = x_axis.PixelMax;
|
if (pMin.x > x_axis.PixelMax) pMin.x = x_axis.PixelMax - 1; //fix issue related to https://github.com/ocornut/imgui/issues/3976
|
||||||
if (pMax.x > x_axis.PixelMax) pMax.x = x_axis.PixelMax;
|
if (pMax.x > x_axis.PixelMax) pMax.x = x_axis.PixelMax - 1; //fix issue related to https://github.com/ocornut/imgui/issues/3976
|
||||||
//plot a rectangle that extends up to x2 with y1 height
|
//plot a rectangle that extends up to x2 with y1 height
|
||||||
if ((pMax.x > pMin.x) && (gp.CurrentPlot->PlotRect.Contains(pMin) || gp.CurrentPlot->PlotRect.Contains(pMax))) {
|
if ((pMax.x > pMin.x) && (gp.CurrentPlot->PlotRect.Contains(pMin) || gp.CurrentPlot->PlotRect.Contains(pMax))) {
|
||||||
// ImVec4 colAlpha = item->Color;
|
// ImVec4 colAlpha = item->Color;
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
// This is a slightly modified version of stb_textedit.h 1.14.
|
// This is a slightly modified version of stb_textedit.h 1.14.
|
||||||
// Those changes would need to be pushed into nothings/stb:
|
// Those changes would need to be pushed into nothings/stb:
|
||||||
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
|
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
|
||||||
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000)
|
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
|
||||||
// Grep for [DEAR IMGUI] to find the changes.
|
// Grep for [DEAR IMGUI] to find the changes.
|
||||||
|
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
|
||||||
|
|
||||||
// stb_textedit.h - v1.14 - public domain - Sean Barrett
|
// stb_textedit.h - v1.14 - public domain - Sean Barrett
|
||||||
// Development of this library was sponsored by RAD Game Tools
|
// Development of this library was sponsored by RAD Game Tools
|
||||||
|
@ -30,7 +31,7 @@
|
||||||
// DEPENDENCIES
|
// DEPENDENCIES
|
||||||
//
|
//
|
||||||
// Uses the C runtime function 'memmove', which you can override
|
// Uses the C runtime function 'memmove', which you can override
|
||||||
// by defining STB_TEXTEDIT_memmove before the implementation.
|
// by defining IMSTB_TEXTEDIT_memmove before the implementation.
|
||||||
// Uses no other functions. Performs no runtime allocations.
|
// Uses no other functions. Performs no runtime allocations.
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
@ -274,8 +275,8 @@
|
||||||
////
|
////
|
||||||
////
|
////
|
||||||
|
|
||||||
#ifndef INCLUDE_STB_TEXTEDIT_H
|
#ifndef INCLUDE_IMSTB_TEXTEDIT_H
|
||||||
#define INCLUDE_STB_TEXTEDIT_H
|
#define INCLUDE_IMSTB_TEXTEDIT_H
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -286,33 +287,33 @@
|
||||||
// and undo state.
|
// and undo state.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef STB_TEXTEDIT_UNDOSTATECOUNT
|
#ifndef IMSTB_TEXTEDIT_UNDOSTATECOUNT
|
||||||
#define STB_TEXTEDIT_UNDOSTATECOUNT 99
|
#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99
|
||||||
#endif
|
#endif
|
||||||
#ifndef STB_TEXTEDIT_UNDOCHARCOUNT
|
#ifndef IMSTB_TEXTEDIT_UNDOCHARCOUNT
|
||||||
#define STB_TEXTEDIT_UNDOCHARCOUNT 999
|
#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999
|
||||||
#endif
|
#endif
|
||||||
#ifndef STB_TEXTEDIT_CHARTYPE
|
#ifndef IMSTB_TEXTEDIT_CHARTYPE
|
||||||
#define STB_TEXTEDIT_CHARTYPE int
|
#define IMSTB_TEXTEDIT_CHARTYPE int
|
||||||
#endif
|
#endif
|
||||||
#ifndef STB_TEXTEDIT_POSITIONTYPE
|
#ifndef IMSTB_TEXTEDIT_POSITIONTYPE
|
||||||
#define STB_TEXTEDIT_POSITIONTYPE int
|
#define IMSTB_TEXTEDIT_POSITIONTYPE int
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
// private data
|
// private data
|
||||||
STB_TEXTEDIT_POSITIONTYPE where;
|
IMSTB_TEXTEDIT_POSITIONTYPE where;
|
||||||
STB_TEXTEDIT_POSITIONTYPE insert_length;
|
IMSTB_TEXTEDIT_POSITIONTYPE insert_length;
|
||||||
STB_TEXTEDIT_POSITIONTYPE delete_length;
|
IMSTB_TEXTEDIT_POSITIONTYPE delete_length;
|
||||||
int char_storage;
|
int char_storage;
|
||||||
} StbUndoRecord;
|
} StbUndoRecord;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
// private data
|
// private data
|
||||||
StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT];
|
StbUndoRecord undo_rec [IMSTB_TEXTEDIT_UNDOSTATECOUNT];
|
||||||
STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT];
|
IMSTB_TEXTEDIT_CHARTYPE undo_char[IMSTB_TEXTEDIT_UNDOCHARCOUNT];
|
||||||
short undo_point, redo_point;
|
short undo_point, redo_point;
|
||||||
int undo_char_point, redo_char_point;
|
int undo_char_point, redo_char_point;
|
||||||
} StbUndoState;
|
} StbUndoState;
|
||||||
|
@ -371,7 +372,7 @@ typedef struct
|
||||||
float ymin,ymax; // height of row above and below baseline
|
float ymin,ymax; // height of row above and below baseline
|
||||||
int num_chars;
|
int num_chars;
|
||||||
} StbTexteditRow;
|
} StbTexteditRow;
|
||||||
#endif //INCLUDE_STB_TEXTEDIT_H
|
#endif //INCLUDE_IMSTB_TEXTEDIT_H
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -384,11 +385,11 @@ typedef struct
|
||||||
|
|
||||||
// implementation isn't include-guarded, since it might have indirectly
|
// implementation isn't include-guarded, since it might have indirectly
|
||||||
// included just the "header" portion
|
// included just the "header" portion
|
||||||
#ifdef STB_TEXTEDIT_IMPLEMENTATION
|
#ifdef IMSTB_TEXTEDIT_IMPLEMENTATION
|
||||||
|
|
||||||
#ifndef STB_TEXTEDIT_memmove
|
#ifndef IMSTB_TEXTEDIT_memmove
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#define STB_TEXTEDIT_memmove memmove
|
#define IMSTB_TEXTEDIT_memmove memmove
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -398,7 +399,7 @@ typedef struct
|
||||||
//
|
//
|
||||||
|
|
||||||
// traverse the layout to locate the nearest character to a display position
|
// traverse the layout to locate the nearest character to a display position
|
||||||
static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
|
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||||
{
|
{
|
||||||
StbTexteditRow r;
|
StbTexteditRow r;
|
||||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||||
|
@ -458,7 +459,7 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
|
||||||
}
|
}
|
||||||
|
|
||||||
// API click: on mouse down, move the cursor to the clicked location, and reset the selection
|
// API click: on mouse down, move the cursor to the clicked location, and reset the selection
|
||||||
static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||||
{
|
{
|
||||||
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
|
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
|
||||||
// goes off the top or bottom of the text
|
// goes off the top or bottom of the text
|
||||||
|
@ -476,7 +477,7 @@ static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
|
||||||
}
|
}
|
||||||
|
|
||||||
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
|
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
|
||||||
static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||||
{
|
{
|
||||||
int p = 0;
|
int p = 0;
|
||||||
|
|
||||||
|
@ -502,11 +503,11 @@ static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state
|
||||||
//
|
//
|
||||||
|
|
||||||
// forward declarations
|
// forward declarations
|
||||||
static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
||||||
static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
||||||
static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
|
static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
|
||||||
static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length);
|
static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length);
|
||||||
static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
|
static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
@ -518,7 +519,7 @@ typedef struct
|
||||||
|
|
||||||
// find the x/y location of a character, and remember info about the previous row in
|
// find the x/y location of a character, and remember info about the previous row in
|
||||||
// case we get a move-up event (for page up, we'll have to rescan)
|
// case we get a move-up event (for page up, we'll have to rescan)
|
||||||
static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line)
|
static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING *str, int n, int single_line)
|
||||||
{
|
{
|
||||||
StbTexteditRow r;
|
StbTexteditRow r;
|
||||||
int prev_start = 0;
|
int prev_start = 0;
|
||||||
|
@ -549,7 +550,10 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
|
||||||
i += r.num_chars;
|
i += r.num_chars;
|
||||||
find->y += r.baseline_y_delta;
|
find->y += r.baseline_y_delta;
|
||||||
if (i == z) // [DEAR IMGUI]
|
if (i == z) // [DEAR IMGUI]
|
||||||
|
{
|
||||||
|
r.num_chars = 0; // [DEAR IMGUI]
|
||||||
break; // [DEAR IMGUI]
|
break; // [DEAR IMGUI]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
find->first_char = first = i;
|
find->first_char = first = i;
|
||||||
|
@ -566,7 +570,7 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
|
||||||
#define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end)
|
#define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end)
|
||||||
|
|
||||||
// make the selection/cursor state valid if client altered the string
|
// make the selection/cursor state valid if client altered the string
|
||||||
static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
static void stb_textedit_clamp(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||||
{
|
{
|
||||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||||
if (STB_TEXT_HAS_SELECTION(state)) {
|
if (STB_TEXT_HAS_SELECTION(state)) {
|
||||||
|
@ -580,7 +584,7 @@ static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete characters while updating undo
|
// delete characters while updating undo
|
||||||
static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
|
static void stb_textedit_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
|
||||||
{
|
{
|
||||||
stb_text_makeundo_delete(str, state, where, len);
|
stb_text_makeundo_delete(str, state, where, len);
|
||||||
STB_TEXTEDIT_DELETECHARS(str, where, len);
|
STB_TEXTEDIT_DELETECHARS(str, where, len);
|
||||||
|
@ -588,7 +592,7 @@ static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *sta
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete the section
|
// delete the section
|
||||||
static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
static void stb_textedit_delete_selection(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||||
{
|
{
|
||||||
stb_textedit_clamp(str, state);
|
stb_textedit_clamp(str, state);
|
||||||
if (STB_TEXT_HAS_SELECTION(state)) {
|
if (STB_TEXT_HAS_SELECTION(state)) {
|
||||||
|
@ -625,7 +629,7 @@ static void stb_textedit_move_to_first(STB_TexteditState *state)
|
||||||
}
|
}
|
||||||
|
|
||||||
// move cursor to last character of selection
|
// move cursor to last character of selection
|
||||||
static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||||
{
|
{
|
||||||
if (STB_TEXT_HAS_SELECTION(state)) {
|
if (STB_TEXT_HAS_SELECTION(state)) {
|
||||||
stb_textedit_sortselection(state);
|
stb_textedit_sortselection(state);
|
||||||
|
@ -637,13 +641,13 @@ static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditStat
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef STB_TEXTEDIT_IS_SPACE
|
#ifdef STB_TEXTEDIT_IS_SPACE
|
||||||
static int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx )
|
static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
|
||||||
{
|
{
|
||||||
return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1;
|
return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
|
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
|
||||||
static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
|
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||||
{
|
{
|
||||||
--c; // always move at least one character
|
--c; // always move at least one character
|
||||||
while( c >= 0 && !is_word_boundary( str, c ) )
|
while( c >= 0 && !is_word_boundary( str, c ) )
|
||||||
|
@ -658,7 +662,7 @@ static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef STB_TEXTEDIT_MOVEWORDRIGHT
|
#ifndef STB_TEXTEDIT_MOVEWORDRIGHT
|
||||||
static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c )
|
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||||
{
|
{
|
||||||
const int len = STB_TEXTEDIT_STRINGLEN(str);
|
const int len = STB_TEXTEDIT_STRINGLEN(str);
|
||||||
++c; // always move at least one character
|
++c; // always move at least one character
|
||||||
|
@ -685,7 +689,7 @@ static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state)
|
||||||
}
|
}
|
||||||
|
|
||||||
// API cut: delete selection
|
// API cut: delete selection
|
||||||
static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
static int stb_textedit_cut(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||||
{
|
{
|
||||||
if (STB_TEXT_HAS_SELECTION(state)) {
|
if (STB_TEXT_HAS_SELECTION(state)) {
|
||||||
stb_textedit_delete_selection(str,state); // implicitly clamps
|
stb_textedit_delete_selection(str,state); // implicitly clamps
|
||||||
|
@ -696,7 +700,7 @@ static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||||
}
|
}
|
||||||
|
|
||||||
// API paste: replace existing selection with passed-in text
|
// API paste: replace existing selection with passed-in text
|
||||||
static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
|
static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE *text, int len)
|
||||||
{
|
{
|
||||||
// if there's a selection, the paste should delete it
|
// if there's a selection, the paste should delete it
|
||||||
stb_textedit_clamp(str, state);
|
stb_textedit_clamp(str, state);
|
||||||
|
@ -717,14 +721,14 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// API key: process a keyboard input
|
// API key: process a keyboard input
|
||||||
static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
|
static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
|
||||||
{
|
{
|
||||||
retry:
|
retry:
|
||||||
switch (key) {
|
switch (key) {
|
||||||
default: {
|
default: {
|
||||||
int c = STB_TEXTEDIT_KEYTOTEXT(key);
|
int c = STB_TEXTEDIT_KEYTOTEXT(key);
|
||||||
if (c > 0) {
|
if (c > 0) {
|
||||||
STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c;
|
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE) c;
|
||||||
|
|
||||||
// can't add newline in single-line mode
|
// can't add newline in single-line mode
|
||||||
if (c == '\n' && state->single_line)
|
if (c == '\n' && state->single_line)
|
||||||
|
@ -889,8 +893,8 @@ retry:
|
||||||
x = row.x0;
|
x = row.x0;
|
||||||
for (i=0; i < row.num_chars; ++i) {
|
for (i=0; i < row.num_chars; ++i) {
|
||||||
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
|
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
|
||||||
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
|
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||||
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
|
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
x += dx;
|
x += dx;
|
||||||
|
@ -951,8 +955,8 @@ retry:
|
||||||
x = row.x0;
|
x = row.x0;
|
||||||
for (i=0; i < row.num_chars; ++i) {
|
for (i=0; i < row.num_chars; ++i) {
|
||||||
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
|
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
|
||||||
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
|
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||||
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
|
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
x += dx;
|
x += dx;
|
||||||
|
@ -1109,8 +1113,8 @@ retry:
|
||||||
|
|
||||||
static void stb_textedit_flush_redo(StbUndoState *state)
|
static void stb_textedit_flush_redo(StbUndoState *state)
|
||||||
{
|
{
|
||||||
state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
|
state->redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
|
||||||
state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
|
state->redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// discard the oldest entry in the undo list
|
// discard the oldest entry in the undo list
|
||||||
|
@ -1122,13 +1126,13 @@ static void stb_textedit_discard_undo(StbUndoState *state)
|
||||||
int n = state->undo_rec[0].insert_length, i;
|
int n = state->undo_rec[0].insert_length, i;
|
||||||
// delete n characters from all other records
|
// delete n characters from all other records
|
||||||
state->undo_char_point -= n;
|
state->undo_char_point -= n;
|
||||||
STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE)));
|
IMSTB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
|
||||||
for (i=0; i < state->undo_point; ++i)
|
for (i=0; i < state->undo_point; ++i)
|
||||||
if (state->undo_rec[i].char_storage >= 0)
|
if (state->undo_rec[i].char_storage >= 0)
|
||||||
state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it
|
state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it
|
||||||
}
|
}
|
||||||
--state->undo_point;
|
--state->undo_point;
|
||||||
STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
|
IMSTB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1138,7 +1142,7 @@ static void stb_textedit_discard_undo(StbUndoState *state)
|
||||||
// fill up even though the undo buffer didn't
|
// fill up even though the undo buffer didn't
|
||||||
static void stb_textedit_discard_redo(StbUndoState *state)
|
static void stb_textedit_discard_redo(StbUndoState *state)
|
||||||
{
|
{
|
||||||
int k = STB_TEXTEDIT_UNDOSTATECOUNT-1;
|
int k = IMSTB_TEXTEDIT_UNDOSTATECOUNT-1;
|
||||||
|
|
||||||
if (state->redo_point <= k) {
|
if (state->redo_point <= k) {
|
||||||
// if the k'th undo state has characters, clean those up
|
// if the k'th undo state has characters, clean those up
|
||||||
|
@ -1146,7 +1150,7 @@ static void stb_textedit_discard_redo(StbUndoState *state)
|
||||||
int n = state->undo_rec[k].insert_length, i;
|
int n = state->undo_rec[k].insert_length, i;
|
||||||
// move the remaining redo character data to the end of the buffer
|
// move the remaining redo character data to the end of the buffer
|
||||||
state->redo_char_point += n;
|
state->redo_char_point += n;
|
||||||
STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE)));
|
IMSTB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((IMSTB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
|
||||||
// adjust the position of all the other records to account for above memmove
|
// adjust the position of all the other records to account for above memmove
|
||||||
for (i=state->redo_point; i < k; ++i)
|
for (i=state->redo_point; i < k; ++i)
|
||||||
if (state->undo_rec[i].char_storage >= 0)
|
if (state->undo_rec[i].char_storage >= 0)
|
||||||
|
@ -1154,12 +1158,12 @@ static void stb_textedit_discard_redo(StbUndoState *state)
|
||||||
}
|
}
|
||||||
// now move all the redo records towards the end of the buffer; the first one is at 'redo_point'
|
// now move all the redo records towards the end of the buffer; the first one is at 'redo_point'
|
||||||
// [DEAR IMGUI]
|
// [DEAR IMGUI]
|
||||||
size_t move_size = (size_t)((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
|
size_t move_size = (size_t)((IMSTB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
|
||||||
const char* buf_begin = (char*)state->undo_rec; (void)buf_begin;
|
const char* buf_begin = (char*)state->undo_rec; (void)buf_begin;
|
||||||
const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end;
|
const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end;
|
||||||
IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin);
|
IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin);
|
||||||
IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end);
|
IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end);
|
||||||
STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
|
IMSTB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
|
||||||
|
|
||||||
// now move redo_point to point to the new one
|
// now move redo_point to point to the new one
|
||||||
++state->redo_point;
|
++state->redo_point;
|
||||||
|
@ -1173,32 +1177,32 @@ static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numch
|
||||||
|
|
||||||
// if we have no free records, we have to make room, by sliding the
|
// if we have no free records, we have to make room, by sliding the
|
||||||
// existing records down
|
// existing records down
|
||||||
if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
|
if (state->undo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
|
||||||
stb_textedit_discard_undo(state);
|
stb_textedit_discard_undo(state);
|
||||||
|
|
||||||
// if the characters to store won't possibly fit in the buffer, we can't undo
|
// if the characters to store won't possibly fit in the buffer, we can't undo
|
||||||
if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) {
|
if (numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
|
||||||
state->undo_point = 0;
|
state->undo_point = 0;
|
||||||
state->undo_char_point = 0;
|
state->undo_char_point = 0;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we don't have enough free characters in the buffer, we have to make room
|
// if we don't have enough free characters in the buffer, we have to make room
|
||||||
while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT)
|
while (state->undo_char_point + numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT)
|
||||||
stb_textedit_discard_undo(state);
|
stb_textedit_discard_undo(state);
|
||||||
|
|
||||||
return &state->undo_rec[state->undo_point++];
|
return &state->undo_rec[state->undo_point++];
|
||||||
}
|
}
|
||||||
|
|
||||||
static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
|
static IMSTB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
|
||||||
{
|
{
|
||||||
StbUndoRecord *r = stb_text_create_undo_record(state, insert_len);
|
StbUndoRecord *r = stb_text_create_undo_record(state, insert_len);
|
||||||
if (r == NULL)
|
if (r == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
r->where = pos;
|
r->where = pos;
|
||||||
r->insert_length = (STB_TEXTEDIT_POSITIONTYPE) insert_len;
|
r->insert_length = (IMSTB_TEXTEDIT_POSITIONTYPE) insert_len;
|
||||||
r->delete_length = (STB_TEXTEDIT_POSITIONTYPE) delete_len;
|
r->delete_length = (IMSTB_TEXTEDIT_POSITIONTYPE) delete_len;
|
||||||
|
|
||||||
if (insert_len == 0) {
|
if (insert_len == 0) {
|
||||||
r->char_storage = -1;
|
r->char_storage = -1;
|
||||||
|
@ -1210,7 +1214,7 @@ static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||||
{
|
{
|
||||||
StbUndoState *s = &state->undostate;
|
StbUndoState *s = &state->undostate;
|
||||||
StbUndoRecord u, *r;
|
StbUndoRecord u, *r;
|
||||||
|
@ -1237,7 +1241,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||||
// characters stored for *undoing* don't leave room for redo
|
// characters stored for *undoing* don't leave room for redo
|
||||||
// if the last is true, we have to bail
|
// if the last is true, we have to bail
|
||||||
|
|
||||||
if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) {
|
if (s->undo_char_point + u.delete_length >= IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
|
||||||
// the undo records take up too much character space; there's no space to store the redo characters
|
// the undo records take up too much character space; there's no space to store the redo characters
|
||||||
r->insert_length = 0;
|
r->insert_length = 0;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1246,7 +1250,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||||
// there's definitely room to store the characters eventually
|
// there's definitely room to store the characters eventually
|
||||||
while (s->undo_char_point + u.delete_length > s->redo_char_point) {
|
while (s->undo_char_point + u.delete_length > s->redo_char_point) {
|
||||||
// should never happen:
|
// should never happen:
|
||||||
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
|
if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
|
||||||
return;
|
return;
|
||||||
// there's currently not enough room, so discard a redo record
|
// there's currently not enough room, so discard a redo record
|
||||||
stb_textedit_discard_redo(s);
|
stb_textedit_discard_redo(s);
|
||||||
|
@ -1278,11 +1282,11 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||||
s->redo_point--;
|
s->redo_point--;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||||
{
|
{
|
||||||
StbUndoState *s = &state->undostate;
|
StbUndoState *s = &state->undostate;
|
||||||
StbUndoRecord *u, r;
|
StbUndoRecord *u, r;
|
||||||
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
|
if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// we need to do two things: apply the redo record, and create an undo record
|
// we need to do two things: apply the redo record, and create an undo record
|
||||||
|
@ -1334,20 +1338,20 @@ static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int le
|
||||||
stb_text_createundo(&state->undostate, where, 0, length);
|
stb_text_createundo(&state->undostate, where, 0, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
|
static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
|
IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
|
||||||
if (p) {
|
if (p) {
|
||||||
for (i=0; i < length; ++i)
|
for (i=0; i < length; ++i)
|
||||||
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
|
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
|
static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
|
IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
|
||||||
if (p) {
|
if (p) {
|
||||||
for (i=0; i < old_length; ++i)
|
for (i=0; i < old_length; ++i)
|
||||||
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
|
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
|
||||||
|
@ -1359,8 +1363,8 @@ static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_lin
|
||||||
{
|
{
|
||||||
state->undostate.undo_point = 0;
|
state->undostate.undo_point = 0;
|
||||||
state->undostate.undo_char_point = 0;
|
state->undostate.undo_char_point = 0;
|
||||||
state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
|
state->undostate.redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
|
||||||
state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
|
state->undostate.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
|
||||||
state->select_end = state->select_start = 0;
|
state->select_end = state->select_start = 0;
|
||||||
state->cursor = 0;
|
state->cursor = 0;
|
||||||
state->has_preferred_x = 0;
|
state->has_preferred_x = 0;
|
||||||
|
@ -1383,16 +1387,16 @@ static void stb_textedit_initialize_state(STB_TexteditState *state, int is_singl
|
||||||
#pragma GCC diagnostic ignored "-Wcast-qual"
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len)
|
static int stb_textedit_paste(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE const *ctext, int len)
|
||||||
{
|
{
|
||||||
return stb_textedit_paste_internal(str, state, (STB_TEXTEDIT_CHARTYPE *) ctext, len);
|
return stb_textedit_paste_internal(str, state, (IMSTB_TEXTEDIT_CHARTYPE *) ctext, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif//STB_TEXTEDIT_IMPLEMENTATION
|
#endif//IMSTB_TEXTEDIT_IMPLEMENTATION
|
||||||
|
|
||||||
/*
|
/*
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
|
|
|
@ -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 gui = @import("gui.zig");
|
||||||
|
const backend_glfw = @import("backend_glfw.zig");
|
||||||
|
const backend_dx12 = @import("backend_dx12.zig");
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
window: *const anyopaque, // zglfw.Window
|
window: *const anyopaque, // zglfw.Window
|
||||||
|
@ -6,33 +8,28 @@ pub fn init(
|
||||||
num_frames_in_flight: u32,
|
num_frames_in_flight: u32,
|
||||||
rtv_format: c_uint, // DXGI_FORMAT
|
rtv_format: c_uint, // DXGI_FORMAT
|
||||||
cbv_srv_heap: *const anyopaque, // ID3D12DescriptorHeap
|
cbv_srv_heap: *const anyopaque, // ID3D12DescriptorHeap
|
||||||
font_srv_cpu_desc_handle: D3D12_CPU_DESCRIPTOR_HANDLE,
|
font_srv_cpu_desc_handle: backend_dx12.D3D12_CPU_DESCRIPTOR_HANDLE,
|
||||||
font_srv_gpu_desc_handle: D3D12_GPU_DESCRIPTOR_HANDLE,
|
font_srv_gpu_desc_handle: backend_dx12.D3D12_GPU_DESCRIPTOR_HANDLE,
|
||||||
) void {
|
) void {
|
||||||
if (!ImGui_ImplGlfw_InitForOther(window, true)) {
|
backend_glfw.init(window);
|
||||||
@panic("failed to init glfw for imgui");
|
backend_dx12.init(
|
||||||
}
|
|
||||||
|
|
||||||
if (!ImGui_ImplDX12_Init(
|
|
||||||
device,
|
device,
|
||||||
num_frames_in_flight,
|
num_frames_in_flight,
|
||||||
rtv_format,
|
rtv_format,
|
||||||
cbv_srv_heap,
|
cbv_srv_heap,
|
||||||
font_srv_cpu_desc_handle,
|
font_srv_cpu_desc_handle,
|
||||||
font_srv_gpu_desc_handle,
|
font_srv_gpu_desc_handle,
|
||||||
)) {
|
);
|
||||||
@panic("failed to init d3d12 for imgui");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit() void {
|
pub fn deinit() void {
|
||||||
ImGui_ImplGlfw_Shutdown();
|
backend_dx12.deinit();
|
||||||
ImGui_ImplDX12_Shutdown();
|
backend_glfw.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn newFrame(fb_width: u32, fb_height: u32) void {
|
pub fn newFrame(fb_width: u32, fb_height: u32) void {
|
||||||
ImGui_ImplGlfw_NewFrame();
|
backend_glfw.newFrame();
|
||||||
ImGui_ImplDX12_NewFrame();
|
backend_dx12.newFrame();
|
||||||
|
|
||||||
gui.io.setDisplaySize(@as(f32, @floatFromInt(fb_width)), @as(f32, @floatFromInt(fb_height)));
|
gui.io.setDisplaySize(@as(f32, @floatFromInt(fb_width)), @as(f32, @floatFromInt(fb_height)));
|
||||||
gui.io.setDisplayFramebufferScale(1.0, 1.0);
|
gui.io.setDisplayFramebufferScale(1.0, 1.0);
|
||||||
|
@ -44,31 +41,5 @@ pub fn draw(
|
||||||
graphics_command_list: *const anyopaque, // *ID3D12GraphicsCommandList
|
graphics_command_list: *const anyopaque, // *ID3D12GraphicsCommandList
|
||||||
) void {
|
) void {
|
||||||
gui.render();
|
gui.render();
|
||||||
ImGui_ImplDX12_RenderDrawData(gui.getDrawData(), graphics_command_list);
|
backend_dx12.render(gui.getDrawData(), graphics_command_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const D3D12_CPU_DESCRIPTOR_HANDLE = extern struct {
|
|
||||||
ptr: c_ulonglong,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const D3D12_GPU_DESCRIPTOR_HANDLE = extern struct {
|
|
||||||
ptr: c_ulonglong,
|
|
||||||
};
|
|
||||||
|
|
||||||
extern fn ImGui_ImplGlfw_InitForOther(window: *const anyopaque, install_callbacks: bool) bool;
|
|
||||||
extern fn ImGui_ImplGlfw_NewFrame() void;
|
|
||||||
extern fn ImGui_ImplGlfw_Shutdown() void;
|
|
||||||
extern fn ImGui_ImplDX12_Init(
|
|
||||||
device: *const anyopaque, // ID3D12Device
|
|
||||||
num_frames_in_flight: u32,
|
|
||||||
rtv_format: u32, // DXGI_FORMAT
|
|
||||||
cbv_srv_heap: *const anyopaque, // ID3D12DescriptorHeap
|
|
||||||
font_srv_cpu_desc_handle: D3D12_CPU_DESCRIPTOR_HANDLE,
|
|
||||||
font_srv_gpu_desc_handle: D3D12_GPU_DESCRIPTOR_HANDLE,
|
|
||||||
) bool;
|
|
||||||
extern fn ImGui_ImplDX12_Shutdown() void;
|
|
||||||
extern fn ImGui_ImplDX12_NewFrame() void;
|
|
||||||
extern fn ImGui_ImplDX12_RenderDrawData(
|
|
||||||
draw_data: *const anyopaque, // *ImDrawData
|
|
||||||
graphics_command_list: *const anyopaque, // *ID3D12GraphicsCommandList
|
|
||||||
) void;
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
const gui = @import("gui.zig");
|
const gui = @import("gui.zig");
|
||||||
|
const backend_glfw = @import("backend_glfw.zig");
|
||||||
|
|
||||||
pub fn initWithGlSlVersion(
|
pub fn initWithGlSlVersion(
|
||||||
window: *const anyopaque, // zglfw.Window
|
window: *const anyopaque, // zglfw.Window
|
||||||
glsl_version: ?[:0]const u8, // e.g. "#version 130"
|
glsl_version: ?[:0]const u8, // e.g. "#version 130"
|
||||||
) void {
|
) void {
|
||||||
if (!ImGui_ImplGlfw_InitForOpenGL(window, true)) {
|
backend_glfw.initOpenGL(window);
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui_ImplOpenGL3_Init(@ptrCast(glsl_version));
|
ImGui_ImplOpenGL3_Init(@ptrCast(glsl_version));
|
||||||
}
|
}
|
||||||
|
@ -18,12 +17,12 @@ pub fn init(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit() void {
|
pub fn deinit() void {
|
||||||
ImGui_ImplGlfw_Shutdown();
|
|
||||||
ImGui_ImplOpenGL3_Shutdown();
|
ImGui_ImplOpenGL3_Shutdown();
|
||||||
|
backend_glfw.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn newFrame(fb_width: u32, fb_height: u32) void {
|
pub fn newFrame(fb_width: u32, fb_height: u32) void {
|
||||||
ImGui_ImplGlfw_NewFrame();
|
backend_glfw.newFrame();
|
||||||
ImGui_ImplOpenGL3_NewFrame();
|
ImGui_ImplOpenGL3_NewFrame();
|
||||||
|
|
||||||
gui.io.setDisplaySize(@as(f32, @floatFromInt(fb_width)), @as(f32, @floatFromInt(fb_height)));
|
gui.io.setDisplaySize(@as(f32, @floatFromInt(fb_width)), @as(f32, @floatFromInt(fb_height)));
|
||||||
|
@ -37,9 +36,8 @@ pub fn draw() void {
|
||||||
ImGui_ImplOpenGL3_RenderDrawData(gui.getDrawData());
|
ImGui_ImplOpenGL3_RenderDrawData(gui.getDrawData());
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn ImGui_ImplGlfw_InitForOpenGL(window: *const anyopaque, install_callbacks: bool) bool;
|
// Those functions are defined in 'imgui_impl_opengl3.cpp`
|
||||||
extern fn ImGui_ImplGlfw_NewFrame() void;
|
// (they include few custom changes).
|
||||||
extern fn ImGui_ImplGlfw_Shutdown() void;
|
|
||||||
extern fn ImGui_ImplOpenGL3_Init(glsl_version: [*c]const u8) void;
|
extern fn ImGui_ImplOpenGL3_Init(glsl_version: [*c]const u8) void;
|
||||||
extern fn ImGui_ImplOpenGL3_Shutdown() void;
|
extern fn ImGui_ImplOpenGL3_Shutdown() void;
|
||||||
extern fn ImGui_ImplOpenGL3_NewFrame() void;
|
extern fn ImGui_ImplOpenGL3_NewFrame() void;
|
||||||
|
|
|
@ -1,33 +1,40 @@
|
||||||
const gui = @import("gui.zig");
|
const gui = @import("gui.zig");
|
||||||
|
const backend_glfw = @import("backend_glfw.zig");
|
||||||
|
|
||||||
// This call will install GLFW callbacks to handle GUI interactions.
|
// This call will install GLFW callbacks to handle GUI interactions.
|
||||||
// Those callbacks will chain-call user's previously installed callbacks, if any.
|
// Those callbacks will chain-call user's previously installed callbacks, if any.
|
||||||
// This means that custom user's callbacks need to be installed *before* calling zgpu.gui.init().
|
// This means that custom user's callbacks need to be installed *before* calling zgpu.gui.init().
|
||||||
pub fn init(
|
pub fn init(
|
||||||
window: *const anyopaque, // zglfw.Window
|
window: *const anyopaque, // zglfw.Window
|
||||||
wgpu_device: *const anyopaque, // WGPUDevice
|
wgpu_device: *const anyopaque, // wgpu.Device
|
||||||
wgpu_swap_chain_format: u32, // WGPUTextureFormat
|
wgpu_swap_chain_format: u32, // wgpu.TextureFormat
|
||||||
wgpu_depth_format: u32, // WGPUTextureFormat
|
wgpu_depth_format: u32, // wgpu.TextureFormat
|
||||||
) void {
|
) void {
|
||||||
if (!ImGui_ImplGlfw_InitForOther(window, true)) {
|
backend_glfw.init(window);
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ImGui_ImplWGPU_Init(wgpu_device, 1, wgpu_swap_chain_format, wgpu_depth_format)) {
|
var info = ImGui_ImplWGPU_InitInfo{
|
||||||
|
.device = wgpu_device,
|
||||||
|
.num_frames_in_flight = 1,
|
||||||
|
.rt_format = wgpu_swap_chain_format,
|
||||||
|
.depth_format = wgpu_depth_format,
|
||||||
|
.pipeline_multisample_state = .{},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!ImGui_ImplWGPU_Init(&info)) {
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit() void {
|
pub fn deinit() void {
|
||||||
ImGui_ImplWGPU_Shutdown();
|
ImGui_ImplWGPU_Shutdown();
|
||||||
ImGui_ImplGlfw_Shutdown();
|
backend_glfw.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn newFrame(fb_width: u32, fb_height: u32) void {
|
pub fn newFrame(fb_width: u32, fb_height: u32) void {
|
||||||
ImGui_ImplWGPU_NewFrame();
|
ImGui_ImplWGPU_NewFrame();
|
||||||
ImGui_ImplGlfw_NewFrame();
|
backend_glfw.newFrame();
|
||||||
|
|
||||||
gui.io.setDisplaySize(@as(f32, @floatFromInt(fb_width)), @as(f32, @floatFromInt(fb_height)));
|
gui.io.setDisplaySize(@floatFromInt(fb_width), @floatFromInt(fb_height));
|
||||||
gui.io.setDisplayFramebufferScale(1.0, 1.0);
|
gui.io.setDisplayFramebufferScale(1.0, 1.0);
|
||||||
|
|
||||||
gui.newFrame();
|
gui.newFrame();
|
||||||
|
@ -38,17 +45,23 @@ pub fn draw(wgpu_render_pass: *const anyopaque) void {
|
||||||
ImGui_ImplWGPU_RenderDrawData(gui.getDrawData(), wgpu_render_pass);
|
ImGui_ImplWGPU_RenderDrawData(gui.getDrawData(), wgpu_render_pass);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Those functions are defined in `imgui_impl_glfw.cpp` and 'imgui_impl_wgpu.cpp`
|
pub const ImGui_ImplWGPU_InitInfo = extern struct {
|
||||||
|
device: *const anyopaque,
|
||||||
|
num_frames_in_flight: u32 = 1,
|
||||||
|
rt_format: u32,
|
||||||
|
depth_format: u32,
|
||||||
|
|
||||||
|
pipeline_multisample_state: extern struct {
|
||||||
|
next_in_chain: ?*const anyopaque = null,
|
||||||
|
count: u32 = 1,
|
||||||
|
mask: u32 = @bitCast(@as(i32, -1)),
|
||||||
|
alpha_to_coverage_enabled: bool = false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Those functions are defined in 'imgui_impl_wgpu.cpp`
|
||||||
// (they include few custom changes).
|
// (they include few custom changes).
|
||||||
extern fn ImGui_ImplGlfw_InitForOther(window: *const anyopaque, install_callbacks: bool) bool;
|
extern fn ImGui_ImplWGPU_Init(init_info: *ImGui_ImplWGPU_InitInfo) bool;
|
||||||
extern fn ImGui_ImplGlfw_NewFrame() void;
|
|
||||||
extern fn ImGui_ImplGlfw_Shutdown() void;
|
|
||||||
extern fn ImGui_ImplWGPU_Init(
|
|
||||||
device: *const anyopaque, // WGPUDevice
|
|
||||||
num_frames_in_flight: u32,
|
|
||||||
rt_format: u32, // WGPUTextureFormat
|
|
||||||
wgpu_depth_format: u32, // WGPUTextureFormat
|
|
||||||
) bool;
|
|
||||||
extern fn ImGui_ImplWGPU_NewFrame() void;
|
extern fn ImGui_ImplWGPU_NewFrame() void;
|
||||||
extern fn ImGui_ImplWGPU_RenderDrawData(draw_data: *const anyopaque, pass_encoder: *const anyopaque) void;
|
extern fn ImGui_ImplWGPU_RenderDrawData(draw_data: *const anyopaque, pass_encoder: *const anyopaque) void;
|
||||||
extern fn ImGui_ImplWGPU_Shutdown() void;
|
extern fn ImGui_ImplWGPU_Shutdown() void;
|
||||||
|
|
|
@ -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 plot = @import("plot.zig");
|
||||||
|
pub const te = @import("te.zig");
|
||||||
pub const backend = switch (@import("zgui_options").backend) {
|
pub const backend = switch (@import("zgui_options").backend) {
|
||||||
.glfw_wgpu => @import("backend_glfw_wgpu.zig"),
|
.glfw_wgpu => @import("backend_glfw_wgpu.zig"),
|
||||||
.glfw_opengl3 => @import("backend_glfw_opengl.zig"),
|
.glfw_opengl3 => @import("backend_glfw_opengl.zig"),
|
||||||
.glfw_dx12 => @import("backend_glfw_dx12.zig"),
|
.glfw_dx12 => @import("backend_glfw_dx12.zig"),
|
||||||
.win32_dx12 => .{}, // TODO:
|
.glfw => @import("backend_glfw.zig"),
|
||||||
|
.win32_dx12 => @import("backend_win32_dx12.zig"),
|
||||||
.no_backend => .{},
|
.no_backend => .{},
|
||||||
};
|
};
|
||||||
|
const te_enabled = @import("zgui_options").with_te;
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
@ -26,6 +29,7 @@ pub const DrawVert = extern struct {
|
||||||
color: u32,
|
color: u32,
|
||||||
};
|
};
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator) void {
|
pub fn init(allocator: std.mem.Allocator) void {
|
||||||
if (zguiGetCurrentContext() == null) {
|
if (zguiGetCurrentContext() == null) {
|
||||||
mem_allocator = allocator;
|
mem_allocator = allocator;
|
||||||
|
@ -37,6 +41,10 @@ pub fn init(allocator: std.mem.Allocator) void {
|
||||||
|
|
||||||
temp_buffer = std.ArrayList(u8).init(allocator);
|
temp_buffer = std.ArrayList(u8).init(allocator);
|
||||||
temp_buffer.?.resize(3 * 1024 + 1) catch unreachable;
|
temp_buffer.?.resize(3 * 1024 + 1) catch unreachable;
|
||||||
|
|
||||||
|
if (te_enabled) {
|
||||||
|
te.init();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn deinit() void {
|
pub fn deinit() void {
|
||||||
|
@ -44,6 +52,12 @@ pub fn deinit() void {
|
||||||
temp_buffer.?.deinit();
|
temp_buffer.?.deinit();
|
||||||
zguiDestroyContext(null);
|
zguiDestroyContext(null);
|
||||||
|
|
||||||
|
// Must be after destroy imgui context.
|
||||||
|
// And before allocation check
|
||||||
|
if (te_enabled) {
|
||||||
|
te.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
if (mem_allocations.?.count() > 0) {
|
if (mem_allocations.?.count() > 0) {
|
||||||
var it = mem_allocations.?.iterator();
|
var it = mem_allocations.?.iterator();
|
||||||
while (it.next()) |kv| {
|
while (it.next()) |kv| {
|
||||||
|
@ -124,7 +138,13 @@ pub const ConfigFlags = packed struct(c_int) {
|
||||||
nav_no_capture_keyboard: bool = false,
|
nav_no_capture_keyboard: bool = false,
|
||||||
no_mouse: bool = false,
|
no_mouse: bool = false,
|
||||||
no_mouse_cursor_change: bool = false,
|
no_mouse_cursor_change: bool = false,
|
||||||
user_storage: u14 = 0,
|
dock_enable: bool = false,
|
||||||
|
_pading0: u3 = 0,
|
||||||
|
viewport_enable: bool = false,
|
||||||
|
_pading1: u3 = 0,
|
||||||
|
dpi_enable_scale_viewport: bool = false,
|
||||||
|
dpi_enable_scale_fonts: bool = false,
|
||||||
|
user_storage: u4 = 0,
|
||||||
is_srgb: bool = false,
|
is_srgb: bool = false,
|
||||||
is_touch_screen: bool = false,
|
is_touch_screen: bool = false,
|
||||||
_padding: u10 = 0,
|
_padding: u10 = 0,
|
||||||
|
@ -147,6 +167,7 @@ pub const FontConfig = extern struct {
|
||||||
merge_mode: bool,
|
merge_mode: bool,
|
||||||
font_builder_flags: c_uint,
|
font_builder_flags: c_uint,
|
||||||
rasterizer_multiply: f32,
|
rasterizer_multiply: f32,
|
||||||
|
rasterizer_density: f32,
|
||||||
ellipsis_char: Wchar,
|
ellipsis_char: Wchar,
|
||||||
name: [40]u8,
|
name: [40]u8,
|
||||||
dst_font: *Font,
|
dst_font: *Font,
|
||||||
|
@ -326,7 +347,7 @@ pub const DrawData = *extern struct {
|
||||||
cmd_lists_count: c_int,
|
cmd_lists_count: c_int,
|
||||||
total_idx_count: c_int,
|
total_idx_count: c_int,
|
||||||
total_vtx_count: c_int,
|
total_vtx_count: c_int,
|
||||||
cmd_lists: [*]DrawList,
|
cmd_lists: Vector(DrawList),
|
||||||
display_pos: [2]f32,
|
display_pos: [2]f32,
|
||||||
display_size: [2]f32,
|
display_size: [2]f32,
|
||||||
framebuffer_scale: [2]f32,
|
framebuffer_scale: [2]f32,
|
||||||
|
@ -334,7 +355,7 @@ pub const DrawData = *extern struct {
|
||||||
pub const Font = *opaque {};
|
pub const Font = *opaque {};
|
||||||
pub const Ident = u32;
|
pub const Ident = u32;
|
||||||
pub const TextureIdent = *anyopaque;
|
pub const TextureIdent = *anyopaque;
|
||||||
pub const Wchar = u16;
|
pub const Wchar = if (@import("zgui_options").use_wchar32) u32 else u16;
|
||||||
pub const Key = enum(c_int) {
|
pub const Key = enum(c_int) {
|
||||||
none = 0,
|
none = 0,
|
||||||
tab = 512,
|
tab = 512,
|
||||||
|
@ -409,6 +430,18 @@ pub const Key = enum(c_int) {
|
||||||
f10,
|
f10,
|
||||||
f11,
|
f11,
|
||||||
f12,
|
f12,
|
||||||
|
f13,
|
||||||
|
f14,
|
||||||
|
f15,
|
||||||
|
f16,
|
||||||
|
f17,
|
||||||
|
f18,
|
||||||
|
f19,
|
||||||
|
f20,
|
||||||
|
f21,
|
||||||
|
f22,
|
||||||
|
f23,
|
||||||
|
f24,
|
||||||
apostrophe,
|
apostrophe,
|
||||||
comma,
|
comma,
|
||||||
minus,
|
minus,
|
||||||
|
@ -443,6 +476,9 @@ pub const Key = enum(c_int) {
|
||||||
keypad_enter,
|
keypad_enter,
|
||||||
keypad_equal,
|
keypad_equal,
|
||||||
|
|
||||||
|
app_back,
|
||||||
|
app_forward,
|
||||||
|
|
||||||
gamepad_start,
|
gamepad_start,
|
||||||
gamepad_back,
|
gamepad_back,
|
||||||
gamepad_faceleft,
|
gamepad_faceleft,
|
||||||
|
@ -483,6 +519,7 @@ pub const Key = enum(c_int) {
|
||||||
mod_super = 1 << 15,
|
mod_super = 1 << 15,
|
||||||
mod_mask_ = 0xf000,
|
mod_mask_ = 0xf000,
|
||||||
};
|
};
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
pub const WindowFlags = packed struct(c_int) {
|
pub const WindowFlags = packed struct(c_int) {
|
||||||
no_title_bar: bool = false,
|
no_title_bar: bool = false,
|
||||||
|
@ -501,12 +538,11 @@ pub const WindowFlags = packed struct(c_int) {
|
||||||
no_bring_to_front_on_focus: bool = false,
|
no_bring_to_front_on_focus: bool = false,
|
||||||
always_vertical_scrollbar: bool = false,
|
always_vertical_scrollbar: bool = false,
|
||||||
always_horizontal_scrollbar: bool = false,
|
always_horizontal_scrollbar: bool = false,
|
||||||
always_use_window_padding: bool = false,
|
|
||||||
_removed: u1 = 0,
|
|
||||||
no_nav_inputs: bool = false,
|
no_nav_inputs: bool = false,
|
||||||
no_nav_focus: bool = false,
|
no_nav_focus: bool = false,
|
||||||
unsaved_document: bool = false,
|
unsaved_document: bool = false,
|
||||||
_padding: u11 = 0,
|
no_docking: bool = false,
|
||||||
|
_padding: u12 = 0,
|
||||||
|
|
||||||
pub const no_nav = WindowFlags{ .no_nav_inputs = true, .no_nav_focus = true };
|
pub const no_nav = WindowFlags{ .no_nav_inputs = true, .no_nav_focus = true };
|
||||||
pub const no_decoration = WindowFlags{
|
pub const no_decoration = WindowFlags{
|
||||||
|
@ -521,6 +557,20 @@ pub const WindowFlags = packed struct(c_int) {
|
||||||
.no_nav_focus = true,
|
.no_nav_focus = true,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const ChildFlags = packed struct(c_int) {
|
||||||
|
border: bool = false,
|
||||||
|
no_move: bool = false,
|
||||||
|
always_use_window_padding: bool = false,
|
||||||
|
resize_x: bool = false,
|
||||||
|
resize_y: bool = false,
|
||||||
|
auto_resize_x: bool = false,
|
||||||
|
auto_resize_y: bool = false,
|
||||||
|
always_auto_resize: bool = false,
|
||||||
|
frame_style: bool = false,
|
||||||
|
_padding: u23 = 0,
|
||||||
|
};
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
pub const SliderFlags = packed struct(c_int) {
|
pub const SliderFlags = packed struct(c_int) {
|
||||||
_reserved0: bool = false,
|
_reserved0: bool = false,
|
||||||
|
@ -629,7 +679,12 @@ pub fn setNextWindowBgAlpha(args: SetNextWindowBgAlpha) void {
|
||||||
zguiSetNextWindowBgAlpha(args.alpha);
|
zguiSetNextWindowBgAlpha(args.alpha);
|
||||||
}
|
}
|
||||||
extern fn zguiSetNextWindowBgAlpha(alpha: f32) void;
|
extern fn zguiSetNextWindowBgAlpha(alpha: f32) void;
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
pub fn setWindowFocus(name: ?[:0]const u8) void {
|
||||||
|
zguiSetWindowFocus(name orelse null);
|
||||||
|
}
|
||||||
|
extern fn zguiSetWindowFocus(name: ?[*:0]const u8) void;
|
||||||
|
//-------------------------------------------------------------------------------------------------
|
||||||
pub fn setKeyboardFocusHere(offset: i32) void {
|
pub fn setKeyboardFocusHere(offset: i32) void {
|
||||||
zguiSetKeyboardFocusHere(offset);
|
zguiSetKeyboardFocusHere(offset);
|
||||||
}
|
}
|
||||||
|
@ -651,19 +706,19 @@ extern fn zguiEnd() void;
|
||||||
const BeginChild = struct {
|
const BeginChild = struct {
|
||||||
w: f32 = 0.0,
|
w: f32 = 0.0,
|
||||||
h: f32 = 0.0,
|
h: f32 = 0.0,
|
||||||
border: bool = false,
|
child_flags: ChildFlags = .{},
|
||||||
flags: WindowFlags = .{},
|
window_flags: WindowFlags = .{},
|
||||||
};
|
};
|
||||||
pub fn beginChild(str_id: [:0]const u8, args: BeginChild) bool {
|
pub fn beginChild(str_id: [:0]const u8, args: BeginChild) bool {
|
||||||
return zguiBeginChild(str_id, args.w, args.h, args.border, args.flags);
|
return zguiBeginChild(str_id, args.w, args.h, args.child_flags, args.window_flags);
|
||||||
}
|
}
|
||||||
pub fn beginChildId(id: Ident, args: BeginChild) bool {
|
pub fn beginChildId(id: Ident, args: BeginChild) bool {
|
||||||
return zguiBeginChildId(id, args.w, args.h, args.border, args.flags);
|
return zguiBeginChildId(id, args.w, args.h, args.child_flags, args.window_flags);
|
||||||
}
|
}
|
||||||
/// `pub fn endChild() void`
|
/// `pub fn endChild() void`
|
||||||
pub const endChild = zguiEndChild;
|
pub const endChild = zguiEndChild;
|
||||||
extern fn zguiBeginChild(str_id: [*:0]const u8, w: f32, h: f32, border: bool, flags: WindowFlags) bool;
|
extern fn zguiBeginChild(str_id: [*:0]const u8, w: f32, h: f32, flags: ChildFlags, window_flags: WindowFlags) bool;
|
||||||
extern fn zguiBeginChildId(id: Ident, w: f32, h: f32, border: bool, flags: WindowFlags) bool;
|
extern fn zguiBeginChildId(id: Ident, w: f32, h: f32, flags: ChildFlags, window_flags: WindowFlags) bool;
|
||||||
extern fn zguiEndChild() void;
|
extern fn zguiEndChild() void;
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
/// `pub fn zguiGetScrollX() f32`
|
/// `pub fn zguiGetScrollX() f32`
|
||||||
|
@ -720,7 +775,8 @@ pub const FocusedFlags = packed struct(c_int) {
|
||||||
root_window: bool = false,
|
root_window: bool = false,
|
||||||
any_window: bool = false,
|
any_window: bool = false,
|
||||||
no_popup_hierarchy: bool = false,
|
no_popup_hierarchy: bool = false,
|
||||||
_padding: u28 = 0,
|
dock_hierarchy: bool = false,
|
||||||
|
_padding: u27 = 0,
|
||||||
|
|
||||||
pub const root_and_child_windows = FocusedFlags{ .root_window = true, .child_windows = true };
|
pub const root_and_child_windows = FocusedFlags{ .root_window = true, .child_windows = true };
|
||||||
};
|
};
|
||||||
|
@ -730,22 +786,27 @@ pub const HoveredFlags = packed struct(c_int) {
|
||||||
root_window: bool = false,
|
root_window: bool = false,
|
||||||
any_window: bool = false,
|
any_window: bool = false,
|
||||||
no_popup_hierarchy: bool = false,
|
no_popup_hierarchy: bool = false,
|
||||||
_reserved0: bool = false,
|
dock_hierarchy: bool = false,
|
||||||
allow_when_blocked_by_popup: bool = false,
|
allow_when_blocked_by_popup: bool = false,
|
||||||
_reserved1: bool = false,
|
_reserved1: bool = false,
|
||||||
allow_when_blocked_by_active_item: bool = false,
|
allow_when_blocked_by_active_item: bool = false,
|
||||||
allow_when_overlapped: bool = false,
|
allow_when_overlapped_by_item: bool = false,
|
||||||
|
allow_when_overlapped_by_window: bool = false,
|
||||||
allow_when_disabled: bool = false,
|
allow_when_disabled: bool = false,
|
||||||
no_nav_override: bool = false,
|
no_nav_override: bool = false,
|
||||||
|
for_tooltip: bool = false,
|
||||||
|
stationary: bool = false,
|
||||||
|
delay_none: bool = false,
|
||||||
delay_normal: bool = false,
|
delay_normal: bool = false,
|
||||||
delay_short: bool = false,
|
delay_short: bool = false,
|
||||||
no_shared_delay: bool = false,
|
no_shared_delay: bool = false,
|
||||||
_padding: u18 = 0,
|
_padding: u14 = 0,
|
||||||
|
|
||||||
pub const rect_only = HoveredFlags{
|
pub const rect_only = HoveredFlags{
|
||||||
.allow_when_blocked_by_popup = true,
|
.allow_when_blocked_by_popup = true,
|
||||||
.allow_when_blocked_by_active_item = true,
|
.allow_when_blocked_by_active_item = true,
|
||||||
.allow_when_overlapped = true,
|
.allow_when_overlapped_by_item = true,
|
||||||
|
.allow_when_overlapped_by_window = true,
|
||||||
};
|
};
|
||||||
pub const root_and_child_windows = HoveredFlags{ .root_window = true, .child_windows = true };
|
pub const root_and_child_windows = HoveredFlags{ .root_window = true, .child_windows = true };
|
||||||
};
|
};
|
||||||
|
@ -812,6 +873,79 @@ extern fn zguiGetContentRegionAvail(size: *[2]f32) void;
|
||||||
extern fn zguiGetContentRegionMax(size: *[2]f32) void;
|
extern fn zguiGetContentRegionMax(size: *[2]f32) void;
|
||||||
extern fn zguiGetWindowContentRegionMin(size: *[2]f32) void;
|
extern fn zguiGetWindowContentRegionMin(size: *[2]f32) void;
|
||||||
extern fn zguiGetWindowContentRegionMax(size: *[2]f32) void;
|
extern fn zguiGetWindowContentRegionMax(size: *[2]f32) void;
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Docking
|
||||||
|
//
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
pub const DockNodeFlags = packed struct(c_int) {
|
||||||
|
keep_alive_only: bool = false,
|
||||||
|
_reserved: u1 = 0,
|
||||||
|
no_docking_over_central_node: bool = false,
|
||||||
|
passthru_central_node: bool = false,
|
||||||
|
no_docking_split: bool = false,
|
||||||
|
no_resize: bool = false,
|
||||||
|
auto_hide_tab_bar: bool = false,
|
||||||
|
no_undocking: bool = false,
|
||||||
|
_padding_0: u2 = 0,
|
||||||
|
|
||||||
|
// Extended enum entries from imgui_internal (unstable, subject to change, use at own risk)
|
||||||
|
dock_space: bool = false,
|
||||||
|
central_node: bool = false,
|
||||||
|
no_tab_bar: bool = false,
|
||||||
|
hidden_tab_bar: bool = false,
|
||||||
|
no_window_menu_button: bool = false,
|
||||||
|
no_close_button: bool = false,
|
||||||
|
no_resize_x: bool = false,
|
||||||
|
no_resize_y: bool = false,
|
||||||
|
docked_windows_in_focus_route: bool = false,
|
||||||
|
no_docking_split_other: bool = false,
|
||||||
|
no_docking_over_me: bool = false,
|
||||||
|
no_docking_over_other: bool = false,
|
||||||
|
no_docking_over_empty: bool = false,
|
||||||
|
_padding_1: u9 = 0,
|
||||||
|
};
|
||||||
|
extern fn zguiDockSpace(str_id: [*:0]const u8, size: *const [2]f32, flags: DockNodeFlags) Ident;
|
||||||
|
|
||||||
|
pub fn DockSpace(str_id: [:0]const u8, size: [2]f32, flags: DockNodeFlags) Ident {
|
||||||
|
return zguiDockSpace(str_id.ptr, &size, flags);
|
||||||
|
}
|
||||||
|
extern fn zguiDockSpaceOverViewport(viewport: Viewport, flags: DockNodeFlags) Ident;
|
||||||
|
pub const DockSpaceOverViewport = zguiDockSpaceOverViewport;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// DockBuilder (Unstable internal imgui API, subject to change, use at own risk)
|
||||||
|
//
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
pub fn dockBuilderDockWindow(window_name: [:0]const u8, node_id: Ident) void {
|
||||||
|
zguiDockBuilderDockWindow(window_name.ptr, node_id);
|
||||||
|
}
|
||||||
|
pub const dockBuilderAddNode = zguiDockBuilderAddNode;
|
||||||
|
pub const dockBuilderRemoveNode = zguiDockBuilderRemoveNode;
|
||||||
|
pub fn dockBuilderSetNodePos(node_id: Ident, pos: [2]f32) void {
|
||||||
|
zguiDockBuilderSetNodePos(node_id, &pos);
|
||||||
|
}
|
||||||
|
pub fn dockBuilderSetNodeSize(node_id: Ident, size: [2]f32) void {
|
||||||
|
zguiDockBuilderSetNodeSize(node_id, &size);
|
||||||
|
}
|
||||||
|
pub const dockBuilderSplitNode = zguiDockBuilderSplitNode;
|
||||||
|
pub const dockBuilderFinish = zguiDockBuilderFinish;
|
||||||
|
|
||||||
|
extern fn zguiDockBuilderDockWindow(window_name: [*:0]const u8, node_id: Ident) void;
|
||||||
|
extern fn zguiDockBuilderAddNode(node_id: Ident, flags: DockNodeFlags) Ident;
|
||||||
|
extern fn zguiDockBuilderRemoveNode(node_id: Ident) void;
|
||||||
|
extern fn zguiDockBuilderSetNodePos(node_id: Ident, pos: *const [2]f32) void;
|
||||||
|
extern fn zguiDockBuilderSetNodeSize(node_id: Ident, size: *const [2]f32) void;
|
||||||
|
extern fn zguiDockBuilderSplitNode(
|
||||||
|
node_id: Ident,
|
||||||
|
split_dir: Direction,
|
||||||
|
size_ratio_for_node_at_dir: f32,
|
||||||
|
out_id_at_dir: ?*Ident,
|
||||||
|
out_id_at_opposite_dir: ?*Ident,
|
||||||
|
) Ident;
|
||||||
|
extern fn zguiDockBuilderFinish(node_id: Ident) void;
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// Style
|
// Style
|
||||||
|
@ -847,6 +981,8 @@ pub const Style = extern struct {
|
||||||
tab_rounding: f32,
|
tab_rounding: f32,
|
||||||
tab_border_size: f32,
|
tab_border_size: f32,
|
||||||
tab_min_width_for_close_button: f32,
|
tab_min_width_for_close_button: f32,
|
||||||
|
tab_bar_border_size: f32,
|
||||||
|
table_angled_header_angle: f32,
|
||||||
color_button_position: Direction,
|
color_button_position: Direction,
|
||||||
button_text_align: [2]f32,
|
button_text_align: [2]f32,
|
||||||
selectable_text_align: [2]f32,
|
selectable_text_align: [2]f32,
|
||||||
|
@ -855,6 +991,7 @@ pub const Style = extern struct {
|
||||||
separator_text_padding: [2]f32,
|
separator_text_padding: [2]f32,
|
||||||
display_window_padding: [2]f32,
|
display_window_padding: [2]f32,
|
||||||
display_safe_area_padding: [2]f32,
|
display_safe_area_padding: [2]f32,
|
||||||
|
docking_separator_size: f32,
|
||||||
mouse_cursor_scale: f32,
|
mouse_cursor_scale: f32,
|
||||||
anti_aliased_lines: bool,
|
anti_aliased_lines: bool,
|
||||||
anti_aliased_lines_use_tex: bool,
|
anti_aliased_lines_use_tex: bool,
|
||||||
|
@ -864,6 +1001,13 @@ pub const Style = extern struct {
|
||||||
|
|
||||||
colors: [@typeInfo(StyleCol).Enum.fields.len][4]f32,
|
colors: [@typeInfo(StyleCol).Enum.fields.len][4]f32,
|
||||||
|
|
||||||
|
hover_stationary_delay: f32,
|
||||||
|
hover_delay_short: f32,
|
||||||
|
hover_delay_normal: f32,
|
||||||
|
|
||||||
|
hover_flags_for_tooltip_mouse: HoveredFlags,
|
||||||
|
hover_flags_for_tooltip_nav: HoveredFlags,
|
||||||
|
|
||||||
/// `pub fn init() Style`
|
/// `pub fn init() Style`
|
||||||
pub const init = zguiStyle_Init;
|
pub const init = zguiStyle_Init;
|
||||||
extern fn zguiStyle_Init() Style;
|
extern fn zguiStyle_Init() Style;
|
||||||
|
@ -922,6 +1066,8 @@ pub const StyleCol = enum(c_int) {
|
||||||
tab_active,
|
tab_active,
|
||||||
tab_unfocused,
|
tab_unfocused,
|
||||||
tab_unfocused_active,
|
tab_unfocused_active,
|
||||||
|
docking_preview,
|
||||||
|
docking_empty_bg,
|
||||||
plot_lines,
|
plot_lines,
|
||||||
plot_lines_hovered,
|
plot_lines_hovered,
|
||||||
plot_histogram,
|
plot_histogram,
|
||||||
|
@ -996,11 +1142,13 @@ pub const StyleVar = enum(c_int) {
|
||||||
grab_min_size, // 1f
|
grab_min_size, // 1f
|
||||||
grab_rounding, // 1f
|
grab_rounding, // 1f
|
||||||
tab_rounding, // 1f
|
tab_rounding, // 1f
|
||||||
|
tab_bar_border_size, // 1f
|
||||||
button_text_align, // 2f
|
button_text_align, // 2f
|
||||||
selectable_text_align, // 2f
|
selectable_text_align, // 2f
|
||||||
separator_text_border_size, // 1f
|
separator_text_border_size, // 1f
|
||||||
separator_text_align, // 2f
|
separator_text_align, // 2f
|
||||||
separator_text_padding, // 2f
|
separator_text_padding, // 2f
|
||||||
|
docking_separator_size, // 1f
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn pushStyleVar1f(args: struct {
|
pub fn pushStyleVar1f(args: struct {
|
||||||
|
@ -1227,8 +1375,14 @@ pub fn getItemRectMin() [2]f32 {
|
||||||
zguiGetItemRectMin(&rect);
|
zguiGetItemRectMin(&rect);
|
||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
|
pub fn getItemRectSize() [2]f32 {
|
||||||
|
var rect: [2]f32 = undefined;
|
||||||
|
zguiGetItemRectSize(&rect);
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
extern fn zguiGetItemRectMax(rect: *[2]f32) void;
|
extern fn zguiGetItemRectMax(rect: *[2]f32) void;
|
||||||
extern fn zguiGetItemRectMin(rect: *[2]f32) void;
|
extern fn zguiGetItemRectMin(rect: *[2]f32) void;
|
||||||
|
extern fn zguiGetItemRectSize(rect: *[2]f32) void;
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// ID stack/scopes
|
// ID stack/scopes
|
||||||
|
@ -1502,31 +1656,35 @@ pub fn comboFromEnum(
|
||||||
/// i32 (the underlying imgui restriction)
|
/// i32 (the underlying imgui restriction)
|
||||||
current_item: anytype,
|
current_item: anytype,
|
||||||
) bool {
|
) bool {
|
||||||
const item_names = comptime lbl: {
|
const EnumType = @TypeOf(current_item.*);
|
||||||
const item_type = @typeInfo(@TypeOf(current_item.*));
|
const enum_type_info = switch (@typeInfo(EnumType)) {
|
||||||
switch (item_type) {
|
.Enum => |enum_type_info| enum_type_info,
|
||||||
.Enum => |e| {
|
else => @compileError("Error: current_item must be a pointer-to-an-enum, not a " ++ @TypeOf(current_item)),
|
||||||
var str: [:0]const u8 = "";
|
|
||||||
|
|
||||||
for (e.fields) |f| {
|
|
||||||
str = str ++ f.name ++ "\x00";
|
|
||||||
}
|
|
||||||
break :lbl str;
|
|
||||||
},
|
|
||||||
else => {
|
|
||||||
@compileError("Error: current_item must be a pointer-to-an-enum, not a " ++ @TypeOf(current_item));
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var item: i32 = @intCast(@intFromEnum(current_item.*));
|
const FieldNameIndex = std.meta.Tuple(&.{ []const u8, i32 });
|
||||||
|
comptime var item_names: [:0]const u8 = "";
|
||||||
|
comptime var field_name_to_index_list: [enum_type_info.fields.len]FieldNameIndex = undefined;
|
||||||
|
comptime var index_to_enum: [enum_type_info.fields.len]EnumType = undefined;
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
for (enum_type_info.fields, 0..) |f, i| {
|
||||||
|
item_names = item_names ++ f.name ++ "\x00";
|
||||||
|
const e: EnumType = @enumFromInt(f.value);
|
||||||
|
field_name_to_index_list[i] = .{ f.name, @intCast(i) };
|
||||||
|
index_to_enum[i] = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const field_name_to_index = std.StaticStringMap(i32).initComptime(&field_name_to_index_list);
|
||||||
|
var item: i32 = field_name_to_index.get(@tagName(current_item.*)).?;
|
||||||
|
|
||||||
const result = combo(label, .{
|
const result = combo(label, .{
|
||||||
.items_separated_by_zeros = item_names,
|
.items_separated_by_zeros = item_names,
|
||||||
.current_item = &item,
|
.current_item = &item,
|
||||||
});
|
});
|
||||||
|
|
||||||
current_item.* = @enumFromInt(item);
|
current_item.* = index_to_enum[@intCast(item)];
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1545,7 +1703,8 @@ pub const ComboFlags = packed struct(c_int) {
|
||||||
height_largest: bool = false,
|
height_largest: bool = false,
|
||||||
no_arrow_button: bool = false,
|
no_arrow_button: bool = false,
|
||||||
no_preview: bool = false,
|
no_preview: bool = false,
|
||||||
_padding: u25 = 0,
|
width_fit_preview: bool = false,
|
||||||
|
_padding: u24 = 0,
|
||||||
};
|
};
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
const BeginCombo = struct {
|
const BeginCombo = struct {
|
||||||
|
@ -2235,7 +2394,7 @@ pub const InputTextCallbackData = extern struct {
|
||||||
pub const InputTextCallback = *const fn (data: *InputTextCallbackData) i32;
|
pub const InputTextCallback = *const fn (data: *InputTextCallbackData) i32;
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
pub fn inputText(label: [:0]const u8, args: struct {
|
pub fn inputText(label: [:0]const u8, args: struct {
|
||||||
buf: []u8,
|
buf: [:0]u8,
|
||||||
flags: InputTextFlags = .{},
|
flags: InputTextFlags = .{},
|
||||||
callback: ?InputTextCallback = null,
|
callback: ?InputTextCallback = null,
|
||||||
user_data: ?*anyopaque = null,
|
user_data: ?*anyopaque = null,
|
||||||
|
@ -2243,7 +2402,7 @@ pub fn inputText(label: [:0]const u8, args: struct {
|
||||||
return zguiInputText(
|
return zguiInputText(
|
||||||
label,
|
label,
|
||||||
args.buf.ptr,
|
args.buf.ptr,
|
||||||
args.buf.len,
|
args.buf.len + 1, // + 1 for sentinel
|
||||||
args.flags,
|
args.flags,
|
||||||
if (args.callback) |cb| cb else null,
|
if (args.callback) |cb| cb else null,
|
||||||
args.user_data,
|
args.user_data,
|
||||||
|
@ -2259,7 +2418,7 @@ extern fn zguiInputText(
|
||||||
) bool;
|
) bool;
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
pub fn inputTextMultiline(label: [:0]const u8, args: struct {
|
pub fn inputTextMultiline(label: [:0]const u8, args: struct {
|
||||||
buf: []u8,
|
buf: [:0]u8,
|
||||||
w: f32 = 0.0,
|
w: f32 = 0.0,
|
||||||
h: f32 = 0.0,
|
h: f32 = 0.0,
|
||||||
flags: InputTextFlags = .{},
|
flags: InputTextFlags = .{},
|
||||||
|
@ -2269,7 +2428,7 @@ pub fn inputTextMultiline(label: [:0]const u8, args: struct {
|
||||||
return zguiInputTextMultiline(
|
return zguiInputTextMultiline(
|
||||||
label,
|
label,
|
||||||
args.buf.ptr,
|
args.buf.ptr,
|
||||||
args.buf.len,
|
args.buf.len + 1, // + 1 for sentinel
|
||||||
args.w,
|
args.w,
|
||||||
args.h,
|
args.h,
|
||||||
args.flags,
|
args.flags,
|
||||||
|
@ -2290,7 +2449,7 @@ extern fn zguiInputTextMultiline(
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
pub fn inputTextWithHint(label: [:0]const u8, args: struct {
|
pub fn inputTextWithHint(label: [:0]const u8, args: struct {
|
||||||
hint: [:0]const u8,
|
hint: [:0]const u8,
|
||||||
buf: []u8,
|
buf: [:0]u8,
|
||||||
flags: InputTextFlags = .{},
|
flags: InputTextFlags = .{},
|
||||||
callback: ?InputTextCallback = null,
|
callback: ?InputTextCallback = null,
|
||||||
user_data: ?*anyopaque = null,
|
user_data: ?*anyopaque = null,
|
||||||
|
@ -2299,7 +2458,7 @@ pub fn inputTextWithHint(label: [:0]const u8, args: struct {
|
||||||
label,
|
label,
|
||||||
args.hint,
|
args.hint,
|
||||||
args.buf.ptr,
|
args.buf.ptr,
|
||||||
args.buf.len,
|
args.buf.len + 1, // + 1 for sentinel
|
||||||
args.flags,
|
args.flags,
|
||||||
if (args.callback) |cb| cb else null,
|
if (args.callback) |cb| cb else null,
|
||||||
args.user_data,
|
args.user_data,
|
||||||
|
@ -2620,7 +2779,7 @@ extern fn zguiColorButton(
|
||||||
pub const TreeNodeFlags = packed struct(c_int) {
|
pub const TreeNodeFlags = packed struct(c_int) {
|
||||||
selected: bool = false,
|
selected: bool = false,
|
||||||
framed: bool = false,
|
framed: bool = false,
|
||||||
allow_item_overlap: bool = false,
|
allow_overlap: bool = false,
|
||||||
no_tree_push_on_open: bool = false,
|
no_tree_push_on_open: bool = false,
|
||||||
no_auto_open_on_log: bool = false,
|
no_auto_open_on_log: bool = false,
|
||||||
default_open: bool = false,
|
default_open: bool = false,
|
||||||
|
@ -2631,8 +2790,9 @@ pub const TreeNodeFlags = packed struct(c_int) {
|
||||||
frame_padding: bool = false,
|
frame_padding: bool = false,
|
||||||
span_avail_width: bool = false,
|
span_avail_width: bool = false,
|
||||||
span_full_width: bool = false,
|
span_full_width: bool = false,
|
||||||
|
span_all_columns: bool = false,
|
||||||
nav_left_jumps_back_here: bool = false,
|
nav_left_jumps_back_here: bool = false,
|
||||||
_padding: u18 = 0,
|
_padding: u17 = 0,
|
||||||
|
|
||||||
pub const collapsing_header = TreeNodeFlags{
|
pub const collapsing_header = TreeNodeFlags{
|
||||||
.framed = true,
|
.framed = true,
|
||||||
|
@ -2732,7 +2892,7 @@ pub const SelectableFlags = packed struct(c_int) {
|
||||||
span_all_columns: bool = false,
|
span_all_columns: bool = false,
|
||||||
allow_double_click: bool = false,
|
allow_double_click: bool = false,
|
||||||
disabled: bool = false,
|
disabled: bool = false,
|
||||||
allow_item_overlap: bool = false,
|
allow_overlap: bool = false,
|
||||||
_padding: u27 = 0,
|
_padding: u27 = 0,
|
||||||
};
|
};
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
@ -3245,11 +3405,13 @@ pub const PopupFlags = packed struct(c_int) {
|
||||||
_reserved0: bool = false,
|
_reserved0: bool = false,
|
||||||
_reserved1: bool = false,
|
_reserved1: bool = false,
|
||||||
|
|
||||||
|
no_reopen: bool = false,
|
||||||
|
_reserved2: bool = false,
|
||||||
no_open_over_existing_popup: bool = false,
|
no_open_over_existing_popup: bool = false,
|
||||||
no_open_over_items: bool = false,
|
no_open_over_items: bool = false,
|
||||||
any_popup_id: bool = false,
|
any_popup_id: bool = false,
|
||||||
any_popup_level: bool = false,
|
any_popup_level: bool = false,
|
||||||
_padding: u23 = 0,
|
_padding: u21 = 0,
|
||||||
|
|
||||||
pub const any_popup = PopupFlags{ .any_popup_id = true, .any_popup_level = true };
|
pub const any_popup = PopupFlags{ .any_popup_id = true, .any_popup_level = true };
|
||||||
};
|
};
|
||||||
|
@ -3297,7 +3459,8 @@ pub const TabItemFlags = packed struct(c_int) {
|
||||||
no_reorder: bool = false,
|
no_reorder: bool = false,
|
||||||
leading: bool = false,
|
leading: bool = false,
|
||||||
trailing: bool = false,
|
trailing: bool = false,
|
||||||
_padding: u24 = 0,
|
no_assumed_closure: bool = false,
|
||||||
|
_padding: u23 = 0,
|
||||||
};
|
};
|
||||||
pub fn beginTabBar(label: [:0]const u8, flags: TabBarFlags) bool {
|
pub fn beginTabBar(label: [:0]const u8, flags: TabBarFlags) bool {
|
||||||
return zguiBeginTabBar(label, flags);
|
return zguiBeginTabBar(label, flags);
|
||||||
|
@ -4380,6 +4543,14 @@ pub const DrawList = *opaque {
|
||||||
extern fn zguiDrawList_AddResetRenderStateCallback(draw_list: DrawList) void;
|
extern fn zguiDrawList_AddResetRenderStateCallback(draw_list: DrawList) void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn Vector(comptime T: type) type {
|
||||||
|
return extern struct {
|
||||||
|
len: c_int,
|
||||||
|
capacity: c_int,
|
||||||
|
items: [*]T,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
|
||||||
|
|
|
@ -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"
|
#include "implot.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if ZGUI_TE
|
||||||
|
#include "imgui_te_engine.h"
|
||||||
|
#include "imgui_te_context.h"
|
||||||
|
#include "imgui_te_ui.h"
|
||||||
|
#include "imgui_te_utils.h"
|
||||||
|
#include "imgui_te_exporters.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "imgui_internal.h"
|
||||||
|
|
||||||
#ifndef ZGUI_API
|
#ifndef ZGUI_API
|
||||||
#define ZGUI_API
|
#define ZGUI_API
|
||||||
#endif
|
#endif
|
||||||
|
@ -51,6 +61,10 @@ ZGUI_API void zguiSetNextWindowBgAlpha(float alpha) {
|
||||||
ImGui::SetNextWindowBgAlpha(alpha);
|
ImGui::SetNextWindowBgAlpha(alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiSetWindowFocus(const char* name) {
|
||||||
|
ImGui::SetWindowFocus(name);
|
||||||
|
}
|
||||||
|
|
||||||
ZGUI_API void zguiSetKeyboardFocusHere(int offset) {
|
ZGUI_API void zguiSetKeyboardFocusHere(int offset) {
|
||||||
ImGui::SetKeyboardFocusHere(offset);
|
ImGui::SetKeyboardFocusHere(offset);
|
||||||
}
|
}
|
||||||
|
@ -63,12 +77,12 @@ ZGUI_API void zguiEnd(void) {
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
ZGUI_API bool zguiBeginChild(const char* str_id, float w, float h, bool border, ImGuiWindowFlags flags) {
|
ZGUI_API bool zguiBeginChild(const char* str_id, float w, float h, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) {
|
||||||
return ImGui::BeginChild(str_id, { w, h }, border, flags);
|
return ImGui::BeginChild(str_id, { w, h }, child_flags, window_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
ZGUI_API bool zguiBeginChildId(ImGuiID id, float w, float h, bool border, ImGuiWindowFlags flags) {
|
ZGUI_API bool zguiBeginChildId(ImGuiID id, float w, float h, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) {
|
||||||
return ImGui::BeginChild(id, { w, h }, border, flags);
|
return ImGui::BeginChild(id, { w, h }, child_flags, window_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
ZGUI_API void zguiEndChild(void) {
|
ZGUI_API void zguiEndChild(void) {
|
||||||
|
@ -213,6 +227,12 @@ ZGUI_API void zguiGetItemRectMin(float rect[2]) {
|
||||||
rect[1] = r.y;
|
rect[1] = r.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiGetItemRectSize(float rect[2]) {
|
||||||
|
const ImVec2 r = ImGui::GetItemRectSize();
|
||||||
|
rect[0] = r.x;
|
||||||
|
rect[1] = r.y;
|
||||||
|
}
|
||||||
|
|
||||||
ZGUI_API void zguiGetCursorPos(float pos[2]) {
|
ZGUI_API void zguiGetCursorPos(float pos[2]) {
|
||||||
const ImVec2 p = ImGui::GetCursorPos();
|
const ImVec2 p = ImGui::GetCursorPos();
|
||||||
pos[0] = p.x;
|
pos[0] = p.x;
|
||||||
|
@ -2208,6 +2228,66 @@ ZGUI_API void zguiViewport_GetWorkSize(ImGuiViewport* viewport, float p[2]) {
|
||||||
p[1] = sz.y;
|
p[1] = sz.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Docking
|
||||||
|
//
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
ZGUI_API ImGuiID zguiDockSpace(const char* str_id, float size[2], ImGuiDockNodeFlags flags) {
|
||||||
|
return ImGui::DockSpace(ImGui::GetID(str_id), {size[0], size[1]}, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API ImGuiID zguiDockSpaceOverViewport(const ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags) {
|
||||||
|
return ImGui::DockSpaceOverViewport(viewport, dockspace_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// DockBuilder (Unstable internal imgui API, subject to change, use at own risk)
|
||||||
|
//
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
ZGUI_API void zguiDockBuilderDockWindow(const char* window_name, ImGuiID node_id) {
|
||||||
|
ImGui::DockBuilderDockWindow(window_name, node_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API ImGuiID zguiDockBuilderAddNode(ImGuiID node_id, ImGuiDockNodeFlags flags) {
|
||||||
|
return ImGui::DockBuilderAddNode(node_id, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiDockBuilderRemoveNode(ImGuiID node_id) {
|
||||||
|
ImGui::DockBuilderRemoveNode(node_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiDockBuilderSetNodePos(ImGuiID node_id, float pos[2]) {
|
||||||
|
ImGui::DockBuilderSetNodePos(node_id, {pos[0], pos[1]});
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiDockBuilderSetNodeSize(ImGuiID node_id, float size[2]) {
|
||||||
|
ImGui::DockBuilderSetNodeSize(node_id, {size[0], size[1]});
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API ImGuiID zguiDockBuilderSplitNode(
|
||||||
|
ImGuiID node_id,
|
||||||
|
ImGuiDir split_dir,
|
||||||
|
float size_ratio_for_node_at_dir,
|
||||||
|
ImGuiID* out_id_at_dir,
|
||||||
|
ImGuiID* out_id_at_opposite_dir
|
||||||
|
) {
|
||||||
|
return ImGui::DockBuilderSplitNode(
|
||||||
|
node_id,
|
||||||
|
split_dir,
|
||||||
|
size_ratio_for_node_at_dir,
|
||||||
|
out_id_at_dir,
|
||||||
|
out_id_at_opposite_dir
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiDockBuilderFinish(ImGuiID node_id) {
|
||||||
|
ImGui::DockBuilderFinish(node_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#if ZGUI_IMPLOT
|
#if ZGUI_IMPLOT
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
|
@ -2473,3 +2553,156 @@ ZGUI_API void zguiPlot_PlotText(
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
|
||||||
|
|
||||||
|
#if ZGUI_TE
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// ImGUI Test Engine
|
||||||
|
//
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
|
||||||
|
ZGUI_API void *zguiTe_CreateContext(void)
|
||||||
|
{
|
||||||
|
ImGuiTestEngine *e = ImGuiTestEngine_CreateContext();
|
||||||
|
|
||||||
|
ImGuiTestEngine_Start(e, ImGui::GetCurrentContext());
|
||||||
|
ImGuiTestEngine_InstallDefaultCrashHandler();
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_DestroyContext(ImGuiTestEngine *engine)
|
||||||
|
{
|
||||||
|
ImGuiTestEngine_DestroyContext(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_EngineSetRunSpeed(ImGuiTestEngine *engine, ImGuiTestRunSpeed speed)
|
||||||
|
{
|
||||||
|
ImGuiTestEngine_GetIO(engine).ConfigRunSpeed = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_EngineExportJunitResult(ImGuiTestEngine *engine, const char *filename)
|
||||||
|
{
|
||||||
|
ImGuiTestEngine_GetIO(engine).ExportResultsFilename = filename;
|
||||||
|
ImGuiTestEngine_GetIO(engine).ExportResultsFormat = ImGuiTestEngineExportFormat_JUnitXml;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_TryAbortEngine(ImGuiTestEngine *engine)
|
||||||
|
{
|
||||||
|
ImGuiTestEngine_TryAbortEngine(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_Stop(ImGuiTestEngine *engine)
|
||||||
|
{
|
||||||
|
ImGuiTestEngine_Stop(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_PostSwap(ImGuiTestEngine *engine)
|
||||||
|
{
|
||||||
|
ImGuiTestEngine_PostSwap(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API bool zguiTe_IsTestQueueEmpty(ImGuiTestEngine *engine)
|
||||||
|
{
|
||||||
|
return ImGuiTestEngine_IsTestQueueEmpty(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_GetResult(ImGuiTestEngine *engine, int *count_tested, int *count_success)
|
||||||
|
{
|
||||||
|
int ct = 0;
|
||||||
|
int cs = 0;
|
||||||
|
ImGuiTestEngine_GetResult(engine, ct, cs);
|
||||||
|
*count_tested = ct;
|
||||||
|
*count_success = cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_PrintResultSummary(ImGuiTestEngine *engine)
|
||||||
|
{
|
||||||
|
ImGuiTestEngine_PrintResultSummary(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_QueueTests(ImGuiTestEngine *engine, ImGuiTestGroup group, const char *filter_str, ImGuiTestRunFlags run_flags)
|
||||||
|
{
|
||||||
|
ImGuiTestEngine_QueueTests(engine, group, filter_str, run_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_ShowTestEngineWindows(ImGuiTestEngine *engine, bool *p_open)
|
||||||
|
{
|
||||||
|
ImGuiTestEngine_ShowTestEngineWindows(engine, p_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void *zguiTe_RegisterTest(ImGuiTestEngine *engine, const char *category, const char *name, const char *src_file, int src_line, ImGuiTestGuiFunc *gui_fce, ImGuiTestTestFunc *gui_test_fce)
|
||||||
|
{
|
||||||
|
auto t = ImGuiTestEngine_RegisterTest(engine, category, name, src_file, src_line);
|
||||||
|
t->GuiFunc = gui_fce;
|
||||||
|
t->TestFunc = gui_test_fce;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API bool zguiTe_Check(const char *file, const char *func, int line, ImGuiTestCheckFlags flags, bool result, const char *expr)
|
||||||
|
{
|
||||||
|
return ImGuiTestEngine_Check(file, func, line, flags, result, expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CONTEXT
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_ContextSetRef(ImGuiTestContext *ctx, const char *ref)
|
||||||
|
{
|
||||||
|
ctx->SetRef(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_ContextWindowFocus(ImGuiTestContext *ctx, const char *ref)
|
||||||
|
{
|
||||||
|
ctx->WindowFocus(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_ContextItemAction(ImGuiTestContext *ctx, ImGuiTestAction action, const char *ref, ImGuiTestOpFlags flags = 0, void *action_arg = NULL)
|
||||||
|
{
|
||||||
|
ctx->ItemAction(action, ref, flags, action_arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_ContextItemInputStrValue(ImGuiTestContext *ctx, const char *ref, const char *value)
|
||||||
|
{
|
||||||
|
ctx->ItemInputValue(ref, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_ContextItemInputIntValue(ImGuiTestContext *ctx, const char *ref, int value)
|
||||||
|
{
|
||||||
|
ctx->ItemInputValue(ref, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_ContextItemInputFloatValue(ImGuiTestContext *ctx, const char *ref, float value)
|
||||||
|
{
|
||||||
|
ctx->ItemInputValue(ref, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_ContextYield(ImGuiTestContext *ctx, int frame_count)
|
||||||
|
{
|
||||||
|
ctx->Yield(frame_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_ContextMenuAction(ImGuiTestContext *ctx, ImGuiTestAction action, const char *ref)
|
||||||
|
{
|
||||||
|
ctx->MenuAction(action, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_ContextDragAndDrop(ImGuiTestContext *ctx, const char *ref_src, const char *ref_dst, ImGuiMouseButton button)
|
||||||
|
{
|
||||||
|
ctx->ItemDragAndDrop(ref_src, ref_dst, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_ContextKeyDown(ImGuiTestContext *ctx, ImGuiKeyChord key_chord)
|
||||||
|
{
|
||||||
|
ctx->KeyDown(key_chord);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZGUI_API void zguiTe_ContextKeyUp(ImGuiTestContext *ctx, ImGuiKeyChord key_chord)
|
||||||
|
{
|
||||||
|
ctx->KeyUp(key_chord);
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue