Implement experimental window icon support
This commit is contained in:
parent
cdd68833cb
commit
1e6fa79716
5 changed files with 148 additions and 13 deletions
|
@ -134,9 +134,11 @@ public final class Main {
|
|||
// Create window
|
||||
Window window;
|
||||
try {
|
||||
// Build window
|
||||
window = new Window.Builder()
|
||||
.setName("sosengine-testapp")
|
||||
.setTitle("test application window")
|
||||
//.setIcons(new Path[]{ new File("/home/jeremystartm/Code/StarOpenSource/Engine/dist/branding/current.png").toPath() })
|
||||
.setSize(new Vec2i(960, 540))
|
||||
.setPosition(new Vec2i(10, 10))
|
||||
.build();
|
||||
|
@ -145,6 +147,7 @@ public final class Main {
|
|||
return;
|
||||
}
|
||||
|
||||
// Render loop
|
||||
LinkedHashMap<@NotNull Window, @NotNull Throwable> renderLoopFailures = WindowingSubsystem
|
||||
.getInstance()
|
||||
.getApi()
|
||||
|
@ -153,6 +156,8 @@ public final class Main {
|
|||
if (shutdown || window.isClosureRequested())
|
||||
Engine.getInstance().shutdown();
|
||||
});
|
||||
|
||||
// Print render loop failures
|
||||
StringBuilder message = new StringBuilder();
|
||||
message.append("Render loop failed on some windows:\n");
|
||||
|
||||
|
@ -172,7 +177,6 @@ public final class Main {
|
|||
System.err.println(Miscellaneous.getStackTraceHeader(exception));
|
||||
System.err.println(Miscellaneous.getStackTraceAsString(exception, true));
|
||||
|
||||
// Halt JVM
|
||||
Runtime.getRuntime().halt(255);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,13 +31,12 @@ switch (OperatingSystem.current()) {
|
|||
case OperatingSystem.LINUX:
|
||||
project.dependencyLwjglNatives = "natives-linux"
|
||||
def osArch = System.getProperty("os.arch")
|
||||
if (osArch.startsWith("arm") || osArch.startsWith("aarch64")) {
|
||||
if (osArch.startsWith("arm") || osArch.startsWith("aarch64"))
|
||||
project.dependencyLwjglNatives += osArch.contains("64") || osArch.startsWith("armv8") ? "-arm64" : "-arm32"
|
||||
} else if (osArch.startsWith("ppc")) {
|
||||
else if (osArch.startsWith("ppc"))
|
||||
project.dependencyLwjglNatives += "-ppc64le"
|
||||
} else if (osArch.startsWith("riscv")) {
|
||||
else if (osArch.startsWith("riscv"))
|
||||
project.dependencyLwjglNatives += "-riscv64"
|
||||
}
|
||||
break
|
||||
case OperatingSystem.MAC_OS:
|
||||
project.dependencyLwjglNatives = System.getProperty("os.arch").startsWith("aarch64") ? "natives-macos-arm64" : "natives-macos"
|
||||
|
@ -64,8 +63,10 @@ dependencies {
|
|||
implementation(platform("org.lwjgl:lwjgl-bom:${dependencyLwjgl}"))
|
||||
implementation("org.lwjgl:lwjgl")
|
||||
implementation("org.lwjgl:lwjgl-glfw")
|
||||
implementation("org.lwjgl:lwjgl-stb")
|
||||
runtimeOnly("org.lwjgl:lwjgl::${dependencyLwjglNatives}")
|
||||
runtimeOnly("org.lwjgl:lwjgl-glfw::${dependencyLwjglNatives}")
|
||||
runtimeOnly("org.lwjgl:lwjgl-stb::${dependencyLwjglNatives}")
|
||||
if (project.dependencyLwjglNatives == "natives-macos" || project.dependencyLwjglNatives == "natives-macos-arm64") runtimeOnly("org.lwjgl:lwjgl-vulkan::${dependencyLwjglNatives}")
|
||||
|
||||
// -> Testing <-
|
||||
|
@ -126,7 +127,7 @@ test {
|
|||
useJUnitPlatform()
|
||||
|
||||
// Pass test configuration to test VMs
|
||||
Map<String, String> testConfiguration = new HashMap<>();
|
||||
Map<String, String> testConfiguration = new HashMap<>()
|
||||
for (String property : project.properties.keySet())
|
||||
if (property.startsWith("test."))
|
||||
testConfiguration.put(property, project.properties.get(property).toString())
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package de.staropensource.engine.windowing.glfw.implementable;
|
||||
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.type.vector.Vec2i;
|
||||
import de.staropensource.engine.base.utility.Miscellaneous;
|
||||
import de.staropensource.engine.windowing.WindowingSubsystemConfiguration;
|
||||
|
@ -33,13 +34,21 @@ import de.staropensource.engine.windowing.implementable.Window;
|
|||
import de.staropensource.engine.windowing.type.window.VsyncMode;
|
||||
import de.staropensource.engine.windowing.type.window.WindowMode;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.glfw.GLFWImage;
|
||||
import org.lwjgl.glfw.GLFWKeyCallback;
|
||||
import org.lwjgl.glfw.GLFWMouseButtonCallback;
|
||||
import org.lwjgl.stb.STBImage;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
|
@ -84,6 +93,7 @@ public final class GlfwWindow extends Window {
|
|||
*
|
||||
* @param name name
|
||||
* @param title title
|
||||
* @param icons icons
|
||||
* @param size size
|
||||
* @param minimumSize minimum size
|
||||
* @param maximumSize maximum size
|
||||
|
@ -99,8 +109,8 @@ public final class GlfwWindow extends Window {
|
|||
* @throws Exception stuff thrown by the {@link #initializeWindow()} and {@link #render()} methods of the implementing windowing API
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public GlfwWindow(@NotNull String name, @NotNull String title, @NotNull Vec2i size, @NotNull Vec2i minimumSize, @NotNull Vec2i maximumSize, @NotNull Vec2i position, @NotNull WindowMode windowMode, @NotNull Monitor monitor, boolean resizable, boolean borderless, boolean focusable, boolean onTop, boolean transparent, boolean rendering) throws Exception {
|
||||
super(name, title, size, minimumSize, maximumSize, position, windowMode, monitor, resizable, borderless, focusable, onTop, transparent, rendering);
|
||||
public GlfwWindow(@NotNull String name, @NotNull String title, @NotNull Path @Nullable [] icons, @NotNull Vec2i size, @NotNull Vec2i minimumSize, @NotNull Vec2i maximumSize, @NotNull Vec2i position, @NotNull WindowMode windowMode, @NotNull Monitor monitor, boolean resizable, boolean borderless, boolean focusable, boolean onTop, boolean transparent, boolean rendering) throws Exception {
|
||||
super(name, title, icons, size, minimumSize, maximumSize, position, windowMode, monitor, resizable, borderless, focusable, onTop, transparent, rendering);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
|
@ -163,6 +173,7 @@ public final class GlfwWindow extends Window {
|
|||
glfwSetMouseButtonCallback(identifier, mouseButtonCallback);
|
||||
|
||||
// Update the window state
|
||||
setIcons(getIcons());
|
||||
setSize(getSize());
|
||||
setMinimumSize(getMinimumSize());
|
||||
setMaximumSize(getMaximumSize());
|
||||
|
@ -342,6 +353,67 @@ public final class GlfwWindow extends Window {
|
|||
glfwSetWindowTitle(identifierLong, title);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@ApiStatus.Experimental
|
||||
@Override
|
||||
public void setIcons(@NotNull Path @Nullable [] icons) {
|
||||
getLogger().warn("GlfwWindow#setIcons is experimental and may cause engine or JVM crashes. Here be dragons!");
|
||||
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
|
||||
super.setIcons(icons);
|
||||
if (icons != null)
|
||||
try (GLFWImage.Buffer iconsBuffer = GLFWImage.malloc(icons.length)) {
|
||||
getLogger().diag("GlfwWindow#setIcons » icons.length = " + icons.length);
|
||||
|
||||
List<ByteBuffer> iconBuffers = new ArrayList<>();
|
||||
IntBuffer width = MemoryUtil.memAllocInt(1);
|
||||
IntBuffer height = MemoryUtil.memAllocInt(1);
|
||||
IntBuffer channels = MemoryUtil.memAllocInt(1);
|
||||
|
||||
for (Path filepath : icons) {
|
||||
getLogger().diag("GlfwWindow#setIcons » iterating icons » " + iconBuffers.size() + " » " + filepath);
|
||||
// Load icon
|
||||
getLogger().diag("GlfwWindow#setIcons » loading icon");
|
||||
iconBuffers.add(STBImage.stbi_load(filepath.toAbsolutePath().toString(), width, height, channels, 4));
|
||||
|
||||
if (iconBuffers.getLast() == null) {
|
||||
getLogger().warn("Icon " + iconsBuffer.position() + " could not be loaded" + (STBImage.stbi_failure_reason() == null ? "" : ": " + STBImage.stbi_failure_reason()));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Save into 'iconsBuffer'
|
||||
getLogger().diag("GlfwWindow#setIcons » saving into buffer");
|
||||
iconsBuffer
|
||||
.position(iconsBuffer.position() + 1)
|
||||
.width(width.get(0))
|
||||
.height(height.get(0))
|
||||
.pixels(iconBuffers.getLast());
|
||||
}
|
||||
getLogger().diag("GlfwWindow#setIcons » out of iteration");
|
||||
|
||||
// Set icons
|
||||
getLogger().diag("GlfwWindow#setIcons » setting position");
|
||||
iconsBuffer.position(0);
|
||||
getLogger().diag("GlfwWindow#setIcons » setting icons");
|
||||
Logger.flushLogMessages();
|
||||
glfwSetWindowIcon(identifierLong, iconsBuffer);
|
||||
|
||||
// Free icons
|
||||
getLogger().diag("GlfwWindow#setIcons » freeing icons");
|
||||
for (ByteBuffer buffer : iconBuffers)
|
||||
if (buffer != null) {
|
||||
getLogger().diag("GlfwWindow#setIcons » freeing buffer");
|
||||
STBImage.stbi_image_free(buffer);
|
||||
} else
|
||||
getLogger().diag("GlfwWindow#setIcons » skipping null buffer");
|
||||
}
|
||||
else
|
||||
getLogger().diag("GlfwWindow#setIcons » icons is null");
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setSize(@NotNull Vec2i size) {
|
||||
|
|
|
@ -12,8 +12,8 @@ module sosengine.windowing.glfw {
|
|||
// -> Libraries
|
||||
requires transitive static lombok;
|
||||
requires transitive org.jetbrains.annotations;
|
||||
requires org.lwjgl;
|
||||
requires org.lwjgl.glfw;
|
||||
requires org.lwjgl.stb;
|
||||
|
||||
// API access
|
||||
exports de.staropensource.engine.windowing.glfw;
|
||||
|
|
|
@ -30,6 +30,7 @@ import lombok.Setter;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
|
@ -202,6 +203,27 @@ public abstract class Window implements AutoCloseable {
|
|||
@Setter
|
||||
private @NotNull String title;
|
||||
|
||||
/**
|
||||
* Contains file paths to window
|
||||
* icons in the PNG format.
|
||||
*
|
||||
* @since v1-alpha6
|
||||
* -- GETTER --
|
||||
* Returns file paths to window
|
||||
* icons in the PNG format.
|
||||
*
|
||||
* @return file paths to icons
|
||||
* @since v1-alpha6
|
||||
* -- SETTER --
|
||||
* Sets file paths to window
|
||||
* icons in the PNG format.
|
||||
*
|
||||
* @param icons new file paths to icons
|
||||
* @since v1-alpha6
|
||||
*/
|
||||
@Setter
|
||||
private @NotNull Path @Nullable [] icons;
|
||||
|
||||
/**
|
||||
* Contains the size of this window.
|
||||
*
|
||||
|
@ -470,6 +492,7 @@ public abstract class Window implements AutoCloseable {
|
|||
*
|
||||
* @param name name
|
||||
* @param title title
|
||||
* @param icons icon file paths
|
||||
* @param size size
|
||||
* @param minimumSize minimum size
|
||||
* @param maximumSize maximum size
|
||||
|
@ -485,11 +508,12 @@ public abstract class Window implements AutoCloseable {
|
|||
* @throws Exception stuff thrown by the {@link #initializeWindow()} and {@link #render()} methods of the implementing windowing API
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
protected Window(@NotNull String name, @NotNull String title, @NotNull Vec2i size, @NotNull Vec2i minimumSize, @NotNull Vec2i maximumSize, @NotNull Vec2i position, @NotNull WindowMode windowMode, @NotNull Monitor monitor, boolean resizable, boolean borderless, boolean focusable, boolean onTop, boolean transparent, boolean rendering) throws Exception {
|
||||
protected Window(@NotNull String name, @NotNull String title, @NotNull Path @Nullable [] icons, @NotNull Vec2i size, @NotNull Vec2i minimumSize, @NotNull Vec2i maximumSize, @NotNull Vec2i position, @NotNull WindowMode windowMode, @NotNull Monitor monitor, boolean resizable, boolean borderless, boolean focusable, boolean onTop, boolean transparent, boolean rendering) throws Exception {
|
||||
// Initialize variables
|
||||
this.uniqueIdentifier = UUID.randomUUID();
|
||||
this.name = name;
|
||||
this.title = title;
|
||||
this.icons = icons;
|
||||
this.size = size;
|
||||
this.minimumSize = minimumSize;
|
||||
this.maximumSize = maximumSize;
|
||||
|
@ -504,7 +528,7 @@ public abstract class Window implements AutoCloseable {
|
|||
this.rendering = rendering;
|
||||
|
||||
// Log about window creation
|
||||
logger.diag("Creating new window with properties: uniqueIdentifier=" + uniqueIdentifier + " name=\"" + name + "\" title=\"" + title + "\" size=" + size + " minimumSize=" + minimumSize + " maximumSize=" + maximumSize + " position=" + position + " windowMode=" + windowMode + " monitor=" + monitor.getUniqueIdentifier() + " (" + monitor.getName() + ") resizable=" + resizable + " borderless=" + borderless + " focusable=" + focusable + " onTop=" + onTop + " transparent=" + transparent + " rendering=" + rendering);
|
||||
logger.diag("Creating new window with properties: uniqueIdentifier=" + uniqueIdentifier + " name=\"" + name + "\" title=\"" + title + "\" icons=" + Arrays.toString(icons) + " size=" + size + " minimumSize=" + minimumSize + " maximumSize=" + maximumSize + " position=" + position + " windowMode=" + windowMode + " monitor=" + monitor.getUniqueIdentifier() + " (" + monitor.getName() + ") resizable=" + resizable + " borderless=" + borderless + " focusable=" + focusable + " onTop=" + onTop + " transparent=" + transparent + " rendering=" + rendering);
|
||||
|
||||
// Allow windowing API to initialize window
|
||||
initializeWindow();
|
||||
|
@ -627,6 +651,14 @@ public abstract class Window implements AutoCloseable {
|
|||
*/
|
||||
private @Nullable String title = null;
|
||||
|
||||
/**
|
||||
* Contains the window icons in the PNG format.
|
||||
*
|
||||
* @see Window#icons
|
||||
* @since v1-alpha6
|
||||
*/
|
||||
private @NotNull Path @Nullable [] icons = null;
|
||||
|
||||
/**
|
||||
* Contains the window size.
|
||||
*
|
||||
|
@ -783,8 +815,8 @@ public abstract class Window implements AutoCloseable {
|
|||
|
||||
// Create new Window instance
|
||||
return WindowingSubsystem.getInstance().getApi().getInternalApi().getWindowClass()
|
||||
.getDeclaredConstructor(String.class, String.class, Vec2i.class, Vec2i.class, Vec2i.class, Vec2i.class, WindowMode.class, Monitor.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE)
|
||||
.newInstance(name, title, size, minimumSize, maximumSize, position, windowMode, monitor, resizableBoolean, borderlessBoolean, focusableBoolean, onTopBoolean, transparentBoolean, renderingBoolean);
|
||||
.getDeclaredConstructor(String.class, String.class, Path[].class, Vec2i.class, Vec2i.class, Vec2i.class, Vec2i.class, WindowMode.class, Monitor.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE)
|
||||
.newInstance(name, title, icons, size, minimumSize, maximumSize, position, windowMode, monitor, resizableBoolean, borderlessBoolean, focusableBoolean, onTopBoolean, transparentBoolean, renderingBoolean);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -809,6 +841,18 @@ public abstract class Window implements AutoCloseable {
|
|||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns file paths to window
|
||||
* icons in the PNG format.
|
||||
*
|
||||
* @return file paths to icons
|
||||
* @see Window#icons
|
||||
* @since v1-alpha6
|
||||
*/
|
||||
public @NotNull Path @Nullable [] getIcons() {
|
||||
return icons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the window size.
|
||||
*
|
||||
|
@ -967,6 +1011,20 @@ public abstract class Window implements AutoCloseable {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets file paths to window
|
||||
* icons in the PNG format.
|
||||
*
|
||||
* @param icons new file paths to icons
|
||||
* @return builder instance
|
||||
* @see Window#icons
|
||||
* @since v1-alpha6
|
||||
*/
|
||||
public @NotNull Builder setIcons(@NotNull Path @Nullable [] icons) {
|
||||
this.icons = icons;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the window size.
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue