diff --git a/bin/imgui-java.dll b/bin/imgui-java.dll index dabaf01..e0e74ec 100755 Binary files a/bin/imgui-java.dll and b/bin/imgui-java.dll differ diff --git a/bin/imgui-java64.dll b/bin/imgui-java64.dll index 43a7b90..9d4c599 100755 Binary files a/bin/imgui-java64.dll and b/bin/imgui-java64.dll differ diff --git a/bin/libimgui-java.so b/bin/libimgui-java.so index 684cc11..add29f4 100755 Binary files a/bin/libimgui-java.so and b/bin/libimgui-java.so differ diff --git a/bin/libimgui-java64.so b/bin/libimgui-java64.so index d65fa47..efdfecd 100755 Binary files a/bin/libimgui-java64.so and b/bin/libimgui-java64.so differ diff --git a/buildSrc/scripts/build-natives-win64.sh b/buildSrc/scripts/build-natives-win64.sh new file mode 100644 index 0000000..08dba67 --- /dev/null +++ b/buildSrc/scripts/build-natives-win64.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +BASEDIR=$(dirname "$0") +cd $BASEDIR/../../imgui-binding || exit + +rm -frd /tmp/imgui +../gradlew clean generateLibs -Denvs=win64 diff --git a/imgui-binding/src/main/java/imgui/ImGui.java b/imgui-binding/src/main/java/imgui/ImGui.java index 7972532..d6f8193 100644 --- a/imgui-binding/src/main/java/imgui/ImGui.java +++ b/imgui-binding/src/main/java/imgui/ImGui.java @@ -5439,7 +5439,7 @@ public final class ImGui { } private static native long nGetPlatformIO(); /* - return (intptr_t)ImGui::GetMainViewport(); + return (intptr_t)&ImGui::GetPlatformIO(); */ /** @@ -5491,4 +5491,16 @@ public final class ImGui { private static native long nFindViewportByID(int imGuiID); /* return (intptr_t)ImGui::FindViewportByID(imGuiID); */ + + /** + * This is a helper for back-ends. The type platform_handle is decided by the back-end (e.g. HWND, MyWindow*, GLFWwindow* etc.) + */ + public static ImGuiViewport findViewportByPlatformHandle(long platformHandle) { + FIND_VIEWPORT.ptr = nFindViewportByPlatformHandle(platformHandle); + return FIND_VIEWPORT; + } + + private static native long nFindViewportByPlatformHandle(long platformHandle); /* + return (intptr_t)ImGui::FindViewportByPlatformHandle((void*)platformHandle); + */ } diff --git a/imgui-binding/src/main/java/imgui/ImGuiIO.java b/imgui-binding/src/main/java/imgui/ImGuiIO.java index b298cd7..08c8cb8 100644 --- a/imgui-binding/src/main/java/imgui/ImGuiIO.java +++ b/imgui-binding/src/main/java/imgui/ImGuiIO.java @@ -390,7 +390,7 @@ public final class ImGuiIO { * Optional: Platform back-end name (informational only! will be displayed in About Window) + User data for back-end/wrappers to store their own stuff. */ public native String getBackendPlatformName(); /* - return env -> NewStringUTF(ImGui::GetIO().BackendPlatformName); + return env->NewStringUTF(ImGui::GetIO().BackendPlatformName); */ /** @@ -404,7 +404,7 @@ public final class ImGuiIO { * Optional: Renderer back-end name (informational only! will be displayed in About Window) + User data for back-end/wrappers to store their own stuff. */ public native String getBackendRendererName(); /* - return env -> NewStringUTF(ImGui::GetIO().BackendRendererName); + return env->NewStringUTF(ImGui::GetIO().BackendRendererName); */ /** @@ -699,6 +699,24 @@ public final class ImGuiIO { ImGui::GetIO().MouseWheelH = mouseDeltaX; */ + /** + * (Optional) When using multiple viewports: viewport the OS mouse cursor is hovering _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag, + * and _REGARDLESS_ of whether another viewport is focused. Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. + * If you don't imgui will infer the value using the rectangles and last focused time of the viewports it knows about (ignoring other OS windows). + */ + public native float getMouseHoveredViewport(); /* + return ImGui::GetIO().MouseHoveredViewport; + */ + + /** + * (Optional) When using multiple viewports: viewport the OS mouse cursor is hovering _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag, + * and _REGARDLESS_ of whether another viewport is focused. Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. + * If you don't imgui will infer the value using the rectangles and last focused time of the viewports it knows about (ignoring other OS windows). + */ + public native void setMouseHoveredViewport(int imGuiId); /* + ImGui::GetIO().MouseHoveredViewport = imGuiId; + */ + /** * Keyboard modifier pressed: Control */ diff --git a/imgui-binding/src/main/java/imgui/ImGuiPlatformIO.java b/imgui-binding/src/main/java/imgui/ImGuiPlatformIO.java index 16639fc..23a85db 100644 --- a/imgui-binding/src/main/java/imgui/ImGuiPlatformIO.java +++ b/imgui-binding/src/main/java/imgui/ImGuiPlatformIO.java @@ -70,6 +70,7 @@ import imgui.callback.ImPlatformFuncViewportSuppImVec2; * Platform functions are typically called before their Renderer counterpart, apart from Destroy which are called the other way. */ public final class ImGuiPlatformIO extends ImGuiStruct { + private static final ImGuiViewport MAIN_VIEWPORT = new ImGuiViewport(0); private static final ImGuiViewport TMP_VIEWPORT = new ImGuiViewport(0); private static final ImVec2 TMP_IM_VEC2 = new ImVec2(); @@ -307,7 +308,7 @@ public final class ImGuiPlatformIO extends ImGuiStruct { IMGUI_PLATFORM_IO->Platform_SetWindowTitle = PlatformStubSetWindowTitle; */ - /*JNI + /*JNI jobject platformCallbackSetWindowAlpha = NULL; void PlatformStubSetWindowAlpha(ImGuiViewport* vp, float alpha) { if (platformCallbackSetWindowAlpha != NULL) { @@ -485,4 +486,62 @@ public final class ImGuiPlatformIO extends ImGuiStruct { public native void setRendererSwapBuffers(ImPlatformFuncViewport func); /* IM_RENDERER_FUNC_VIEWPORT_METHOD_TMPL(SwapBuffers) */ + + // (Optional) Monitor list + // - Updated by: app/back-end. Update every frame to dynamically support changing monitor or DPI configuration. + // - Used by: dear imgui to query DPI info, clamp popups/tooltips within same monitor and not have them straddle monitors. + + //------------------------------------------------------------------ + // Output - List of viewports to render into platform windows + //------------------------------------------------------------------ + + public native void resizeMonitors(int size); /* + IMGUI_PLATFORM_IO->Monitors.resize(0); + */ + + public native int getMonitorsSize(); /* + return IMGUI_PLATFORM_IO->Monitors.Size; + */ + + public native void pushMonitors(float mainPosX, float mainPosY, float mainSizeX, float mainSizeY, float workPosX, float workPosY, float workSizeX, float workSizeY, float dpiScale); /* + ImGuiPlatformMonitor monitor; + + monitor.MainPos = ImVec2(mainPosX, mainPosY); + monitor.MainSize = ImVec2(mainSizeX, mainSizeY); + + monitor.WorkPos = ImVec2(workPosX, workPosY); + monitor.WorkSize = ImVec2(workSizeX, workSizeY); + + monitor.DpiScale = dpiScale; + + IMGUI_PLATFORM_IO->Monitors.push_back(monitor); + */ + + // Viewports list (the list is updated by calling ImGui::EndFrame or ImGui::Render) + // (in the future we will attempt to organize this feature to remove the need for a "main viewport") + + /** + * Guaranteed to be == Viewports[0] + */ + public ImGuiViewport getMainViewport() { + MAIN_VIEWPORT.ptr = nGetMainViewport(); + return MAIN_VIEWPORT; + } + + private native long nGetMainViewport(); /* + return (intptr_t)IMGUI_PLATFORM_IO->MainViewport; + */ + + public native int getViewportsSize(); /* + return IMGUI_PLATFORM_IO->Viewports.Size; + */ + + public ImGuiViewport getViewports(final int idx) { + TMP_VIEWPORT.ptr = nGetViewports(idx); + return TMP_VIEWPORT; + } + + private native long nGetViewports(int idx); /* + return (intptr_t)IMGUI_PLATFORM_IO->Viewports[idx]; + */ } diff --git a/imgui-binding/src/main/java/imgui/ImGuiViewport.java b/imgui-binding/src/main/java/imgui/ImGuiViewport.java index b1373a8..952adfd 100644 --- a/imgui-binding/src/main/java/imgui/ImGuiViewport.java +++ b/imgui-binding/src/main/java/imgui/ImGuiViewport.java @@ -212,7 +212,67 @@ public final class ImGuiViewport extends ImGuiStruct { // by the same system and you may not need to use all the UserData/Handle fields. // The library never uses those fields, they are merely storage to facilitate back-end implementation. - // TODO: PlatformUserData, PlatformHandle, PlatformHandleRaw + /** + * void* to hold custom data structure for the renderer (e.g. swap chain, framebuffers etc.). generally set by your Renderer_CreateWindow function. + */ + public native void setRendererUserData(Object data); /* + if (IMGUI_VIEWPORT->RendererUserData != NULL) { + env->DeleteGlobalRef((jobject)IMGUI_VIEWPORT->RendererUserData); + } + IMGUI_VIEWPORT->RendererUserData = (data == NULL ? NULL : (void*)env->NewGlobalRef(data)); + */ + + /** + * void* to hold custom data structure for the renderer (e.g. swap chain, framebuffers etc.). generally set by your Renderer_CreateWindow function. + */ + public native Object getRendererUserData(); /* + return (jobject)IMGUI_VIEWPORT->RendererUserData; + */ + + /** + * void* to hold custom data structure for the OS / platform (e.g. windowing info, render context). generally set by your Platform_CreateWindow function. + */ + public native void setPlatformUserData(Object data); /* + if (IMGUI_VIEWPORT->PlatformUserData != NULL) { + env->DeleteGlobalRef((jobject)IMGUI_VIEWPORT->PlatformUserData); + } + IMGUI_VIEWPORT->PlatformUserData = (data == NULL ? NULL : (void*)env->NewGlobalRef(data)); + */ + + /** + * void* to hold custom data structure for the OS / platform (e.g. windowing info, render context). generally set by your Platform_CreateWindow function. + */ + public native Object getPlatformUserData(); /* + return (jobject)IMGUI_VIEWPORT->PlatformUserData; + */ + + /** + * void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GLFWWindow*, SDL_Window*) + */ + public native void setPlatformHandle(long data); /* + IMGUI_VIEWPORT->PlatformHandle = (void*)data; + */ + + /** + * void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GLFWWindow*, SDL_Window*) + */ + public native long getPlatformHandle(); /* + return (intptr_t)IMGUI_VIEWPORT->PlatformHandle; + */ + + /** + * void* to hold lower-level, platform-native window handle (e.g. the HWND) when using an abstraction layer like GLFW or SDL (where PlatformHandle would be a SDL_Window*) + */ + public native void setPlatformHandleRaw(long data); /* + IMGUI_VIEWPORT->PlatformHandleRaw = (void*)data; + */ + + /** + * void* to hold lower-level, platform-native window handle (e.g. the HWND) when using an abstraction layer like GLFW or SDL (where PlatformHandle would be a SDL_Window*) + */ + public native long getPlatformHandleRaw(); /* + return (intptr_t)IMGUI_VIEWPORT->PlatformHandleRaw; + */ /** * Platform window requested move (e.g. window was moved by the OS / host window manager, authoritative position will be OS window position). diff --git a/imgui-binding/src/main/java/imgui/callback/ImPlatformFuncViewportFloat.java b/imgui-binding/src/main/java/imgui/callback/ImPlatformFuncViewportFloat.java index 1308447..d39f75c 100644 --- a/imgui-binding/src/main/java/imgui/callback/ImPlatformFuncViewportFloat.java +++ b/imgui-binding/src/main/java/imgui/callback/ImPlatformFuncViewportFloat.java @@ -6,5 +6,5 @@ import imgui.ImGuiViewport; * Callback to represent ImGuiPlatformIO function with args: (ImGuiViewport*, String) */ public abstract class ImPlatformFuncViewportFloat { - public abstract void accept(ImGuiViewport vp, Float f); + public abstract void accept(ImGuiViewport vp, float f); } diff --git a/imgui-binding/src/main/java/imgui/flag/ImGuiBackendFlags.java b/imgui-binding/src/main/java/imgui/flag/ImGuiBackendFlags.java index 9e440fd..5c45c2b 100644 --- a/imgui-binding/src/main/java/imgui/flag/ImGuiBackendFlags.java +++ b/imgui-binding/src/main/java/imgui/flag/ImGuiBackendFlags.java @@ -24,4 +24,21 @@ public final class ImGuiBackendFlags { * Back-end Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bit indices. */ public static final int RendererHasVtxOffset = 1 << 3; + + // [BETA] Viewports + + /** + * Back-end Platform supports multiple viewports. + */ + public static final int PlatformHasViewports = 1 << 10; + /** + * Back-end Platform supports setting io.MouseHoveredViewport to the viewport directly under the mouse _IGNORING_ viewports + * with the ImGuiViewportFlags_NoInputs flag and _REGARDLESS_ of whether another viewport is focused and may be capturing the mouse. + * This information is _NOT EASY_ to provide correctly with most high-level engines! Don't set this without studying how the examples/ back-end handle it! + */ + public static final int HasMouseHoveredViewport = 1 << 11; + /** + * Back-end Renderer supports multiple viewports. + */ + public static final int RendererHasViewports = 1 << 12; } diff --git a/imgui-binding/src/main/java/imgui/flag/ImGuiConfigFlags.java b/imgui-binding/src/main/java/imgui/flag/ImGuiConfigFlags.java index f092b7b..05737e7 100644 --- a/imgui-binding/src/main/java/imgui/flag/ImGuiConfigFlags.java +++ b/imgui-binding/src/main/java/imgui/flag/ImGuiConfigFlags.java @@ -43,6 +43,24 @@ public final class ImGuiConfigFlags { */ public static final int DockingEnable = 1 << 6; + // [BETA] Viewports + // When using viewports it is recommended that your default value for ImGuiCol_WindowBg is opaque (Alpha=1.0) so transition to a viewport won't be noticeable. + + /** + * Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) + */ + public static final int ViewportsEnable = 1 << 10; + /** + * [BETA: Don't use] FIXME-DPI: Reposition and resize imgui windows when the DpiScale of a viewport changed (mostly useful for the main viewport hosting other window). + * Note that resizing the main window itself is up to your application. + */ + public static final int DpiEnableScaleViewports = 1 << 14; + /** + * [BETA: Don't use] FIXME-DPI: Request bitmap-scaled fonts to match DpiScale. This is a very low-quality workaround. + * The correct way to handle DPI is _currently_ to replace the atlas and/or fonts in the Platform_OnChangedViewport callback, but this is all early work in progress. + */ + public static final int DpiEnableScaleFonts = 1 << 15; + // User storage (to allow your back-end/engine to communicate to code that may be shared between multiple projects. Those flags are not used by core Dear ImGui) /** diff --git a/imgui-binding/src/main/native/jni_callbacks.cpp b/imgui-binding/src/main/native/jni_callbacks.cpp index 0b9016c..3e6445f 100644 --- a/imgui-binding/src/main/native/jni_callbacks.cpp +++ b/imgui-binding/src/main/native/jni_callbacks.cpp @@ -72,7 +72,7 @@ namespace Jni } void CallImPlatformFuncViewportString(JNIEnv* env, jobject func, jobject vp, const char* str) { - env->CallVoidMethod(func, jImPlatformFuncViewportStringAcceptMID, env->NewStringUTF(str)); + env->CallVoidMethod(func, jImPlatformFuncViewportStringAcceptMID, vp, env->NewStringUTF(str)); } void CallImPlatformFuncViewportSuppImVec2(JNIEnv* env, jobject func, jobject vp, jobject imVec2) { diff --git a/imgui-lwjgl3/src/main/java/imgui/gl3/ImGuiImplGl3.java b/imgui-lwjgl3/src/main/java/imgui/gl3/ImGuiImplGl3.java index 1e8a0f2..3f3a641 100644 --- a/imgui-lwjgl3/src/main/java/imgui/gl3/ImGuiImplGl3.java +++ b/imgui-lwjgl3/src/main/java/imgui/gl3/ImGuiImplGl3.java @@ -4,9 +4,13 @@ import imgui.ImDrawData; import imgui.ImFontAtlas; import imgui.ImGui; import imgui.ImGuiIO; -import imgui.type.ImInt; +import imgui.ImGuiViewport; import imgui.ImVec2; import imgui.ImVec4; +import imgui.callback.ImPlatformFuncViewport; +import imgui.flag.ImGuiConfigFlags; +import imgui.flag.ImGuiViewportFlags; +import imgui.type.ImInt; import imgui.flag.ImGuiBackendFlags; import java.nio.ByteBuffer; @@ -31,17 +35,17 @@ public final class ImGuiImplGl3 { // OpenGL Data private int glVersion = 0; private String glslVersion = ""; - private int gVboHandle = 0; - private int gElementsHandle = 0; + private int gFontTexture = 0; private int gShaderHandle = 0; - private int gFragHandle = 0; private int gVertHandle = 0; + private int gFragHandle = 0; private int gAttribLocationTex = 0; private int gAttribLocationProjMtx = 0; private int gAttribLocationVtxPos = 0; private int gAttribLocationVtxUV = 0; private int gAttribLocationVtxColor = 0; - private int gFontTexture = 0; + private int gVboHandle = 0; + private int gElementsHandle = 0; private int gVertexArrayObjectHandle = 0; // Used to store tmp renderer data @@ -72,7 +76,7 @@ public final class ImGuiImplGl3 { /** * Method to do an initialization of the {@link ImGuiImplGl3} state. - * It SHOULD be called before calling of the {@link ImGuiImplGl3#render(ImDrawData)} method. + * It SHOULD be called before calling of the {@link ImGuiImplGl3#renderDrawData(ImDrawData)} method. *

* Unlike in the {@link #init(String)} method, here the glslVersion argument is omitted. * Thus a "#version 130" string will be used instead. @@ -83,7 +87,7 @@ public final class ImGuiImplGl3 { /** * Method to do an initialization of the {@link ImGuiImplGl3} state. - * It SHOULD be called before calling of the {@link ImGuiImplGl3#render(ImDrawData)} method. + * It SHOULD be called before calling of the {@link ImGuiImplGl3#renderDrawData(ImDrawData)} method. *

* Method takes an argument, which should be a valid GLSL string with the version to use. *

@@ -117,12 +121,16 @@ public final class ImGuiImplGl3 {
         }
 
         createDeviceObjects();
+
+        if ((ImGui.getIO().getConfigFlags() & ImGuiConfigFlags.ViewportsEnable) != 0) {
+            initPlatformInterface();
+        }
     }
 
     /**
      * Method to render {@link ImDrawData} into current OpenGL context.
      */
-    public void render(final ImDrawData drawData) {
+    public void renderDrawData(final ImDrawData drawData) {
         if (drawData.getCmdListsCount() <= 0) {
             return;
         }
@@ -142,7 +150,7 @@ public final class ImGuiImplGl3 {
         drawData.getDisplayPos(displayPos);
 
         backupGlState();
-        bind();
+        bind(fbWidth, fbHeight);
 
         // Render command lists
         for (int cmdListIdx = 0; cmdListIdx < drawData.getCmdListsCount(); cmdListIdx++) {
@@ -194,6 +202,7 @@ public final class ImGuiImplGl3 {
         glDetachShader(gShaderHandle, gFragHandle);
         glDeleteProgram(gShaderHandle);
         glDeleteTextures(gFontTexture);
+        shutdownPlatformInterface();
     }
 
     /**
@@ -232,8 +241,11 @@ public final class ImGuiImplGl3 {
 
         // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
         if (glVersion >= 320) {
-            io.setBackendFlags(io.getBackendFlags() | ImGuiBackendFlags.RendererHasVtxOffset);
+            io.addBackendFlags(ImGuiBackendFlags.RendererHasVtxOffset);
         }
+
+        // We can create multi-viewports on the Renderer side (optional)
+        io.addBackendFlags(ImGuiBackendFlags.RendererHasViewports);
     }
 
     private void createDeviceObjects() {
@@ -347,7 +359,7 @@ public final class ImGuiImplGl3 {
     }
 
     // Setup desired GL state
-    private void bind() {
+    private void bind(final int fbWidth, final int fbHeight) {
         // Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
         // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
         gVertexArrayObjectHandle = glGenVertexArrays();
@@ -363,7 +375,7 @@ public final class ImGuiImplGl3 {
         // Setup viewport, orthographic projection matrix
         // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right).
         // DisplayPos is (0,0) for single viewport apps.
-        glViewport(0, 0, (int) (displaySize.x * framebufferScale.x), (int) (displaySize.y * framebufferScale.y));
+        glViewport(0, 0, fbWidth, fbHeight);
         final float left = displayPos.x;
         final float right = displayPos.x + displaySize.x;
         final float top = displayPos.y;
@@ -400,6 +412,29 @@ public final class ImGuiImplGl3 {
         glDeleteVertexArrays(gVertexArrayObjectHandle);
     }
 
+    //--------------------------------------------------------------------------------------------------------
+    // MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
+    // This is an _advanced_ and _optional_ feature, allowing the back-end 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..
+    //--------------------------------------------------------------------------------------------------------
+
+    private void initPlatformInterface() {
+        ImGui.getPlatformIO().setRendererRenderWindow(new ImPlatformFuncViewport() {
+            @Override
+            public void accept(final ImGuiViewport vp) {
+                if ((vp.getFlags() & ImGuiViewportFlags.NoRendererClear) == 0) {
+                    glClearColor(0, 0, 0, 0);
+                    glClear(GL_COLOR_BUFFER_BIT);
+                }
+                renderDrawData(vp.getDrawData());
+            }
+        });
+    }
+
+    private void shutdownPlatformInterface() {
+        ImGui.destroyPlatformWindows();
+    }
+
     private int createAndCompileShader(final int type, final CharSequence source) {
         final int id = glCreateShader(type);
 
diff --git a/imgui-lwjgl3/src/main/java/imgui/glfw/ImGuiImplGlfw.java b/imgui-lwjgl3/src/main/java/imgui/glfw/ImGuiImplGlfw.java
index be588c3..632420d 100644
--- a/imgui-lwjgl3/src/main/java/imgui/glfw/ImGuiImplGlfw.java
+++ b/imgui-lwjgl3/src/main/java/imgui/glfw/ImGuiImplGlfw.java
@@ -2,7 +2,15 @@ package imgui.glfw;
 
 import imgui.ImGui;
 import imgui.ImGuiIO;
+import imgui.ImGuiPlatformIO;
+import imgui.ImGuiViewport;
 import imgui.ImVec2;
+import imgui.callback.ImPlatformFuncViewport;
+import imgui.callback.ImPlatformFuncViewportFloat;
+import imgui.callback.ImPlatformFuncViewportImVec2;
+import imgui.callback.ImPlatformFuncViewportString;
+import imgui.callback.ImPlatformFuncViewportSuppBoolean;
+import imgui.callback.ImPlatformFuncViewportSuppImVec2;
 import imgui.callback.ImStrConsumer;
 import imgui.callback.ImStrSupplier;
 import imgui.flag.ImGuiBackendFlags;
@@ -11,79 +19,21 @@ import imgui.flag.ImGuiKey;
 import imgui.flag.ImGuiMouseButton;
 import imgui.flag.ImGuiMouseCursor;
 import imgui.flag.ImGuiNavInput;
+import imgui.flag.ImGuiViewportFlags;
+import org.lwjgl.PointerBuffer;
 import org.lwjgl.glfw.GLFWCharCallback;
 import org.lwjgl.glfw.GLFWErrorCallback;
 import org.lwjgl.glfw.GLFWKeyCallback;
+import org.lwjgl.glfw.GLFWMonitorCallback;
 import org.lwjgl.glfw.GLFWMouseButtonCallback;
 import org.lwjgl.glfw.GLFWScrollCallback;
+import org.lwjgl.glfw.GLFWVidMode;
 
 import java.nio.ByteBuffer;
 import java.nio.FloatBuffer;
 
-import static org.lwjgl.glfw.GLFW.GLFW_ARROW_CURSOR;
-import static org.lwjgl.glfw.GLFW.GLFW_CURSOR;
-import static org.lwjgl.glfw.GLFW.GLFW_CURSOR_DISABLED;
-import static org.lwjgl.glfw.GLFW.GLFW_CURSOR_HIDDEN;
-import static org.lwjgl.glfw.GLFW.GLFW_CURSOR_NORMAL;
-import static org.lwjgl.glfw.GLFW.GLFW_FOCUSED;
-import static org.lwjgl.glfw.GLFW.GLFW_HAND_CURSOR;
-import static org.lwjgl.glfw.GLFW.GLFW_HRESIZE_CURSOR;
-import static org.lwjgl.glfw.GLFW.GLFW_IBEAM_CURSOR;
-import static org.lwjgl.glfw.GLFW.GLFW_JOYSTICK_1;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_A;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_BACKSPACE;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_C;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_DELETE;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_DOWN;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_END;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_ENTER;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_ESCAPE;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_HOME;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_INSERT;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_ENTER;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_ALT;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_CONTROL;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_SHIFT;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_SUPER;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_PAGE_DOWN;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_PAGE_UP;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_ALT;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_CONTROL;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_SHIFT;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_SUPER;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_SPACE;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_TAB;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_UP;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_V;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_X;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_Y;
-import static org.lwjgl.glfw.GLFW.GLFW_KEY_Z;
-import static org.lwjgl.glfw.GLFW.GLFW_PRESS;
-import static org.lwjgl.glfw.GLFW.GLFW_RELEASE;
-import static org.lwjgl.glfw.GLFW.GLFW_VRESIZE_CURSOR;
-import static org.lwjgl.glfw.GLFW.glfwCreateStandardCursor;
-import static org.lwjgl.glfw.GLFW.glfwDestroyCursor;
-import static org.lwjgl.glfw.GLFW.glfwGetClipboardString;
-import static org.lwjgl.glfw.GLFW.glfwGetCursorPos;
-import static org.lwjgl.glfw.GLFW.glfwGetFramebufferSize;
-import static org.lwjgl.glfw.GLFW.glfwGetInputMode;
-import static org.lwjgl.glfw.GLFW.glfwGetJoystickAxes;
-import static org.lwjgl.glfw.GLFW.glfwGetJoystickButtons;
-import static org.lwjgl.glfw.GLFW.glfwGetMouseButton;
-import static org.lwjgl.glfw.GLFW.glfwGetTime;
-import static org.lwjgl.glfw.GLFW.glfwGetWindowAttrib;
-import static org.lwjgl.glfw.GLFW.glfwGetWindowSize;
-import static org.lwjgl.glfw.GLFW.glfwSetCharCallback;
-import static org.lwjgl.glfw.GLFW.glfwSetClipboardString;
-import static org.lwjgl.glfw.GLFW.glfwSetCursor;
-import static org.lwjgl.glfw.GLFW.glfwSetCursorPos;
-import static org.lwjgl.glfw.GLFW.glfwSetErrorCallback;
-import static org.lwjgl.glfw.GLFW.glfwSetInputMode;
-import static org.lwjgl.glfw.GLFW.glfwSetKeyCallback;
-import static org.lwjgl.glfw.GLFW.glfwSetMouseButtonCallback;
-import static org.lwjgl.glfw.GLFW.glfwSetScrollCallback;
+import static org.lwjgl.glfw.GLFW.*;
+import static org.lwjgl.system.MemoryUtil.NULL;
 
 /**
  * This class is a straightforward port of the
@@ -111,24 +61,38 @@ public class ImGuiImplGlfw {
     // For mouse tracking
     private final boolean[] mouseJustPressed = new boolean[ImGuiMouseButton.COUNT];
     private final ImVec2 mousePosBackup = new ImVec2();
-    private final double[] cursorPosX = new double[1];
-    private final double[] cursorPosY = new double[1];
+    private final double[] mouseX = new double[1];
+    private final double[] mouseY = new double[1];
+
+    private final int[] windowX = new int[1];
+    private final int[] windowY = new int[1];
+
+    private final int[] monitorX = new int[1];
+    private final int[] monitorY = new int[1];
+    private final int[] monitorWorkAreaX = new int[1];
+    private final int[] monitorWorkAreaY = new int[1];
+    private final int[] monitorWorkAreaWidth = new int[1];
+    private final int[] monitorWorkAreaHeight = new int[1];
+    private final float[] monitorContentScaleX = new float[1];
+    private final float[] monitorContentScaleY = new float[1];
 
     // GLFW callbacks
-    private GLFWMouseButtonCallback previousMouseButtonCallback = null;
-    private GLFWScrollCallback previousScrollCallback = null;
-    private GLFWKeyCallback previousKeyCallback = null;
-    private GLFWCharCallback previousCharCallback = null;
+    private GLFWMouseButtonCallback prevUserCallbackMouseButton = null;
+    private GLFWScrollCallback prevUserCallbackScroll = null;
+    private GLFWKeyCallback prevUserCallbackKey = null;
+    private GLFWCharCallback prevUserCallbackChar = null;
+    private GLFWMonitorCallback prevUserCallbackMonitor = null;
 
-    private boolean callbacksInstalled;
+    private boolean callbacksInstalled = false;
+    private boolean wantUpdateMonitors = true;
     private double time = 0.0;
 
     /**
      * Method to set the {@link GLFWMouseButtonCallback}.
      */
     public void mouseButtonCallback(final long windowId, final int button, final int action, final int mods) {
-        if (previousMouseButtonCallback != null) {
-            previousMouseButtonCallback.invoke(windowId, button, action, mods);
+        if (prevUserCallbackMouseButton != null && windowId == windowPtr) {
+            prevUserCallbackMouseButton.invoke(windowId, button, action, mods);
         }
 
         if (action == GLFW_PRESS && button >= 0 && button < mouseJustPressed.length) {
@@ -140,8 +104,8 @@ public class ImGuiImplGlfw {
      * Method to set the {@link GLFWScrollCallback}.
      */
     public void scrollCallback(final long windowId, final double xOffset, final double yOffset) {
-        if (previousScrollCallback != null) {
-            previousScrollCallback.invoke(windowId, xOffset, yOffset);
+        if (prevUserCallbackScroll != null && windowId == windowPtr) {
+            prevUserCallbackScroll.invoke(windowId, xOffset, yOffset);
         }
 
         final ImGuiIO io = ImGui.getIO();
@@ -153,8 +117,8 @@ public class ImGuiImplGlfw {
      * Method to set the {@link GLFWKeyCallback}.
      */
     public void keyCallback(final long windowId, final int key, final int scancode, final int action, final int mods) {
-        if (previousKeyCallback != null) {
-            previousKeyCallback.invoke(windowId, key, scancode, action, mods);
+        if (prevUserCallbackKey != null && windowId == windowPtr) {
+            prevUserCallbackKey.invoke(windowId, key, scancode, action, mods);
         }
 
         final ImGuiIO io = ImGui.getIO();
@@ -175,14 +139,21 @@ public class ImGuiImplGlfw {
      * Method to set the {@link GLFWCharCallback}.
      */
     public void charCallback(final long windowId, final int c) {
-        if (previousCharCallback != null) {
-            previousCharCallback.invoke(windowId, c);
+        if (prevUserCallbackChar != null && windowId == windowPtr) {
+            prevUserCallbackChar.invoke(windowId, c);
         }
 
         final ImGuiIO io = ImGui.getIO();
         io.addInputCharacter(c);
     }
 
+    /**
+     * Method to set the {@link GLFWMonitorCallback}.
+     */
+    public void monitorCallback(final long windowId, final int event) {
+        wantUpdateMonitors = true;
+    }
+
     /**
      * Method to do an initialization of the {@link ImGuiImplGlfw} state. It SHOULD be called before calling the {@link ImGuiImplGlfw#newFrame()} method.
      * 

@@ -193,7 +164,7 @@ public class ImGuiImplGlfw { final ImGuiIO io = ImGui.getIO(); - io.addBackendFlags(ImGuiBackendFlags.HasMouseCursors | ImGuiBackendFlags.HasSetMousePos); + io.addBackendFlags(ImGuiBackendFlags.HasMouseCursors | ImGuiBackendFlags.HasSetMousePos | ImGuiBackendFlags.PlatformHasViewports); io.setBackendPlatformName("imgui_java_impl_glfw"); // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array. @@ -253,24 +224,72 @@ public class ImGuiImplGlfw { if (installCallbacks) { callbacksInstalled = true; - previousMouseButtonCallback = glfwSetMouseButtonCallback(windowId, this::mouseButtonCallback); - previousScrollCallback = glfwSetScrollCallback(windowId, this::scrollCallback); - previousKeyCallback = glfwSetKeyCallback(windowId, this::keyCallback); - previousCharCallback = glfwSetCharCallback(windowId, this::charCallback); + prevUserCallbackMouseButton = glfwSetMouseButtonCallback(windowId, this::mouseButtonCallback); + prevUserCallbackScroll = glfwSetScrollCallback(windowId, this::scrollCallback); + prevUserCallbackKey = glfwSetKeyCallback(windowId, this::keyCallback); + prevUserCallbackChar = glfwSetCharCallback(windowId, this::charCallback); + prevUserCallbackMonitor = glfwSetMonitorCallback(this::monitorCallback); + } + + // Update monitors the first time (note: monitor callback are broken in GLFW 3.2 and earlier, see github.com/glfw/glfw/issues/784) + updateMonitors(); + glfwSetMonitorCallback(this::monitorCallback); + + // Our mouse update function expect PlatformHandle to be filled for the main viewport + final ImGuiViewport mainViewport = ImGui.getMainViewport(); + mainViewport.setPlatformHandle(windowPtr); + + if ((io.getConfigFlags() & ImGuiConfigFlags.ViewportsEnable) != 0) { + initPlatformInterface(); } return true; } + /** + * Updates {@link ImGuiIO} and {@link org.lwjgl.glfw.GLFW} state. + */ + public void newFrame() { + final ImGuiIO io = ImGui.getIO(); + if (!io.getFonts().isBuilt()) { + throw new IllegalStateException( + "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer init() method? e.g. ImGuiImplGl3.init()" + ); + } + + glfwGetWindowSize(windowPtr, winWidth, winHeight); + glfwGetFramebufferSize(windowPtr, fbWidth, fbHeight); + + io.setDisplaySize((float) winWidth[0], (float) winHeight[0]); + if (winWidth[0] > 0 && winHeight[0] > 0) { + final float scaleX = (float) fbWidth[0] / winWidth[0]; + final float scaleY = (float) fbHeight[0] / winHeight[0]; + io.setDisplayFramebufferScale(scaleX, scaleY); + } + if (wantUpdateMonitors) { + updateMonitors(); + } + + final double currentTime = glfwGetTime(); + io.setDeltaTime(time > 0.0 ? (float) (currentTime - time) : 1.0f / 60.0f); + time = currentTime; + + updateMousePosAndButtons(); + updateMouseCursor(); + updateGamepads(); + } + /** * Method to restore {@link org.lwjgl.glfw.GLFW} to it's state prior to calling method {@link ImGuiImplGlfw#init(long, boolean)}. */ public void dispose() { + shutdownPlatformInterface(); + if (callbacksInstalled) { - glfwSetMouseButtonCallback(windowPtr, previousMouseButtonCallback); - glfwSetScrollCallback(windowPtr, previousScrollCallback); - glfwSetKeyCallback(windowPtr, previousKeyCallback); - glfwSetCharCallback(windowPtr, previousCharCallback); + glfwSetMouseButtonCallback(windowPtr, prevUserCallbackMouseButton); + glfwSetScrollCallback(windowPtr, prevUserCallbackScroll); + glfwSetKeyCallback(windowPtr, prevUserCallbackKey); + glfwSetCharCallback(windowPtr, prevUserCallbackChar); callbacksInstalled = false; } @@ -290,14 +309,35 @@ public class ImGuiImplGlfw { io.getMousePos(mousePosBackup); io.setMousePos(-Float.MAX_VALUE, -Float.MAX_VALUE); + io.setMouseHoveredViewport(0); - final boolean focused = glfwGetWindowAttrib(windowPtr, GLFW_FOCUSED) != 0; - if (focused) { - if (io.getWantSetMousePos()) { - glfwSetCursorPos(windowPtr, mousePosBackup.x, mousePosBackup.y); - } else { - glfwGetCursorPos(windowPtr, cursorPosX, cursorPosY); - io.setMousePos((float) cursorPosX[0], (float) cursorPosY[0]); + final ImGuiPlatformIO platformIO = ImGui.getPlatformIO(); + + for (int n = 0; n < platformIO.getViewportsSize(); n++) { + final ImGuiViewport viewport = platformIO.getViewports(n); + final long windowPtr = viewport.getPlatformHandle(); + + final boolean focused = glfwGetWindowAttrib(windowPtr, GLFW_FOCUSED) != 0; + + if (focused) { + if (io.getWantSetMousePos()) { + glfwSetCursorPos(windowPtr, mousePosBackup.x - viewport.getPosX(), mousePosBackup.y - viewport.getPosY()); + } else { + glfwGetCursorPos(windowPtr, mouseX, mouseY); + + if ((io.getConfigFlags() & ImGuiConfigFlags.ViewportsEnable) != 0) { + // 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) + glfwGetWindowPos(windowPtr, windowX, windowY); + io.setMousePos((float) mouseX[0] + windowX[0], (float) mouseY[0] + windowY[0]); + } else { + // 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) + io.setMousePos((float) mouseX[0], (float) mouseY[0]); + } + } + + for (int i = 0; i < ImGuiMouseButton.COUNT; i++) { + io.setMouseDown(i, glfwGetMouseButton(windowPtr, i) != 0); + } } } } @@ -312,13 +352,21 @@ public class ImGuiImplGlfw { return; } - final int cursor = ImGui.getMouseCursor(); + final int imguiCursor = ImGui.getMouseCursor(); + final ImGuiPlatformIO platformIO = ImGui.getPlatformIO(); - if (cursor == ImGuiMouseCursor.None || io.getMouseDrawCursor()) { - glfwSetInputMode(windowPtr, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); - } else { - glfwSetCursor(windowPtr, mouseCursors[cursor] != 0 ? mouseCursors[cursor] : mouseCursors[ImGuiMouseCursor.Arrow]); - glfwSetInputMode(windowPtr, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + for (int n = 0; n < platformIO.getViewportsSize(); n++) { + final long windowPtr = platformIO.getViewports(n).getPlatformHandle(); + + if (imguiCursor == ImGuiMouseCursor.None || io.getMouseDrawCursor()) { + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + glfwSetInputMode(windowPtr, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + } else { + // Show OS mouse cursor + // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. + glfwSetCursor(windowPtr, mouseCursors[imguiCursor] != 0 ? mouseCursors[imguiCursor] : mouseCursors[ImGuiMouseCursor.Arrow]); + glfwSetInputMode(windowPtr, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } } } @@ -355,9 +403,9 @@ public class ImGuiImplGlfw { mapAnalog(ImGuiNavInput.LStickDown, 1, -0.3f, -0.9f, axis, axisCount, io); if (axisCount > 0 && buttonsCount > 0) { - io.setBackendFlags(io.getBackendFlags() | ImGuiBackendFlags.HasGamepad); + io.addBackendFlags(ImGuiBackendFlags.HasGamepad); } else { - io.setBackendFlags(io.getBackendFlags() & ~ImGuiBackendFlags.HasGamepad); + io.removeBackendFlags(ImGuiBackendFlags.HasGamepad); } } @@ -386,33 +434,274 @@ public class ImGuiImplGlfw { } } - /** - * Updates {@link ImGuiIO} and {@link org.lwjgl.glfw.GLFW} state. - */ - public void newFrame() { - final ImGuiIO io = ImGui.getIO(); - if (!io.getFonts().isBuilt()) { - throw new IllegalStateException( - "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer init() method? e.g. ImGuiImplGl3.init()" - ); + private void updateMonitors() { + final ImGuiPlatformIO platformIO = ImGui.getPlatformIO(); + final PointerBuffer monitors = glfwGetMonitors(); + + platformIO.resizeMonitors(0); + + for (int n = 0; n < monitors.limit(); n++) { + final long monitor = monitors.get(n); + + glfwGetMonitorPos(monitor, monitorX, monitorY); + final GLFWVidMode vidMode = glfwGetVideoMode(monitor); + final float mainPosX = monitorX[0]; + final float mainPosY = monitorY[0]; + final float mainSizeX = vidMode.width(); + final float mainSizeY = vidMode.height(); + + glfwGetMonitorWorkarea(monitor, monitorWorkAreaX, monitorWorkAreaY, monitorWorkAreaWidth, monitorWorkAreaHeight); + final float workPosX = monitorWorkAreaX[0]; + final float workPosY = monitorWorkAreaY[0]; + final float workSizeX = monitorWorkAreaWidth[0]; + final float workSizeY = monitorWorkAreaHeight[0]; + + // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, + // which generally needs to be set in the manifest or at runtime. + glfwGetMonitorContentScale(monitor, monitorContentScaleX, monitorContentScaleY); + final float dpiScale = monitorContentScaleX[0]; + + platformIO.pushMonitors(mainPosX, mainPosY, mainSizeX, mainSizeY, workPosX, workPosY, workSizeX, workSizeY, dpiScale); } - glfwGetWindowSize(windowPtr, winWidth, winHeight); - glfwGetFramebufferSize(windowPtr, fbWidth, fbHeight); + wantUpdateMonitors = false; + } - io.setDisplaySize((float) winWidth[0], (float) winHeight[0]); - if (winWidth[0] > 0 && winHeight[0] > 0) { - final float scaleX = (float) fbWidth[0] / winWidth[0]; - final float scaleY = (float) fbHeight[0] / winHeight[0]; - io.setDisplayFramebufferScale(scaleX, scaleY); + //-------------------------------------------------------------------------------------------------------- + // MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT + // This is an _advanced_ and _optional_ feature, allowing the back-end 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.. + //-------------------------------------------------------------------------------------------------------- + + private void windowCloseCallback(final long windowId) { + final ImGuiViewport vp = ImGui.findViewportByPlatformHandle(windowId); + vp.setPlatformRequestClose(true); + } + + // GLFW may dispatch window pos/size events after calling glfwSetWindowPos()/glfwSetWindowSize(). + // However: depending on the platform the callback may be invoked at different time: + // - on Windows it appears to be called within the glfwSetWindowPos()/glfwSetWindowSize() call + // - on Linux it is queued and invoked during glfwPollEvents() + // Because the event doesn't always fire on glfwSetWindowXXX() we use a frame counter tag to only + // ignore recent glfwSetWindowXXX() calls. + private void windowPosCallback(final long windowId, final int xPos, final int yPos) { + final ImGuiViewport vp = ImGui.findViewportByPlatformHandle(windowId); + final ImGuiViewportDataGlfw data = (ImGuiViewportDataGlfw) vp.getPlatformUserData(); + final boolean ignoreEvent = (ImGui.getFrameCount() <= data.ignoreWindowPosEventFrame + 1); + + if (ignoreEvent) { + return; } - final double currentTime = glfwGetTime(); - io.setDeltaTime(time > 0.0 ? (float) (currentTime - time) : 1.0f / 60.0f); - time = currentTime; + vp.setPlatformRequestMove(true); + } - updateMousePosAndButtons(); - updateMouseCursor(); - updateGamepads(); + private void windowSizeCallback(final long windowId, final int width, final int height) { + final ImGuiViewport vp = ImGui.findViewportByPlatformHandle(windowId); + final ImGuiViewportDataGlfw data = (ImGuiViewportDataGlfw) vp.getPlatformUserData(); + final boolean ignoreEvent = (ImGui.getFrameCount() <= data.ignoreWindowSizeEventFrame + 1); + + if (ignoreEvent) { + return; + } + + vp.setPlatformRequestResize(true); + } + + private final class CreateWindowFunction extends ImPlatformFuncViewport { + @Override + public void accept(final ImGuiViewport vp) { + final ImGuiViewportDataGlfw data = new ImGuiViewportDataGlfw(); + + vp.setPlatformUserData(data); + + // GLFW 3.2 unfortunately always set focus on glfwCreateWindow() if GLFW_VISIBLE is set, regardless of GLFW_FOCUSED + // With GLFW 3.3, the hint GLFW_FOCUS_ON_SHOW fixes this problem + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE); + glfwWindowHint(GLFW_FOCUS_ON_SHOW, GLFW_FALSE); + glfwWindowHint(GLFW_DECORATED, (vp.getFlags() & ImGuiViewportFlags.NoDecoration) != 0 ? GLFW_FALSE : GLFW_TRUE); + glfwWindowHint(GLFW_FLOATING, (vp.getFlags() & ImGuiViewportFlags.TopMost) != 0 ? GLFW_TRUE : GLFW_FALSE); + + data.window = glfwCreateWindow((int) vp.getSizeX(), (int) vp.getSizeY(), "No Title Yet", NULL, windowPtr); + data.windowOwned = true; + + vp.setPlatformHandle(data.window); + + glfwSetWindowPos(data.window, (int) vp.getPosX(), (int) vp.getPosY()); + + // Install GLFW callbacks for secondary viewports + glfwSetMouseButtonCallback(data.window, ImGuiImplGlfw.this::mouseButtonCallback); + glfwSetScrollCallback(data.window, ImGuiImplGlfw.this::scrollCallback); + glfwSetKeyCallback(data.window, ImGuiImplGlfw.this::keyCallback); + glfwSetCharCallback(data.window, ImGuiImplGlfw.this::charCallback); + glfwSetWindowCloseCallback(data.window, ImGuiImplGlfw.this::windowCloseCallback); + glfwSetWindowPosCallback(data.window, ImGuiImplGlfw.this::windowPosCallback); + glfwSetWindowSizeCallback(data.window, ImGuiImplGlfw.this::windowSizeCallback); + + glfwMakeContextCurrent(data.window); + glfwSwapInterval(0); + } + } + + private static final class DestroyWindowFunction extends ImPlatformFuncViewport { + @Override + public void accept(final ImGuiViewport vp) { + final ImGuiViewportDataGlfw data = (ImGuiViewportDataGlfw) vp.getPlatformUserData(); + + if (data != null && data.windowOwned) { + glfwDestroyWindow(data.window); + } + + vp.setPlatformUserData(null); + vp.setPlatformHandle(0); + } + } + + private static final class ShowWindowFunction extends ImPlatformFuncViewport { + @Override + public void accept(final ImGuiViewport vp) { + final ImGuiViewportDataGlfw data = (ImGuiViewportDataGlfw) vp.getPlatformUserData(); + glfwShowWindow(data.window); + } + } + + private static final class GetWindowPosFunction extends ImPlatformFuncViewportSuppImVec2 { + private final int[] posX = new int[1]; + private final int[] posY = new int[1]; + + @Override + public void get(final ImGuiViewport vp, final ImVec2 dstImVec2) { + final ImGuiViewportDataGlfw data = (ImGuiViewportDataGlfw) vp.getPlatformUserData(); + glfwGetWindowPos(data.window, posX, posY); + dstImVec2.x = posX[0]; + dstImVec2.y = posY[0]; + } + } + + private static final class SetWindowPosFunction extends ImPlatformFuncViewportImVec2 { + @Override + public void accept(final ImGuiViewport vp, final ImVec2 imVec2) { + final ImGuiViewportDataGlfw data = (ImGuiViewportDataGlfw) vp.getPlatformUserData(); + data.ignoreWindowPosEventFrame = ImGui.getFrameCount(); + glfwSetWindowPos(data.window, (int) imVec2.x, (int) imVec2.y); + } + } + + private static final class GetWindowSizeFunction extends ImPlatformFuncViewportSuppImVec2 { + private final int[] width = new int[1]; + private final int[] height = new int[1]; + + @Override + public void get(final ImGuiViewport vp, final ImVec2 dstImVec2) { + final ImGuiViewportDataGlfw data = (ImGuiViewportDataGlfw) vp.getPlatformUserData(); + glfwGetWindowSize(data.window, width, height); + dstImVec2.x = width[0]; + dstImVec2.y = height[0]; + } + } + + private static final class SetWindowSizeFunction extends ImPlatformFuncViewportImVec2 { + @Override + public void accept(final ImGuiViewport vp, final ImVec2 imVec2) { + final ImGuiViewportDataGlfw data = (ImGuiViewportDataGlfw) vp.getPlatformUserData(); + data.ignoreWindowSizeEventFrame = ImGui.getFrameCount(); + glfwSetWindowSize(data.window, (int) imVec2.y, (int) imVec2.y); + } + } + + private static final class SetWindowTitleFunction extends ImPlatformFuncViewportString { + @Override + public void accept(final ImGuiViewport vp, final String str) { + final ImGuiViewportDataGlfw data = (ImGuiViewportDataGlfw) vp.getPlatformUserData(); + glfwSetWindowTitle(data.window, str); + } + } + + private static final class SetWindowFocusFunction extends ImPlatformFuncViewport { + @Override + public void accept(final ImGuiViewport vp) { + final ImGuiViewportDataGlfw data = (ImGuiViewportDataGlfw) vp.getPlatformUserData(); + glfwFocusWindow(data.window); + } + } + + private static final class GetWindowFocusFunction extends ImPlatformFuncViewportSuppBoolean { + @Override + public boolean get(final ImGuiViewport vp) { + final ImGuiViewportDataGlfw data = (ImGuiViewportDataGlfw) vp.getPlatformUserData(); + return glfwGetWindowAttrib(data.window, GLFW_FOCUSED) != 0; + } + } + + private static final class GetWindowMinimizedFunction extends ImPlatformFuncViewportSuppBoolean { + @Override + public boolean get(final ImGuiViewport vp) { + final ImGuiViewportDataGlfw data = (ImGuiViewportDataGlfw) vp.getPlatformUserData(); + return glfwGetWindowAttrib(data.window, GLFW_ICONIFIED) != 0; + } + } + + private static final class SetWindowAlphaFunction extends ImPlatformFuncViewportFloat { + @Override + public void accept(final ImGuiViewport vp, final float f) { + final ImGuiViewportDataGlfw data = (ImGuiViewportDataGlfw) vp.getPlatformUserData(); + glfwSetWindowOpacity(data.window, f); + } + } + + private static final class RenderWindowFunction extends ImPlatformFuncViewport { + @Override + public void accept(final ImGuiViewport vp) { + final ImGuiViewportDataGlfw data = (ImGuiViewportDataGlfw) vp.getPlatformUserData(); + glfwMakeContextCurrent(data.window); + } + } + + private static final class SwapBuffersFunction extends ImPlatformFuncViewport { + @Override + public void accept(final ImGuiViewport vp) { + final ImGuiViewportDataGlfw data = (ImGuiViewportDataGlfw) vp.getPlatformUserData(); + glfwMakeContextCurrent(data.window); + glfwSwapBuffers(data.window); + } + } + + private void initPlatformInterface() { + final ImGuiPlatformIO platformIO = ImGui.getPlatformIO(); + + // Register platform interface (will be coupled with a renderer interface) + platformIO.setPlatformCreateWindow(new CreateWindowFunction()); + platformIO.setPlatformDestroyWindow(new DestroyWindowFunction()); + platformIO.setPlatformShowWindow(new ShowWindowFunction()); + platformIO.setPlatformGetWindowPos(new GetWindowPosFunction()); + platformIO.setPlatformSetWindowPos(new SetWindowPosFunction()); + platformIO.setPlatformGetWindowSize(new GetWindowSizeFunction()); + platformIO.setPlatformSetWindowSize(new SetWindowSizeFunction()); + platformIO.setPlatformSetWindowTitle(new SetWindowTitleFunction()); + platformIO.setPlatformSetWindowFocus(new SetWindowFocusFunction()); + platformIO.setPlatformGetWindowFocus(new GetWindowFocusFunction()); + platformIO.setPlatformGetWindowMinimized(new GetWindowMinimizedFunction()); + platformIO.setPlatformSetWindowAlpha(new SetWindowAlphaFunction()); + platformIO.setPlatformRenderWindow(new RenderWindowFunction()); + platformIO.setPlatformSwapBuffers(new SwapBuffersFunction()); + + // 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. + final ImGuiViewport mainViewport = ImGui.getMainViewport(); + final ImGuiViewportDataGlfw data = new ImGuiViewportDataGlfw(); + data.window = windowPtr; + data.windowOwned = false; + mainViewport.setPlatformUserData(data); + } + + private void shutdownPlatformInterface() { + } + + private static final class ImGuiViewportDataGlfw { + long window; + boolean windowOwned = false; + int ignoreWindowPosEventFrame = -1; + int ignoreWindowSizeEventFrame = -1; } } diff --git a/imgui-lwjgl3/src/test/java/ImGuiGlfwExample.java b/imgui-lwjgl3/src/test/java/ImGuiGlfwExample.java index 8ba4de1..6f81dd3 100644 --- a/imgui-lwjgl3/src/test/java/ImGuiGlfwExample.java +++ b/imgui-lwjgl3/src/test/java/ImGuiGlfwExample.java @@ -1,29 +1,5 @@ import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks; -import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MAJOR; -import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MINOR; -import static org.lwjgl.glfw.GLFW.GLFW_FALSE; -import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_CORE_PROFILE; -import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_FORWARD_COMPAT; -import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_PROFILE; -import static org.lwjgl.glfw.GLFW.GLFW_TRUE; -import static org.lwjgl.glfw.GLFW.GLFW_VISIBLE; -import static org.lwjgl.glfw.GLFW.glfwCreateWindow; -import static org.lwjgl.glfw.GLFW.glfwDefaultWindowHints; -import static org.lwjgl.glfw.GLFW.glfwDestroyWindow; -import static org.lwjgl.glfw.GLFW.glfwGetPrimaryMonitor; -import static org.lwjgl.glfw.GLFW.glfwGetVideoMode; -import static org.lwjgl.glfw.GLFW.glfwGetWindowSize; -import static org.lwjgl.glfw.GLFW.glfwInit; -import static org.lwjgl.glfw.GLFW.glfwMakeContextCurrent; -import static org.lwjgl.glfw.GLFW.glfwPollEvents; -import static org.lwjgl.glfw.GLFW.glfwSetErrorCallback; -import static org.lwjgl.glfw.GLFW.glfwSetWindowPos; -import static org.lwjgl.glfw.GLFW.glfwShowWindow; -import static org.lwjgl.glfw.GLFW.glfwSwapBuffers; -import static org.lwjgl.glfw.GLFW.glfwSwapInterval; -import static org.lwjgl.glfw.GLFW.glfwTerminate; -import static org.lwjgl.glfw.GLFW.glfwWindowHint; -import static org.lwjgl.glfw.GLFW.glfwWindowShouldClose; +import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.opengl.GL32.GL_COLOR_BUFFER_BIT; import static org.lwjgl.opengl.GL32.GL_TRUE; import static org.lwjgl.opengl.GL32.glClear; @@ -65,23 +41,20 @@ public final class ImGuiGlfwExample { private final ExampleUi exampleUi = new ExampleUi(); public void run() throws Exception { - // IMPORTANT!! - // This line is critical for Dear ImGui to work. - ImGui.createContext(); + setupGlfw(); + setupImGui(); // Method initializes GLFW backend. // This method SHOULD be called after you've setup GLFW. // ImGui context should be created as well. - setupGlfw(); imGuiGlfw.init(windowPtr, true); - // Method initializes LWJGL3 renderer. // This method SHOULD be called after you've initialized your ImGui configuration (fonts and so on). // ImGui context should be created as well. - setupImGui(); imGuiGl3.init(glslVersion); exampleUi.init(); + loop(); // You should clean up after yourself in reverse order. @@ -162,12 +135,16 @@ public final class ImGuiGlfwExample { // Initialize Dear ImGui. private void setupImGui() { + // IMPORTANT!! + // This line is critical for Dear ImGui to work. + ImGui.createContext(); + // ------------------------------------------------------------ // Initialize ImGuiIO config final ImGuiIO io = ImGui.getIO(); io.setIniFilename(null); // We don't want to save .ini file - io.addConfigFlags(ImGuiConfigFlags.NavEnableKeyboard | ImGuiConfigFlags.DockingEnable); // Navigation with keyboard and enabled docking + io.addConfigFlags(ImGuiConfigFlags.NavEnableKeyboard | ImGuiConfigFlags.DockingEnable | ImGuiConfigFlags.ViewportsEnable); // ------------------------------------------------------------ // Fonts configuration @@ -235,7 +212,14 @@ public final class ImGuiGlfwExample { private void endFrame() { // After Dear ImGui prepared a draw data, we use it in the LWJGL3 renderer. // At that moment ImGui will be rendered to the current OpenGL context. - imGuiGl3.render(ImGui.getDrawData()); + imGuiGl3.renderDrawData(ImGui.getDrawData()); + + if ((ImGui.getIO().getConfigFlags() & ImGuiConfigFlags.ViewportsEnable) != 0) { + final long backupWindowPtr = glfwGetCurrentContext(); + ImGui.updatePlatformWindows(); + ImGui.renderPlatformWindowsDefault(); + glfwMakeContextCurrent(backupWindowPtr); + } glfwSwapBuffers(windowPtr); glfwPollEvents();