diff --git a/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/classes/GlfwWindow.java b/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/classes/GlfwWindow.java index f3b0e35..2726335 100644 --- a/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/classes/GlfwWindow.java +++ b/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/classes/GlfwWindow.java @@ -32,8 +32,11 @@ import de.staropensource.sosengine.graphics.types.window.VsyncMode; import de.staropensource.sosengine.graphics.types.window.WindowMode; import lombok.Getter; import org.jetbrains.annotations.NotNull; +import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryUtil; +import java.nio.IntBuffer; + import static org.lwjgl.glfw.GLFW.*; /** @@ -57,8 +60,8 @@ public abstract class GlfwWindow extends Window { */ private long identifierLong; - // ------------------------------------------------ [ Window initialization ] ------------------------------------------------ // /** + // ------------------------------------------------ [ Window (de)initialization ] ------------------------------------------------ // * Creates a new window. * * @param name name @@ -85,10 +88,6 @@ public abstract class GlfwWindow extends Window { /** {@inheritDoc} */ @Override protected void initializeWindow() throws Throwable { - // Ensure running on the main thread - if (!Miscellaneous.onMainThread()) - throw new NotOnMainThreadException(); - createGlfwWindow(); // Create the GLFW window initializeGlfwWindow(); // Call GLFW window initialization method } @@ -102,22 +101,25 @@ public abstract class GlfwWindow extends Window { protected abstract void initializeGlfwWindow() throws Throwable; /** - * (Re-)Creates the GLFW window. + * (Re-)Creates the associated GLFW window. * * @since v1-alpha2 */ public void createGlfwWindow() throws WindowCreationFailureException { - // Get current focus - boolean focused = true; + // Ensure running on the main thread + if (!Miscellaneous.onMainThread()) + throw new NotOnMainThreadException(); - // Destroy existing window + // Get current focus and destroy existing window + boolean focused = true; if (getIdentifier() != null) { focused = isFocused(); - glfwDestroyWindow(identifierLong); + closeGlfwWindow(); } // Set window hints setWindowHints(); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // The window's visibility is later changed in setWindowState, this is just for setting up the window glfwWindowHint(GLFW_POSITION_X, getPosition().getX()); glfwWindowHint(GLFW_POSITION_Y, getPosition().getY()); glfwWindowHint(GLFW_CENTER_CURSOR, 0); @@ -152,6 +154,23 @@ public abstract class GlfwWindow extends Window { setWindowMode(getWindowMode()); } + /** + * Closes the associated GLFW window. + * + * @since v1-alpha2 + */ + public void closeGlfwWindow() throws WindowCreationFailureException { + // Ensure running on the main thread + if (!Miscellaneous.onMainThread()) + throw new NotOnMainThreadException(); + + // Close callbacks + keyCallback.close(); + + // Destroy the window + glfwDestroyWindow(identifierLong); + } + /** * Sets window hints. * @@ -159,6 +178,12 @@ public abstract class GlfwWindow extends Window { */ public abstract void setWindowHints(); + /** {@inheritDoc} */ + public void terminate() { + setTerminated(true); + closeGlfwWindow(); + } + // ------------------------------------------------ [ State updates ] ------------------------------------------------ // /** * Updates the window state. @@ -167,6 +192,10 @@ public abstract class GlfwWindow extends Window { */ @Override public void updateState() { + // Ensure the window is not terminated + if (isTerminated()) + return; + // Ensure running on the main thread if (!Miscellaneous.onMainThread()) throw new NotOnMainThreadException(); @@ -177,23 +206,16 @@ public abstract class GlfwWindow extends Window { // Set swap interval based on isDisallowTearing setting glfwSwapInterval(Miscellaneous.getIntegerizedBoolean(GraphicsSubsystemConfiguration.getInstance().isDisallowTearing())); - /* - // Initialize IntBuffers, used for 'size' and 'position' - IntBuffer width = MemoryUtil.memAllocInt(1); - IntBuffer height = MemoryUtil.memAllocInt(1); + try (MemoryStack stack = MemoryStack.stackPush()) { + IntBuffer width = stack.mallocInt(2); + IntBuffer height = stack.mallocInt(2); - // Update window size - glfwGetFramebufferSize(identifierLong, width, height); - super.setSize(new Vec2i(width.get(0), height.get(0))); + glfwGetWindowSize(identifierLong, width, height); + super.setSize(new Vec2i(width.get(), height.get())); - // Clear IntBuffers - width.clear(); - height.clear(); - - // Update window position - glfwGetWindowPos(identifierLong, width, height); - super.setPosition(new Vec2i(width.get(0), height.get(0))); - */ + glfwGetWindowPos(identifierLong, width, height); + super.setPosition(new Vec2i(width.get(), height.get())); + } // Update window mode if (Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifierLong, GLFW_ICONIFIED))) @@ -226,6 +248,10 @@ public abstract class GlfwWindow extends Window { /** {@inheritDoc} */ @Override public void render() throws NotOnMainThreadException { + // Ensure the window is not terminated + if (isTerminated()) + return; + // Ensure running on the main thread if (!Miscellaneous.onMainThread()) throw new NotOnMainThreadException(); @@ -248,22 +274,38 @@ public abstract class GlfwWindow extends Window { /** {@inheritDoc} */ @Override public boolean isClosureRequested() { + // Ensure the window is not terminated + if (isTerminated()) + return false; + return glfwWindowShouldClose(identifierLong); } /** {@inheritDoc} */ @Override public boolean isFocused() { + // Ensure the window is not terminated + if (isTerminated()) + return false; + return Tristate.toBoolean(Miscellaneous.getTristatedInteger(glfwGetWindowAttrib(identifierLong, GLFW_FOCUSED))); } /** {@inheritDoc} */ public void focus() { + // Ensure the window is not terminated + if (isTerminated()) + return; + glfwFocusWindow(identifierLong); } /** {@inheritDoc} */ public void requestAttention() { + // Ensure the window is not terminated + if (isTerminated()) + return; + glfwRequestWindowAttention(identifierLong); } @@ -272,6 +314,10 @@ public abstract class GlfwWindow extends Window { /** {@inheritDoc} */ @Override public void setName(@NotNull String name) { + // Ensure the window is not terminated + if (isTerminated()) + return; + super.setName(name); createGlfwWindow(); } @@ -279,6 +325,10 @@ public abstract class GlfwWindow extends Window { /** {@inheritDoc} */ @Override public void setTitle(@NotNull String title) { + // Ensure the window is not terminated + if (isTerminated()) + return; + super.setTitle(title); glfwSetWindowTitle(identifierLong, title); } @@ -286,6 +336,10 @@ public abstract class GlfwWindow extends Window { /** {@inheritDoc} */ @Override public void setSize(@NotNull Vec2i size) { + // Ensure the window is not terminated + if (isTerminated()) + return; + super.setSize(size); glfwSetWindowSize(identifierLong, size.getX(), size.getY()); } @@ -293,6 +347,10 @@ public abstract class GlfwWindow extends Window { /** {@inheritDoc} */ @Override public void setMinimumSize(@NotNull Vec2i minimumSize) { + // Ensure the window is not terminated + if (isTerminated()) + return; + super.setMinimumSize(minimumSize); glfwSetWindowSizeLimits(identifierLong, minimumSize.getX(), minimumSize.getY(), getMaximumSize().getX(), getMaximumSize().getY()); } @@ -300,6 +358,10 @@ public abstract class GlfwWindow extends Window { /** {@inheritDoc} */ @Override public void setMaximumSize(@NotNull Vec2i maximumSize) { + // Ensure the window is not terminated + if (isTerminated()) + return; + super.setMaximumSize(maximumSize); glfwSetWindowSizeLimits(identifierLong, getMinimumSize().getX(), getMinimumSize().getY(), maximumSize.getX(), maximumSize.getY()); } @@ -307,6 +369,10 @@ public abstract class GlfwWindow extends Window { /** {@inheritDoc} */ @Override public void setPosition(@NotNull Vec2i position) { + // Ensure the window is not terminated + if (isTerminated()) + return; + super.setPosition(position); glfwSetWindowSize(identifierLong, position.getX(), position.getY()); } @@ -314,6 +380,10 @@ public abstract class GlfwWindow extends Window { /** {@inheritDoc} */ @Override public void setWindowMode(@NotNull WindowMode windowMode) { + // Ensure the window is not terminated + if (isTerminated()) + return; + super.setWindowMode(windowMode); switch (windowMode) { case HIDDEN -> glfwHideWindow(identifierLong); @@ -346,30 +416,50 @@ public abstract class GlfwWindow extends Window { /** {@inheritDoc} */ @Override public void setResizable(boolean resizable) { + // Ensure the window is not terminated + if (isTerminated()) + return; + super.setResizable(resizable); } /** {@inheritDoc} */ @Override public void setBorderless(boolean borderless) { + // Ensure the window is not terminated + if (isTerminated()) + return; + super.setBorderless(borderless); } /** {@inheritDoc} */ @Override public void setFocusable(boolean focusable) { + // Ensure the window is not terminated + if (isTerminated()) + return; + super.setFocusable(focusable); } /** {@inheritDoc} */ @Override public void setOnTop(boolean onTop) { + // Ensure the window is not terminated + if (isTerminated()) + return; + super.setOnTop(onTop); } /** {@inheritDoc} */ @Override public void setTransparent(boolean transparent) { + // Ensure the window is not terminated + if (isTerminated()) + return; + super.setTransparent(transparent); createGlfwWindow(); } diff --git a/graphics/opengl/src/main/java/de/staropensource/sosengine/graphics/opengl/classes/OpenGlWindow.java b/graphics/opengl/src/main/java/de/staropensource/sosengine/graphics/opengl/classes/OpenGlWindow.java index aa341d4..7de19eb 100644 --- a/graphics/opengl/src/main/java/de/staropensource/sosengine/graphics/opengl/classes/OpenGlWindow.java +++ b/graphics/opengl/src/main/java/de/staropensource/sosengine/graphics/opengl/classes/OpenGlWindow.java @@ -89,6 +89,10 @@ public class OpenGlWindow extends GlfwWindow { @NotNull @Override public Vec2i getSizeWithDecorations() { + // Ensure the window is not terminated + if (isTerminated()) + return new Vec2i(-1, -1); + return getSize(); } @@ -96,6 +100,10 @@ public class OpenGlWindow extends GlfwWindow { @NotNull @Override public Vec2i getPositionWithDecorations() { + // Ensure the window is not terminated + if (isTerminated()) + return new Vec2i(-1, -1); + return getPosition(); } } diff --git a/graphics/src/main/java/de/staropensource/sosengine/graphics/classes/Window.java b/graphics/src/main/java/de/staropensource/sosengine/graphics/classes/Window.java index 54d6774..f106af7 100644 --- a/graphics/src/main/java/de/staropensource/sosengine/graphics/classes/Window.java +++ b/graphics/src/main/java/de/staropensource/sosengine/graphics/classes/Window.java @@ -51,7 +51,7 @@ import java.util.Set; // TODO monitors @Getter @SuppressWarnings({ "unused", "JavadocDeclaration", "JavadocBlankLines" }) -public abstract class Window { +public abstract class Window implements AutoCloseable { /** * A set of all active windows. * @@ -76,6 +76,26 @@ public abstract class Window { @Getter(value = AccessLevel.PROTECTED) private final LoggerInstance logger = new LoggerInstance(new LogIssuer(getClass(), CodePart.ENGINE)); + /** + * Determines if this window can be interacted with or not. + * + * @since v1-alpha2 + * + * -- GETTER -- + * Returns if this window can be interacted with or not. + * + * @return closed flag state + * @since v1-alpha2 + * + * -- SETTER -- + * Sets if this window can be interacted with or not. + * + * @param closed new closed flag state + * @since v1-alpha2 + */ + @Setter(AccessLevel.PROTECTED) + private boolean terminated; + /** * Contains the window identifier. * @@ -499,7 +519,7 @@ public abstract class Window { * @since v1-alpha2 */ public Window(@NotNull String name, @NotNull String title, @NotNull Vec2i size, @NotNull Vec2i minimumSize, @NotNull Vec2i maximumSize, @NotNull Vec2i position, @NotNull WindowMode windowMode, @NotNull VsyncMode vsyncMode, boolean resizable, boolean borderless, boolean focusable, boolean onTop, boolean transparent, boolean rendering) throws UnexpectedThrowableException { - logger.diag("Creating new window with properties: title=\"" + title + "\" size=" + size + " position=" + position + " windowMode=" + windowMode + " vsyncMode=" + vsyncMode + " resizable=" + resizable + " borderless=" + borderless + " focusable=" + focusable + " onTop=" + onTop + " transparent=" + transparent + " rendering=" + rendering); + logger.diag("Creating new window with properties: name=\"" + name + "\" title=\"" + title + "\" size=" + size + " minimumSize=" + minimumSize + " maximumSize=" + maximumSize + " position=" + position + " windowMode=" + windowMode + " vsyncMode=" + vsyncMode + " resizable=" + resizable + " borderless=" + borderless + " focusable=" + focusable + " onTop=" + onTop + " transparent=" + transparent + " rendering=" + rendering); // Initialize variables this.name = name; @@ -548,6 +568,9 @@ public abstract class Window { *

* Do not call this method manually or you * may cause unintended side effects. + * + * @throws Throwable thrown by implementing method + * @since v1-alpha2 */ public abstract void updateState() throws Throwable; @@ -556,9 +579,29 @@ public abstract class Window { *

* Do not call this method manually or you * may cause unintended side effects. + * + * @throws Throwable thrown by implementing method + * @since v1-alpha2 */ public abstract void render() throws Throwable; + /** + * Alias for {@link #terminate()}. + * + * @see #terminate() + * @since v1-alpha2 + */ + public void close() { + terminate(); + } + + /** + * Terminates the window, making it unable to be interacted with. + * + * @since v1-alpha2 + */ + public abstract void terminate(); + /** * Returns the window size including decorations (e.g. title bar). * diff --git a/testapp/src/main/java/de/staropensource/sosengine/testapp/Main.java b/testapp/src/main/java/de/staropensource/sosengine/testapp/Main.java index b3e7125..5378a79 100644 --- a/testapp/src/main/java/de/staropensource/sosengine/testapp/Main.java +++ b/testapp/src/main/java/de/staropensource/sosengine/testapp/Main.java @@ -116,8 +116,9 @@ public class Main { ApiManagementClass management = api.getManagement(); // Create window + Window window; try { - Window window = new Window.Builder() + window = new Window.Builder() .setName("sosengine-testapp") .setTitle("test application window") .setSize(new Vec2i(960, 540)) @@ -125,13 +126,33 @@ public class Main { .build(); } catch (Throwable throwable) { logger.crash("Window.Builder#build() failed", throwable); + return; + } + + // Sleep for 2.5 seconds + logger.diag("Sleeping for 2.5s"); + try { + Thread.sleep(2500); + } catch (InterruptedException exception) { + logger.crash("Was unable to sleep for 2500ms", exception); + } + + // Update state and render window + try { + logger.diag("Updating state"); + window.updateState(); + logger.diag("Rendering"); + window.render(); + } catch (Throwable throwable) { + logger.crash("Window updating or rendering failed", throwable); } // Sleep for five seconds + logger.diag("Sleeping for 2.5s"); try { - Thread.sleep(5000); + Thread.sleep(2500); } catch (InterruptedException exception) { - logger.crash("Was unable to sleep for 5000ms", exception); + logger.crash("Was unable to sleep for 2500ms", exception); } // Shutdown