diff --git a/buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy b/buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy index 18876dc3..bce85209 100644 --- a/buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy +++ b/buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy @@ -4,6 +4,7 @@ import com.badlogic.gdx.jnigen.* import groovy.transform.CompileStatic import org.gradle.api.DefaultTask import org.gradle.api.file.CopySpec +import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction @@ -53,6 +54,13 @@ class GenerateLibs extends DefaultTask { } spec.from(project.rootProject.file('imgui-binding/src/main/native')) spec.into(jniDir) + spec.duplicatesStrategy = DuplicatesStrategy.INCLUDE //Allows for duplicate imconfig.h, we ensure the correct one is copied below + } + + //Ensure we overwrite imconfig.h with our own + project.copy { CopySpec spec -> + spec.from(project.rootProject.file('imgui-binding/src/main/native/imconfig.h')) + spec.into(jniDir) } //Copy dirent for ImGuiFileDialog diff --git a/imgui-binding/src/main/java/imgui/ImGui.java b/imgui-binding/src/main/java/imgui/ImGui.java index a4460800..cdb3d3ba 100644 --- a/imgui-binding/src/main/java/imgui/ImGui.java +++ b/imgui-binding/src/main/java/imgui/ImGui.java @@ -1,5 +1,6 @@ package imgui; +import imgui.assertion.ImAssertCallback; import imgui.flag.ImGuiCond; import imgui.flag.ImGuiDragDropFlags; import imgui.flag.ImGuiInputTextFlags; @@ -76,6 +77,14 @@ public class ImGui { ImFontAtlas.nInit(); ImGuiPlatformIO.init(); nInitInputTextData(); + setAssertCallback(new ImAssertCallback() { + @Override + public void imAssertCallback(String assertion, int line, String file) { + System.err.println("Dear ImGui Assertion Failed: " + assertion); + System.err.println("Assertion Located At: " + file + ":" + line); + Thread.dumpStack(); + } + }); } private static String resolveFullLibName() { @@ -131,7 +140,9 @@ public static void init() { */ private static native void nInitJni(); /* + Jni::InitJvm(env); Jni::InitCommon(env); + Jni::InitAssertion(env); Jni::InitCallbacks(env); Jni::InitBindingStruct(env); */ @@ -190,6 +201,17 @@ public static void setCurrentContext(ImGuiContext ctx) { ImGui::SetCurrentContext((ImGuiContext*) ptr); */ + /** + * Set a custom assertion callback for ImGui assertions. + * Take note: Any custom assertion callback SHOULD NOT throw any exception. + * After any callback the application will be terminated, any attempt to bypass this behavior + * will result in a EXCEPTION_ACCESS_VIOLATION from within the native layer. + * @param callback The custom ImGui assertion callback + */ + public static native void setAssertCallback(ImAssertCallback callback); /* + Jni::SetAssertionCallback(callback); + */ + // Main /** diff --git a/imgui-binding/src/main/java/imgui/assertion/ImAssertCallback.java b/imgui-binding/src/main/java/imgui/assertion/ImAssertCallback.java new file mode 100644 index 00000000..6f831b40 --- /dev/null +++ b/imgui-binding/src/main/java/imgui/assertion/ImAssertCallback.java @@ -0,0 +1,45 @@ + +package imgui.assertion; + +/** + * Callback for native IM_ASSERT calls. + */ +public abstract class ImAssertCallback { + /** + * Called from native code to bring the callback into java side. + * Will force the application to terminate with exit code 1 after forwarding the callback, this is required, otherwise + * if execution is returned to the native layer, a EXCEPTION_ACCESS_VIOLATION will be thrown. + * If the callback throws an exception, it will be caught and printed along with a warning to prevent execution + * from returning to the native layer. + * + * @param assertion The assertion string + * @param line The line number of the assertion in the source file + * @param file The source file where the assertion occurred + */ + public void imAssert(final String assertion, final int line, final String file) { + try { + imAssertCallback(assertion, line, file); + } catch (Exception ex) { + System.err.println("WARNING: Exception thrown in Dear ImGui Assertion Callback!"); + System.err.println("Dear ImGui Assertion Failed: " + assertion); + System.err.println("Assertion Located At: " + file + ":" + line); + ex.printStackTrace(); + } + System.exit(1); + } + + /** + * The assertion callback from ImGui. + * Do not throw an exception within this callback, to prevent + * execution from returned to the native layer and cause a EXCEPTION_ACCESS_VIOLATION, + * the callback will catch any exceptions and print a warning. + *

+ * There is no way to catch the assertion and continue execution. + * You may however call System.exit(code) with your own exit code. + * + * @param assertion The assertion string + * @param line The line number of the assertion in the source file + * @param file The source file where the assertion occurred + */ + public abstract void imAssertCallback(String assertion, int line, String file); +} diff --git a/imgui-binding/src/main/native/_binding.h b/imgui-binding/src/main/native/_binding.h index 5b9197d8..c5c06a13 100644 --- a/imgui-binding/src/main/native/_binding.h +++ b/imgui-binding/src/main/native/_binding.h @@ -1,5 +1,7 @@ #pragma once +#include "jni_jvm.h" #include "jni_common.h" #include "jni_callbacks.h" #include "jni_binding_struct.h" +#include "jni_assertion.h" diff --git a/imgui-binding/src/main/native/_imnodes.h b/imgui-binding/src/main/native/_imnodes.h index 2a2db539..0e2be4b8 100644 --- a/imgui-binding/src/main/native/_imnodes.h +++ b/imgui-binding/src/main/native/_imnodes.h @@ -1,5 +1,4 @@ #pragma once -#include "_base.h" +#include "_common.h" #include -#include "_binding.h" diff --git a/imgui-binding/src/main/native/_implot.h b/imgui-binding/src/main/native/_implot.h index 5f7dfb77..3511474b 100644 --- a/imgui-binding/src/main/native/_implot.h +++ b/imgui-binding/src/main/native/_implot.h @@ -1,6 +1,5 @@ #pragma once -#include "_base.h" -#include "_binding.h" +#include "_common.h" #include #include "implot.h" diff --git a/imgui-binding/src/main/native/_nodeeditor.h b/imgui-binding/src/main/native/_nodeeditor.h index 990294b2..85f97013 100644 --- a/imgui-binding/src/main/native/_nodeeditor.h +++ b/imgui-binding/src/main/native/_nodeeditor.h @@ -1,9 +1,5 @@ #pragma once -#include -#include +#include "_common.h" #include #include -#include "jni_common.h" -#include "jni_callbacks.h" -#include "jni_binding_struct.h" diff --git a/imgui-binding/src/main/native/_texteditor.h b/imgui-binding/src/main/native/_texteditor.h index fcc05089..4c2c7ad7 100644 --- a/imgui-binding/src/main/native/_texteditor.h +++ b/imgui-binding/src/main/native/_texteditor.h @@ -8,6 +8,5 @@ #include #include #include -#include "_base.h" -#include "_binding.h" +#include "_common.h" #include "TextEditor.h" diff --git a/imgui-binding/src/main/native/imconfig.h b/imgui-binding/src/main/native/imconfig.h new file mode 100644 index 00000000..bbe606a7 --- /dev/null +++ b/imgui-binding/src/main/native/imconfig.h @@ -0,0 +1,125 @@ +//----------------------------------------------------------------------------- +// COMPILE-TIME OPTIONS FOR DEAR IMGUI +// 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. +//----------------------------------------------------------------------------- +// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it) +// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template. +//----------------------------------------------------------------------------- +// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp +// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures. +// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. +// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using. +//----------------------------------------------------------------------------- + +#pragma once +#include "jni_assertion.h" + +//---- Define assertion handler. Defaults to calling assert(). +// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. +//#define IM_ASSERT(_EXPR) MyAssert(_EXPR) +//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts +#define IM_ASSERT(_EXPR) do { if (!(_EXPR)) { if (Jni::ImAssertionSet()) { Jni::ImAssertToCallback(#_EXPR, __LINE__, __FILE__); } else { assert(_EXPR); } } } while (0) + +//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows +// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. +// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() +// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. +//#define IMGUI_API __declspec( dllexport ) +//#define IMGUI_API __declspec( dllimport ) + +//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. +//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS + +//---- Disable all of Dear ImGui or don't implement standard windows. +// It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp. +//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. +//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended. +//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger and other debug tools: ShowMetricsWindow() and ShowStackToolWindow() will be empty. + +//---- Don't implement some functions to reduce linkage requirements. +//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a) +//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW) +//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) +//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime). +//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). +//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) +//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. +//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) +//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. +//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). +//#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 +//#define IMGUI_INCLUDE_IMGUI_USER_H + +//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another) +//#define IMGUI_USE_BGRA_PACKED_COLOR + +//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...) +//#define IMGUI_USE_WCHAR32 + +//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version +// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files. +//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" +//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" +//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION +//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION + +//---- Use stb_printf's faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined) +// Requires 'stb_sprintf.h' to be available in the include path. 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. +// #define IMGUI_USE_STB_SPRINTF + +//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) +// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). +// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. +//#define IMGUI_ENABLE_FREETYPE + +//---- Use stb_truetype to build and rasterize the font atlas (default) +// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend. +//#define IMGUI_ENABLE_STB_TRUETYPE + +//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. +// This will be inlined as part of ImVec2 and ImVec4 class declarations. +/* +#define IM_VEC2_CLASS_EXTRA \ + ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \ + operator MyVec2() const { return MyVec2(x,y); } + +#define IM_VEC4_CLASS_EXTRA \ + ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \ + operator MyVec4() const { return MyVec4(x,y,z,w); } +*/ + +//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices. +// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices). +// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer. +// Read about ImGuiBackendFlags_RendererHasVtxOffset for details. +//#define ImDrawIdx unsigned int + +//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly) +//struct ImDrawList; +//struct ImDrawCmd; +//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); +//#define ImDrawCallback MyImDrawCallback + +//---- Debug Tools: Macro to break in Debugger +// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.) +//#define IM_DEBUG_BREAK IM_ASSERT(0) +//#define IM_DEBUG_BREAK __debugbreak() + +//---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(), +// (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.) +// This adds a small runtime cost which is why it is not enabled by default. +//#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX + +//---- Debug Tools: Enable slower asserts +//#define IMGUI_DEBUG_PARANOID + +//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. +/* +namespace ImGui +{ + void MyFunction(const char* name, const MyMatrix44& v); +} +*/ diff --git a/imgui-binding/src/main/native/jni_assertion.cpp b/imgui-binding/src/main/native/jni_assertion.cpp new file mode 100644 index 00000000..536d327f --- /dev/null +++ b/imgui-binding/src/main/native/jni_assertion.cpp @@ -0,0 +1,37 @@ +#include "jni_assertion.h" + +jmethodID jImAssertCallbackMID = NULL; +jobject jImAssertCallbackInstance = NULL; + +namespace Jni +{ + void InitAssertion(JNIEnv* env) { + jclass jImAssertCallback = env->FindClass("imgui/assertion/ImAssertCallback"); + jImAssertCallbackMID = env->GetMethodID(jImAssertCallback, "imAssert", "(Ljava/lang/String;ILjava/lang/String;)V"); + } + + void SetAssertionCallback(jobject func) { + JNIEnv* env = Jni::GetEnv(); + if (jImAssertCallbackInstance != NULL) { + env->DeleteGlobalRef(jImAssertCallbackInstance); + } + if (func != NULL) { + jImAssertCallbackInstance = env->NewGlobalRef(func); + } else { + jImAssertCallbackInstance = NULL; + } + } + + void ImAssertToCallback(const char* assertion, int line, const char* file) { + JNIEnv* env = Jni::GetEnv(); + assert(jImAssertCallbackMID != NULL); + assert(assertion != NULL); + if (jImAssertCallbackInstance != NULL) { + env->CallVoidMethod(jImAssertCallbackInstance, jImAssertCallbackMID, env->NewStringUTF(assertion), line, env->NewStringUTF(file)); + } + } + + bool ImAssertionSet() { + return jImAssertCallbackInstance != NULL; + } +} diff --git a/imgui-binding/src/main/native/jni_assertion.h b/imgui-binding/src/main/native/jni_assertion.h new file mode 100644 index 00000000..8c5d76d9 --- /dev/null +++ b/imgui-binding/src/main/native/jni_assertion.h @@ -0,0 +1,15 @@ +#pragma once + +#include "jni_jvm.h" +#include + +namespace Jni +{ + void InitAssertion(JNIEnv* env); + + void SetAssertionCallback(jobject func); + + void ImAssertToCallback(const char* assertion, int line, const char* file); + + bool ImAssertionSet(); +} diff --git a/imgui-binding/src/main/native/jni_common.cpp b/imgui-binding/src/main/native/jni_common.cpp index 52a817a0..475999af 100644 --- a/imgui-binding/src/main/native/jni_common.cpp +++ b/imgui-binding/src/main/native/jni_common.cpp @@ -8,13 +8,9 @@ jfieldID imVec4YID; jfieldID imVec4ZID; jfieldID imVec4WID; -static JavaVM* jvm; - namespace Jni { void InitCommon(JNIEnv* env) { - env->GetJavaVM(&jvm); - jclass jImVec2Class = env->FindClass("imgui/ImVec2"); imVec2XID = env->GetFieldID(jImVec2Class, "x", "F"); imVec2YID = env->GetFieldID(jImVec2Class, "y", "F"); @@ -26,13 +22,6 @@ namespace Jni imVec4WID = env->GetFieldID(jImVec4Class, "w", "F"); } - JNIEnv* GetEnv() { - JNIEnv* env; - jint res = jvm->GetEnv((void**)(&env), JNI_VERSION_1_8); - assert(res == JNI_OK); - return env; - } - void ImVec2Cpy(JNIEnv* env, ImVec2* src, jobject dst) { env->SetFloatField(dst, imVec2XID, src->x); env->SetFloatField(dst, imVec2YID, src->y); diff --git a/imgui-binding/src/main/native/jni_common.h b/imgui-binding/src/main/native/jni_common.h index 2eee0068..70319249 100644 --- a/imgui-binding/src/main/native/jni_common.h +++ b/imgui-binding/src/main/native/jni_common.h @@ -7,8 +7,6 @@ namespace Jni { void InitCommon(JNIEnv* env); - JNIEnv* GetEnv(); - void ImVec2Cpy(JNIEnv* env, ImVec2* src, jobject dst); void ImVec2Cpy(JNIEnv* env, ImVec2 src, jobject dst); diff --git a/imgui-binding/src/main/native/jni_jvm.cpp b/imgui-binding/src/main/native/jni_jvm.cpp new file mode 100644 index 00000000..af3fb208 --- /dev/null +++ b/imgui-binding/src/main/native/jni_jvm.cpp @@ -0,0 +1,17 @@ +#include "jni_jvm.h" + +static JavaVM* jvm; + +namespace Jni +{ + void InitJvm(JNIEnv* env) { + env->GetJavaVM(&jvm); + } + + JNIEnv* GetEnv() { + JNIEnv* env; + jint res = jvm->GetEnv((void**)(&env), JNI_VERSION_1_8); + assert(res == JNI_OK); + return env; + } +} diff --git a/imgui-binding/src/main/native/jni_jvm.h b/imgui-binding/src/main/native/jni_jvm.h new file mode 100644 index 00000000..32cfeb4b --- /dev/null +++ b/imgui-binding/src/main/native/jni_jvm.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +namespace Jni +{ + void InitJvm(JNIEnv* env); + + JNIEnv* GetEnv(); +}