Skip to content

Commit

Permalink
[API] Up GLFW impl
Browse files Browse the repository at this point in the history
  • Loading branch information
SpaiR committed Dec 27, 2021
1 parent 384e24a commit 0c37ac6
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 27 deletions.
15 changes: 8 additions & 7 deletions imgui-app/src/main/java/imgui/app/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
* Application class from which ImGui applications extend.
* Serves as an abstraction layer to hide all low-level details about window creation and rendering routine.
*
* <p><h3>Life-cycle</h3>
* The entry point for ImGui applications is the Application class and {@link #launch(Application)} method.
* <h3>Life-cycle</h3>
* <p>The entry point for ImGui applications is the Application class and {@link #launch(Application)} method.
* It initializes application instance and starts the main application loop.
*
* <ol>
Expand All @@ -25,8 +25,8 @@
* Instead of creating widgets and adding listeners to them we have an application loop where everything is handled right away.
* Read more about Immediate GUI mode to understand that paradigm better.
*
* <p><h3>Example</h3>
* The simplest application example could be done in the next way:
* <h3>Example</h3>
* <p>The simplest application example could be done in the next way:
*
* <pre>
* import imgui.ImGui;
Expand All @@ -44,10 +44,11 @@
* }
* </pre>
*
* <p>As its said, {@link #process()} method is meant to be overridden. All your application logic should go there.
* <p>
* As its said, {@link #process()} method is meant to be overridden. All your application logic should go there.
*
* <p><h3>Threading</h3>
* Unlike other Java applications, ImGui is about "one thread for everything". You still can use multi-threading, but be careful.
* <h3>Threading</h3>
* <p>Unlike other Java applications, ImGui is about "one thread for everything". You still can use multi-threading, but be careful.
* For example, large list of computations could be separated between application ticks. {@link #process()} method is called constantly.
* Use that wisely and remember that all GUI should be in the main thread.
*/
Expand Down
4 changes: 4 additions & 0 deletions imgui-lwjgl3/src/main/java/imgui/gl3/ImGuiImplGl3.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ public void init() {
* </pre>
* <p>
* If the argument is null, then a "#version 130" string will be used by default.
*
* @param glslVersion string with the version of the GLSL
*/
public void init(final String glslVersion) {
readGlVersion();
Expand All @@ -131,6 +133,8 @@ public void init(final String glslVersion) {

/**
* Method to render {@link ImDrawData} into current OpenGL context.
*
* @param drawData draw data to render
*/
public void renderDrawData(final ImDrawData drawData) {
if (drawData.getCmdListsCount() <= 0) {
Expand Down
110 changes: 90 additions & 20 deletions imgui-lwjgl3/src/main/java/imgui/glfw/ImGuiImplGlfw.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@
import imgui.flag.ImGuiViewportFlags;
import org.lwjgl.PointerBuffer;
import org.lwjgl.glfw.GLFWCharCallback;
import org.lwjgl.glfw.GLFWCursorEnterCallback;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWKeyCallback;
import org.lwjgl.glfw.GLFWMonitorCallback;
import org.lwjgl.glfw.GLFWMouseButtonCallback;
import org.lwjgl.glfw.GLFWNativeWin32;
import org.lwjgl.glfw.GLFWScrollCallback;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.glfw.GLFWWindowFocusCallback;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
Expand Down Expand Up @@ -93,19 +95,27 @@ public class ImGuiImplGlfw {
private final float[] monitorContentScaleY = new float[1];

// GLFW callbacks
private GLFWWindowFocusCallback prevUserCallbackWindowFocus = null;
private GLFWMouseButtonCallback prevUserCallbackMouseButton = null;
private GLFWScrollCallback prevUserCallbackScroll = null;
private GLFWKeyCallback prevUserCallbackKey = null;
private GLFWCharCallback prevUserCallbackChar = null;
private GLFWMonitorCallback prevUserCallbackMonitor = null;
private GLFWCursorEnterCallback prevUserCallbackCursorEnter = null;

// Internal data
private boolean callbacksInstalled = false;
private boolean wantUpdateMonitors = true;
private double time = 0.0;
private long mouseWindowPtr;

/**
* Method to set the {@link GLFWMouseButtonCallback}.
*
* @param windowId pointer to the window
* @param button clicked mouse button
* @param action click action type
* @param mods click modifiers
*/
public void mouseButtonCallback(final long windowId, final int button, final int action, final int mods) {
if (prevUserCallbackMouseButton != null && windowId == windowPtr) {
Expand All @@ -119,6 +129,10 @@ public void mouseButtonCallback(final long windowId, final int button, final int

/**
* Method to set the {@link GLFWScrollCallback}.
*
* @param windowId pointer to the window
* @param xOffset scroll offset by x-axis
* @param yOffset scroll offset by y-axis
*/
public void scrollCallback(final long windowId, final double xOffset, final double yOffset) {
if (prevUserCallbackScroll != null && windowId == windowPtr) {
Expand All @@ -132,6 +146,12 @@ public void scrollCallback(final long windowId, final double xOffset, final doub

/**
* Method to set the {@link GLFWKeyCallback}.
*
* @param windowId pointer to the window
* @param key pressed key
* @param scancode key scancode
* @param action press action
* @param mods press modifiers
*/
public void keyCallback(final long windowId, final int key, final int scancode, final int action, final int mods) {
if (prevUserCallbackKey != null && windowId == windowPtr) {
Expand All @@ -156,8 +176,44 @@ public void keyCallback(final long windowId, final int key, final int scancode,
io.setKeySuper(io.getKeysDown(GLFW_KEY_LEFT_SUPER) || io.getKeysDown(GLFW_KEY_RIGHT_SUPER));
}

/**
* Method to set the {@link GLFWWindowFocusCallback}.
*
* @param windowId pointer to the window
* @param focused is window focused
*/
public void windowFocusCallback(final long windowId, final boolean focused) {
if (prevUserCallbackWindowFocus != null && windowId == windowPtr) {
prevUserCallbackWindowFocus.invoke(windowId, focused);
}

ImGui.getIO().addFocusEvent(focused);
}

/**
* Method to set the {@link GLFWCursorEnterCallback}.
*
* @param windowId pointer to the window
* @param entered is cursor entered
*/
public void cursorEnterCallback(final long windowId, final boolean entered) {
if (prevUserCallbackCursorEnter != null && windowId == windowPtr) {
prevUserCallbackCursorEnter.invoke(windowId, entered);
}

if (entered) {
mouseWindowPtr = windowId;
}
if (!entered && mouseWindowPtr == windowId) {
mouseWindowPtr = 0;
}
}

/**
* Method to set the {@link GLFWCharCallback}.
*
* @param windowId pointer to the window
* @param c pressed char
*/
public void charCallback(final long windowId, final int c) {
if (prevUserCallbackChar != null && windowId == windowPtr) {
Expand All @@ -170,6 +226,9 @@ public void charCallback(final long windowId, final int c) {

/**
* Method to set the {@link GLFWMonitorCallback}.
*
* @param windowId pointer to the window
* @param event monitor event type (ignored)
*/
public void monitorCallback(final long windowId, final int event) {
wantUpdateMonitors = true;
Expand All @@ -179,6 +238,10 @@ public void monitorCallback(final long windowId, final int event) {
* Method to do an initialization of the {@link ImGuiImplGlfw} state. It SHOULD be called before calling the {@link ImGuiImplGlfw#newFrame()} method.
* <p>
* Method takes two arguments, which should be a valid GLFW window pointer and a boolean indicating whether or not to install callbacks.
*
* @param windowId pointer to the window
* @param installCallbacks should window callbacks be installed
* @return true if everything initialized
*/
public boolean init(final long windowId, final boolean installCallbacks) {
this.windowPtr = windowId;
Expand Down Expand Up @@ -247,6 +310,8 @@ public void accept(final String str) {

if (installCallbacks) {
callbacksInstalled = true;
prevUserCallbackWindowFocus = glfwSetWindowFocusCallback(windowId, this::windowFocusCallback);
prevUserCallbackCursorEnter = glfwSetCursorEnterCallback(windowId, this::cursorEnterCallback);
prevUserCallbackMouseButton = glfwSetMouseButtonCallback(windowId, this::mouseButtonCallback);
prevUserCallbackScroll = glfwSetScrollCallback(windowId, this::scrollCallback);
prevUserCallbackKey = glfwSetKeyCallback(windowId, this::keyCallback);
Expand Down Expand Up @@ -278,11 +343,6 @@ public void accept(final String str) {
*/
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);
Expand Down Expand Up @@ -313,6 +373,8 @@ public void dispose() {
shutdownPlatformInterface();

if (callbacksInstalled) {
glfwSetWindowFocusCallback(windowPtr, prevUserCallbackWindowFocus);
glfwSetCursorEnterCallback(windowPtr, prevUserCallbackCursorEnter);
glfwSetMouseButtonCallback(windowPtr, prevUserCallbackMouseButton);
glfwSetScrollCallback(windowPtr, prevUserCallbackScroll);
glfwSetKeyCallback(windowPtr, prevUserCallbackKey);
Expand Down Expand Up @@ -362,26 +424,34 @@ private void updateMousePosAndButtons() {

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.hasConfigFlags(ImGuiConfigFlags.ViewportsEnable)) {
// 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]);
}
}
final long mouseWindowPtr = (this.mouseWindowPtr == windowPtr || focused) ? windowPtr : 0;

// Update mouse buttons
if (focused) {
for (int i = 0; i < ImGuiMouseButton.COUNT; i++) {
io.setMouseDown(i, glfwGetMouseButton(windowPtr, i) != 0);
}
}

// 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.getWantSetMousePos() && focused) {
glfwSetCursorPos(windowPtr, mousePosBackup.x - viewport.getPosX(), mousePosBackup.y - viewport.getPosY());
}

// Set Dear ImGui mouse position from OS position
if (mouseWindowPtr != 0) {
glfwGetCursorPos(mouseWindowPtr, mouseX, mouseY);

if (io.hasConfigFlags(ImGuiConfigFlags.ViewportsEnable)) {
// 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]);
}
}
}
}

Expand Down

0 comments on commit 0c37ac6

Please sign in to comment.