Merge and rename windowing subsystem

This commit is contained in:
JeremyStar™ 2024-11-10 20:55:01 +01:00
parent 69cf668c4d
commit a45f010196
Signed by: JeremyStarTM
GPG key ID: E366BAEF67E4704D
66 changed files with 1697 additions and 2975 deletions

View file

@ -30,8 +30,7 @@ tasks.register("javadocAll", Javadoc) {
":base",
":ansi",
":slf4j-compat",
":windowing",
":windowing:glfw",
":rendering",
":notification",
]

View file

@ -61,16 +61,17 @@ dependencies {
// LWJGL
implementation(platform("org.lwjgl:lwjgl-bom:${dependencyLwjgl}"))
implementation("org.lwjgl:lwjgl")
implementation("org.lwjgl:lwjgl-glfw")
implementation("org.lwjgl:lwjgl-stb")
implementation("org.lwjgl:lwjgl-glfw")
implementation("org.lwjgl:lwjgl-bgfx")
runtimeOnly("org.lwjgl:lwjgl::${dependencyLwjglNatives}")
runtimeOnly("org.lwjgl:lwjgl-glfw::${dependencyLwjglNatives}")
runtimeOnly("org.lwjgl:lwjgl-stb::${dependencyLwjglNatives}")
runtimeOnly("org.lwjgl:lwjgl-glfw::${dependencyLwjglNatives}")
runtimeOnly("org.lwjgl:lwjgl-bgfx::${dependencyLwjglNatives}")
if (project.dependencyLwjglNatives == "natives-macos" || project.dependencyLwjglNatives == "natives-macos-arm64") runtimeOnly("org.lwjgl:lwjgl-vulkan::${dependencyLwjglNatives}")
// Project
implementation(project(":base"))
implementation(project(":windowing"))
}
// Javadoc configuration
@ -96,26 +97,6 @@ javadoc {
}
}
// Unit testing configuration
test {
useJUnitPlatform()
// Pass test configuration to test VMs
Map<String, String> testConfiguration = new HashMap<>()
for (String property : project.properties.keySet())
if (property.startsWith("test."))
testConfiguration.put(property, project.properties.get(property).toString())
systemProperties(testConfiguration)
setMaxParallelForks(project.hasProperty("jobs") ? Integer.parseInt((String) project.property("jobs")) : 8)
setForkEvery(1)
setFailFast(true)
testLogging {
events("passed", "skipped", "failed")
}
}
// Include javadoc and source jar during publishing
java {
withJavadocJar()

View file

@ -0,0 +1,323 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.rendering;
import de.staropensource.engine.base.annotation.EngineSubsystem;
import de.staropensource.engine.base.annotation.EventListener;
import de.staropensource.engine.base.implementable.SubsystemClass;
import de.staropensource.engine.base.implementable.helper.EventHelper;
import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.type.EventPriority;
import de.staropensource.engine.base.utility.Math;
import de.staropensource.engine.base.utility.information.EngineInformation;
import de.staropensource.engine.base.implementation.versioning.StarOpenSourceVersioningSystem;
import de.staropensource.engine.base.event.InternalEngineShutdownEvent;
import de.staropensource.engine.base.type.DependencyVector;
import de.staropensource.engine.base.utility.Miscellaneous;
import de.staropensource.engine.rendering.event.InputEvent;
import de.staropensource.engine.rendering.event.RenderingErrorEvent;
import de.staropensource.engine.rendering.exception.NotOnMainThreadException;
import de.staropensource.engine.rendering.type.Window;
import de.staropensource.engine.rendering.type.window.VsyncMode;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.*;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicReference;
import static org.lwjgl.glfw.GLFW.*;
/**
* Main class of the {@code rendering} subsystem.
*
* @since v1-alpha9
*/
@EngineSubsystem
@SuppressWarnings({ "JavadocDeclaration" })
public final class RenderingSubsystem extends SubsystemClass {
/**
* Contains the class instance.
*
* @since v1-alpha9
* -- GETTER --
* Returns the class instance.
*
* @return class instance unless the subsystem is uninitialized
* @since v1-alpha9
*/
@Getter
private static RenderingSubsystem instance = null;
/**
* The {@link GLFWErrorCallback} to use.
* <p>
* Only declared publicly for freeing during engine shutdown.
*
* @since v1-alpha2
*/
private GLFWErrorCallback errorCallback = null;
// -----> Subsystem
/**
* Initializes this subsystem.
*
* @since v1-alpha9
*/
public RenderingSubsystem() {
// Only allow one instance
if (instance == null)
instance = this;
else
Logger.crash("Only one instance of this class is allowed, use getInstance() instead of creating a new instance");
}
/** {@inheritDoc} */
@Override
public @NotNull String getName() {
return "rendering";
}
/** {@inheritDoc} */
@Override
public @NotNull DependencyVector getDependencyVector() {
return new DependencyVector.Builder()
.setIdentifier(getName())
.setVersioningSystem(StarOpenSourceVersioningSystem.class)
.setVersion(EngineInformation.getVersioningString())
.build();
}
/** {@inheritDoc} */
@Override
public void initializeSubsystem() {
if (!Miscellaneous.onMainThread())
Logger.crash("Unable to initialize the rendering subsystem whilst running on a non-main thread", new NotOnMainThreadException());
// Initialize WindowingSubsystemConfiguration and load it
new RenderingSubsystemConfiguration().loadConfiguration();
// Precompute event listeners
cacheEvents();
initGlfw();
// Warn about subsystem and API instability
Logger.warn("The rendering subsystem is experimental. Subsystem and API stability are not guaranteed.");
}
/**
* Caches all windowing subsystem events.
*
* @since v1-alpha9
*/
private void cacheEvents() {
EventHelper.cacheEvent(RenderingErrorEvent.class);
EventHelper.cacheEvent(InputEvent.class);
}
/**
* Initializes GLFW.
*
* @since v1-alpha9
*/
private void initGlfw() {
try {
Logger.verb("Initializing GLFW");
// Set error callback
Logger.diag("Setting error callback");
errorCallback = GLFWErrorCallback.create((error, description) -> new RenderingErrorEvent().callEvent("GLFW: " + GLFWErrorCallback.getDescription(description) + " [" + error + "]")).set();
// Set init hints
Logger.diag("Setting initialization hints");
switch (RenderingSubsystemConfiguration.getInstance().getInitialPlatform()) {
case ANY -> glfwInitHint(GLFW_PLATFORM, GLFW_ANY_PLATFORM);
case WAYLAND -> tryPlatform(GLFW_PLATFORM_WAYLAND);
case X11 -> tryPlatform(GLFW_PLATFORM_X11);
case WIN32 -> tryPlatform(GLFW_PLATFORM_WIN32);
case COCOA -> tryPlatform(GLFW_PLATFORM_COCOA);
case NONE -> glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_NULL);
}
glfwInitHint(GLFW_WAYLAND_LIBDECOR, RenderingSubsystemConfiguration.getInstance().isInitialDisableLibdecor() ? GLFW_WAYLAND_DISABLE_LIBDECOR : GLFW_WAYLAND_PREFER_LIBDECOR);
// Initialize GLFW
Logger.diag("Invoking glfwInit");
if (!glfwInit())
Logger.crash("Failed to initialize GLFW");
} catch (UnsatisfiedLinkError error) {
Logger.crash("Failed to load LWJGL native libraries", error);
}
}
/**
* Shuts the subsystem down.
*
* @since v1-alpha9
*/
@EventListener(event = InternalEngineShutdownEvent.class)
@SuppressWarnings({ "unused" })
protected static void shutdownSubsystem() {
if (instance == null)
return;
Logger.verb("Shutting down");
long shutdownTime = Miscellaneous.measureExecutionTime(() -> {
// Close all windows
for (Window window : Window.getWindows())
window.close();
instance.errorCallback.free();
glfwTerminate();
});
Logger.info("Shut down in " + shutdownTime + "ms");
}
/**
* Logs about rendering errors.
*
* @see RenderingSubsystemConfiguration#errorRenderingFailures
* @since v1-alpha9
*/
@EventListener(event = RenderingErrorEvent.class, priority = EventPriority.EXCLUSIVELY_IMPORTANT)
private static void logRenderingError(@NotNull String error) {
Logger.error("Rendering error occurred: " + error);
}
// -----> APIs
/**
* Renders all windows once.
* To render all windows continuously, invoke
* {@link #runRenderLoop(Runnable)} instead.
*
* @return map of windows and their {@link Throwable}s
* @throws NotOnMainThreadException if not running on the main thread
* @since v1-alpha9
*/
public LinkedHashMap<@NotNull Window, @NotNull Throwable> renderWindows() throws NotOnMainThreadException {
// Ensure running on the main thread
if (!Miscellaneous.onMainThread())
throw new NotOnMainThreadException();
LinkedHashMap<@NotNull Window, @NotNull Throwable> throwables = new LinkedHashMap<>();
// Update and render all windows
for (Window window : Window.getWindows()) {
if (!window.isRendering())
continue;
try {
window.updateState();
window.render();
} catch (Throwable throwable) {
Logger.error("Rendering window " + window + " failed: Threw throwable " + throwable.getClass().getName() + (throwable.getMessage() == null ? "" : ": " + throwable.getMessage()));
throwables.put(window, throwable);
}
}
// Poll for events
glfwPollEvents();
return throwables;
}
/**
* Renders all windows continuously.
* To render all windows just once, invoke
* {@link #renderWindows()} instead.
* <p>
* Immediately returns when a {@link #renderWindows()} call fails.
*
* @param frameCode code which shall be invoked before a frame is rendered
* @return see {@link #renderWindows()} (on failure)
* @throws NotOnMainThreadException if not running on the main thread
* @since v1-alpha9
*/
public LinkedHashMap<@NotNull Window, @NotNull Throwable> runRenderLoop(@NotNull Runnable frameCode) throws NotOnMainThreadException {
// Ensure running on the main thread
if (!Miscellaneous.onMainThread())
throw new NotOnMainThreadException();
// Define variables
AtomicReference<LinkedHashMap<@NotNull Window, @NotNull Throwable>> output = new AtomicReference<>(new LinkedHashMap<>()); // runRenderLoop output
long renderTime; // Amount of time spent rendering
long sleepDuration; // Time spent sleeping the thread
LinkedList<Long> splitDeltaTime = new LinkedList<>(); // Used for calculating the delta time (render time average over one second)
long reportDuration = System.currentTimeMillis() + 1000; // Used for determining when to report frame count and delta time
double deltaTime; // Contains the average render time over one second (delta time)
// Check if delta time and frame count shall be printed to console.
// Unless this code is ran 292 billion years into the future,
// this should sufficiently disable the reporting feature.
if (!RenderingSubsystemConfiguration.getInstance().isDebugFrames())
reportDuration = Long.MAX_VALUE;
// Run while the 'output' is empty
while (output.get().isEmpty()) {
renderTime = Miscellaneous.measureExecutionTime(() -> {
output.set(renderWindows());
frameCode.run();
});
if (RenderingSubsystemConfiguration.getInstance().getVsyncMode() != VsyncMode.OFF)
// V-Sync is enabled, no need for manual busy waiting
sleepDuration = 0L;
else
// Calculate amount of time the thread should spend sleeping
sleepDuration = (long) (1d / RenderingSubsystemConfiguration.getInstance().getMaximumFramesPerSecond() * 1000d) - renderTime;
// Add render and sleep time to list used for calculating the delta time value
splitDeltaTime.add(renderTime + sleepDuration);
// Busy wait unless V-Sync is enabled
if (RenderingSubsystemConfiguration.getInstance().getVsyncMode() == VsyncMode.OFF && RenderingSubsystemConfiguration.getInstance().getMaximumFramesPerSecond() >= 1) {
sleepDuration += System.currentTimeMillis();
while (System.currentTimeMillis() < sleepDuration)
Thread.onSpinWait();
}
// Calculate delta time and frame count every second
if (System.currentTimeMillis() >= reportDuration) {
deltaTime = Math.getMeanLong(splitDeltaTime); // Calculate delta time
Logger.diag("Delta time average: " + deltaTime + " | Frames/s: " + 1000 / deltaTime); // Print delta time and frame count to console
reportDuration = System.currentTimeMillis() + 1000; // Update 'reportDuration'
splitDeltaTime.clear(); // Clear 'splitDeltaTime' list
}
}
return output.get();
}
// -----> Utility methods
/**
* Checks if the specified platform is compatible,
* and if so, specifies it as the platform to use.
*
* @param platform platform to try
* @since v1-alpha9
*/
private void tryPlatform(int platform) {
if (glfwPlatformSupported(platform))
glfwInitHint(GLFW_PLATFORM, platform);
else
glfwInitHint(GLFW_PLATFORM, GLFW_ANY_PLATFORM);
}
}

View file

@ -0,0 +1,311 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.rendering;
import de.staropensource.engine.base.implementable.Configuration;
import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.utility.PropertiesReader;
import de.staropensource.engine.rendering.event.RenderingErrorEvent;
import de.staropensource.engine.rendering.type.window.RenderingPlatform;
import de.staropensource.engine.rendering.type.window.VsyncMode;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Provides the configuration for the rendering subsystem.
*
* @since v1-alpha9
*/
@Getter
@SuppressWarnings({ "JavadocDeclaration" })
public final class RenderingSubsystemConfiguration extends Configuration {
/**
* Contains the class instance.
*
* @since v1-alpha9
* -- GETTER --
* Returns the class instance.
*
* @return class instance unless {@link RenderingSubsystem} is uninitialized
* @since v1-alpha9
*/
@Getter
private static RenderingSubsystemConfiguration instance;
/**
* Contains the configuration prefix.
*
* @since v1-alpha9
* -- GETTER --
* Returns the configuration prefix.
*
* @return property group
* @since v1-alpha9
*/
@Getter
public final @NotNull String group = "sosengine.rendering.";
/**
* Contains if debugging options should be allowed.
* All debugging options will be forcefully set to
* {@code false} if this option is set to {@code false}.
*
* @since v1-alpha9
* -- GETTER --
* Returns if debugging options should be allowed.
* All debugging options will be forcefully set to
* {@code false} if this option is set to {@code false}.
*
* @return debugging enabled?
* @since v1-alpha9
*/
private boolean debug;
/**
* Contains whether or not key presses
* and releases should be logged.
*
* @since v1-alpha9
* -- GETTER --
* Returns whether or not key presses
* and releases should be logged.
*
* @return log key presses and releases?
* @since v1-alpha9
*/
private boolean debugInput;
/**
* Contains whether or not the delta time and
* FPS count should be logged to the console
* every second.
* <p>
* Changes will no longer be picked up as
* soon as the rendering loop is running.
*
* @since v1-alpha9
* -- GETTER --
* Returns whether or not the delta time and
* FPS count should be logged to the console
* every second.
* <p>
* Changes will no longer be picked up as
* soon as the rendering loop is running.
*
* @return print delta time and FPS count?
* @since v1-alpha9
*/
private boolean debugFrames;
/**
* Contains the platform GLFW shall try initialising.
*
* @since v1-alpha9
* -- GETTER --
* Returns the platform GLFW shall try initialising.
*
* @return GLFW platform
* @since v1-alpha9
*/
private RenderingPlatform initialPlatform;
/**
* Contains whether or not to disable support for
* <a href="https://gitlab.freedesktop.org/libdecor/libdecor">libdecor</a>.
* <p>
* Only affects the {@link RenderingPlatform#WAYLAND} platform.
*
* @since v1-alpha9
* -- GETTER --
* Returns whether or not to disable support for
* <a href="https://gitlab.freedesktop.org/libdecor/libdecor">libdecor</a>.
* <p>
* Only affects the {@link RenderingPlatform#WAYLAND} platform.
*
* @return libdecor support disabled?
* @since v1-alpha9
*/
private boolean initialDisableLibdecor;
/**
* Contains whether or not rendering
* errors should be logged.
* <p>
* The {@link RenderingErrorEvent} will
* be emitted anyway, regardless of the
* value of this variable.
*
* @see RenderingErrorEvent
* @since v1-alpha9
* -- GETTER --
* Returns whether or not rendering
* errors should be logged.
* <p>
* The {@link RenderingErrorEvent} will
* be emitted anyway, regardless of the
* value of this variable.
*
* @return log rendering failures?
* @see RenderingErrorEvent
* @since v1-alpha9
*/
private boolean errorRenderingFailures;
/**
* Contains which {@link VsyncMode} to use.
* <p>
* This setting determines if and how V-Sync
* will operate, which (if enabled) tries to
* synchronize the frame rate to the monitor's
* refresh rate. See {@link VsyncMode}
* for more information.
*
* @see VsyncMode
* @since v1-alpha9
* -- GETTER --
* Returns which {@link VsyncMode} to use.
* <p>
* This setting determines if and how V-Sync
* will operate, which (if enabled) tries to
* synchronize the frame rate to the monitor's
* refresh rate. See {@link VsyncMode}
* for more information.
*
* @return active V-Sync mode
* @see VsyncMode
* @since v1-alpha9
*/
private VsyncMode vsyncMode;
/**
* Contains the maximum value of frames
* which can be rendered per second.
* <p>
* This value will have no effect on
* windows with V-Sync enabled.
* Set to {@code 0} for effectively
* no limit. Not recommended.
*
* @since v1-alpha9
* -- GETTER --
* Returns the maximum value of frames
* which can be rendered per second.
* <p>
* This value will have no effect on
* windows with V-Sync enabled.
* Set to {@code 0} for effectively
* no limit. Not recommended.
*
* @return maximum amount of frames per second
* @since v1-alpha9
*/
private int maximumFramesPerSecond;
/**
* Creates and initializes an instance of this class.
*
* @see RenderingSubsystem
* @since v1-alpha9
*/
RenderingSubsystemConfiguration() {
instance = this;
loadDefaultConfiguration();
}
/** {@inheritDoc} */
@Override
protected void matchProperty(@NotNull PropertiesReader parser, @NotNull String property) {
switch (property) {
case "debug" -> debug = parser.getBoolean(group + property);
case "debugInput" -> debugInput = parser.getBoolean(group + property);
case "debugFrames" -> debugFrames = parser.getBoolean(group + property);
case "initialPlatform" -> {
try {
initialPlatform = RenderingPlatform.valueOf(parser.getString(group + property).toUpperCase());
} catch (IllegalArgumentException ignored) {
Logger.error("Platform " + parser.getString(group + property) + " is not valid");
}
}
case "initialDisableLibdecor" -> initialDisableLibdecor = parser.getBoolean(group + property);
case "errorRenderingFailures" -> errorRenderingFailures = parser.getBoolean(group + property);
case "vsyncMode" -> {
try {
vsyncMode = VsyncMode.valueOf(parser.getString(group + property).toUpperCase());
} catch (IllegalArgumentException exception) {
Logger.error("V-Sync mode " + parser.getString(group + property) + " is not valid");
}
}
case "maximumFramesPerSecond" -> maximumFramesPerSecond = parser.getInteger(group + property, true);
}
}
/** {@inheritDoc} */
@Override
protected void processSettings(@NotNull PropertiesReader parser) {
// Disable all debug options if 'debug' is disabled
if (!debug) {
debugInput = false;
debugFrames = false;
}
}
/** {@inheritDoc} */
@Override
public void loadDefaultConfiguration() {
debug = false;
debugInput = false;
debugFrames = false;
initialPlatform = RenderingPlatform.ANY;
initialDisableLibdecor = false;
errorRenderingFailures = true;
vsyncMode = VsyncMode.ON;
maximumFramesPerSecond = 60;
}
/** {@inheritDoc} */
@Override
public @Nullable Object getSetting(@NotNull String setting) {
switch (setting) {
case "debug" -> { return debug; }
case "debugInput" -> { return debugInput; }
case "debugFrames" -> { return debugFrames; }
case "initialPlatform" -> { return initialPlatform; }
case "disableLibdecor" -> { return initialDisableLibdecor; }
case "errorRenderingFailures" -> { return errorRenderingFailures; }
case "vsyncMode" -> { return vsyncMode; }
case "maximumFramesPerSecond" -> { return maximumFramesPerSecond; }
default -> { return null; }
}
}
}

View file

@ -17,13 +17,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.glfw.callback;
package de.staropensource.engine.rendering.callback;
import de.staropensource.engine.windowing.implementable.Window;
import de.staropensource.engine.windowing.event.InputEvent;
import de.staropensource.engine.windowing.glfw.implementable.WindowCallback;
import de.staropensource.engine.windowing.type.input.Key;
import de.staropensource.engine.windowing.type.input.KeyState;
import de.staropensource.engine.rendering.type.Window;
import de.staropensource.engine.rendering.event.InputEvent;
import de.staropensource.engine.rendering.callback.WindowCallback;
import de.staropensource.engine.rendering.type.input.Key;
import de.staropensource.engine.rendering.type.input.KeyState;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFWKeyCallbackI;
@ -32,7 +32,7 @@ import static org.lwjgl.glfw.GLFW.*;
/**
* A {@link GLFWKeyCallbackI} implementation, which emits {@link InputEvent}.
*
* @since v1-alpha2
* @since v1-alpha9
*/
public final class KeyCallback extends WindowCallback implements GLFWKeyCallbackI {
/**
@ -40,7 +40,7 @@ public final class KeyCallback extends WindowCallback implements GLFWKeyCallback
* and making too many allocations, which would potentially decrease
* performance.
*
* @since v1-alpha2
* @since v1-alpha9
*/
private static final InputEvent event = new InputEvent();
@ -48,7 +48,7 @@ public final class KeyCallback extends WindowCallback implements GLFWKeyCallback
* Creates and initializes an instance of this class.
*
* @param window {@link Window} class
* @since v1-alpha2
* @since v1-alpha9
*/
public KeyCallback(@NotNull Window window) {
super(window);

View file

@ -17,13 +17,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.glfw.callback;
package de.staropensource.engine.rendering.callback;
import de.staropensource.engine.windowing.implementable.Window;
import de.staropensource.engine.windowing.event.InputEvent;
import de.staropensource.engine.windowing.glfw.implementable.WindowCallback;
import de.staropensource.engine.windowing.type.input.Key;
import de.staropensource.engine.windowing.type.input.KeyState;
import de.staropensource.engine.rendering.type.Window;
import de.staropensource.engine.rendering.event.InputEvent;
import de.staropensource.engine.rendering.type.input.Key;
import de.staropensource.engine.rendering.type.input.KeyState;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFWMouseButtonCallbackI;
@ -32,7 +31,7 @@ import static org.lwjgl.glfw.GLFW.*;
/**
* A {@link GLFWMouseButtonCallbackI} implementation, which forward them to {@link InputEvent}.
*
* @since v1-alpha2
* @since v1-alpha9
*/
public final class MouseButtonCallback extends WindowCallback implements GLFWMouseButtonCallbackI {
/**
@ -40,7 +39,7 @@ public final class MouseButtonCallback extends WindowCallback implements GLFWMou
* and making too many allocations, which would potentially decrease
* performance.
*
* @since v1-alpha2
* @since v1-alpha9
*/
private static final InputEvent event = new InputEvent();
@ -48,7 +47,7 @@ public final class MouseButtonCallback extends WindowCallback implements GLFWMou
* Creates and initializes an instance of this class.
*
* @param window {@link Window} class
* @since v1-alpha2
* @since v1-alpha9
*/
public MouseButtonCallback(@NotNull Window window) {
super(window);
@ -72,7 +71,7 @@ public final class MouseButtonCallback extends WindowCallback implements GLFWMou
case GLFW_MOUSE_BUTTON_RIGHT -> Key.MOUSE_RIGHT;
case GLFW_MOUSE_BUTTON_4, GLFW_MOUSE_BUTTON_5,
GLFW_MOUSE_BUTTON_6, GLFW_MOUSE_BUTTON_7,
GLFW_MOUSE_BUTTON_8 -> Key.UNKNOWN_MOUSE;
GLFW_MOUSE_BUTTON_8 -> Key.UNKNOWN_MOUSE_BUTTON;
default -> throw new IllegalStateException("Mouse button " + key + " is invalid");
},
// Key state

View file

@ -17,9 +17,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.glfw.implementable;
package de.staropensource.engine.rendering.callback;
import de.staropensource.engine.windowing.implementable.Window;
import de.staropensource.engine.rendering.type.Window;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
@ -27,7 +27,7 @@ import org.jetbrains.annotations.NotNull;
* Abstract class used for easily implementing
* callbacks which require a {@link Window} instance.
*
* @since v1-alpha2
* @since v1-alpha9
*/
@Getter
@SuppressWarnings({ "JavadocDeclaration" })
@ -36,13 +36,13 @@ public class WindowCallback {
* Refers to the {@link Window} instance
* this callback is tied to.
*
* @since v1-alpha2
* @since v1-alpha9
* -- GETTER --
* Returns the {@link Window} instance
* this callback is tied to.
*
* @return attached {@link Window} instance
* @since v1-alpha2
* @since v1-alpha9
*/
private final @NotNull Window attachedWindow;
@ -50,7 +50,7 @@ public class WindowCallback {
* Creates and initializes an instance of this abstract class.
*
* @param window {@link Window} class
* @since v1-alpha2
* @since v1-alpha9
*/
public WindowCallback(@NotNull Window window) {
this.attachedWindow = window;

View file

@ -20,6 +20,6 @@
/**
* Callbacks, which emit {@link de.staropensource.engine.base.implementable.Event}s.
*
* @since v1-alpha2
* @since v1-alpha9
*/
package de.staropensource.engine.windowing.glfw.callback;
package de.staropensource.engine.rendering.callback;

View file

@ -17,28 +17,28 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.event;
package de.staropensource.engine.rendering.event;
import de.staropensource.engine.base.implementable.Event;
import de.staropensource.engine.base.implementable.helper.EventHelper;
import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.windowing.WindowingSubsystemConfiguration;
import de.staropensource.engine.windowing.implementable.Window;
import de.staropensource.engine.windowing.type.input.Key;
import de.staropensource.engine.windowing.type.input.KeyState;
import de.staropensource.engine.rendering.RenderingSubsystemConfiguration;
import de.staropensource.engine.rendering.type.Window;
import de.staropensource.engine.rendering.type.input.Key;
import de.staropensource.engine.rendering.type.input.KeyState;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Called when a key or button is pressed.
*
* @since v1-alpha2
* @since v1-alpha9
*/
public final class InputEvent implements Event {
/**
* Creates and initializes an instance of this event.
*
* @since v1-alpha2
* @since v1-alpha9
*/
public InputEvent() {}
@ -57,10 +57,10 @@ public final class InputEvent implements Event {
* @param window window the input originated from. May be {@code null}, depending on the windowing API
* @param key key
* @param state key state
* @since v1-alpha0
* @since v1-alpha9
*/
public void callEvent(@Nullable Window window, @NotNull Key key, @NotNull KeyState state) {
if (WindowingSubsystemConfiguration.getInstance().isDebugInput())
if (RenderingSubsystemConfiguration.getInstance().isDebugInput())
Logger.diag("Got input event: window=" + (window == null ? "\\<null>" : window.getUniqueIdentifier()) + " key=" + key.name() + " state=" + state.name());
EventHelper.invokeAnnotatedMethods(getClass(), window, key, state);

View file

@ -17,27 +17,28 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.event;
package de.staropensource.engine.rendering.event;
import de.staropensource.engine.base.implementable.Event;
import de.staropensource.engine.base.implementable.helper.EventHelper;
import org.jetbrains.annotations.NotNull;
/**
* Called when an error occurs in the renderer API (e.g. OpenGL, Vulkan).
* Emitted when a rendering error occurs.
*
* @since v1-alpha0
* @since v1-alpha9
*/
public final class RenderingErrorEvent implements Event {
/**
* Creates and initializes an instance of this event.
*
* @since v1-alpha0
* @since v1-alpha9
*/
public RenderingErrorEvent() {}
/**
* {@inheritDoc}
*
* @deprecated use the {@code callEvent} method with arguments
* @see #callEvent(String)
*/
@ -49,7 +50,7 @@ public final class RenderingErrorEvent implements Event {
* Emits the event and calls all event listeners.
*
* @param error error description
* @since v1-alpha0
* @since v1-alpha9
*/
public void callEvent(@NotNull String error) {
EventHelper.invokeAnnotatedMethods(getClass(), error);

View file

@ -20,6 +20,6 @@
/**
* Events. There's nothing more to say.
*
* @since v1-alpha0
* @since v1-alpha9
*/
package de.staropensource.engine.windowing.event;
package de.staropensource.engine.rendering.event;

View file

@ -17,18 +17,18 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.exception;
package de.staropensource.engine.rendering.exception;
/**
* Thrown when the specified monitor does not exist.
*
* @since v1-alpha2
* @since v1-alpha9
*/
public final class InvalidMonitorException extends RuntimeException {
/**
* Creates and initializes an instance of this exception.
*
* @since v1-alpha2
* @since v1-alpha9
*/
public InvalidMonitorException() {}
}

View file

@ -17,18 +17,18 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.exception;
package de.staropensource.engine.rendering.exception;
/**
* Thrown when trying to access one or more monitors but none are found.
*
* @since v1-alpha2
* @since v1-alpha9
*/
public final class NoMonitorsFoundException extends RuntimeException {
/**
* Creates and initializes an instance of this exception.
*
* @since v1-alpha2
* @since v1-alpha9
*/
public NoMonitorsFoundException() {}
}

View file

@ -17,18 +17,18 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.exception;
package de.staropensource.engine.rendering.exception;
/**
* Thrown when trying to communicate with a windowing API over a non-main thread.
*
* @since v1-alpha2
* @since v1-alpha9
*/
public final class NotOnMainThreadException extends RuntimeException {
/**
* Creates and initializes an instance of this exception.
*
* @since v1-alpha2
* @since v1-alpha9
*/
public NotOnMainThreadException() {}
}

View file

@ -17,21 +17,21 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.exception;
package de.staropensource.engine.rendering.exception;
import de.staropensource.engine.windowing.implementable.Window;
import de.staropensource.engine.rendering.type.Window;
import org.jetbrains.annotations.NotNull;
/**
* Thrown when a {@link Window} cannot be created.
*
* @since v1-alpha2
* @since v1-alpha9
*/
public final class WindowCreationFailureException extends RuntimeException {
/**
* Creates and initializes an instance of this exception.
*
* @since v1-alpha2
* @since v1-alpha9
*/
public WindowCreationFailureException() {}
@ -39,7 +39,7 @@ public final class WindowCreationFailureException extends RuntimeException {
* Creates and initializes an instance of this exception.
*
* @param message error message
* @since v1-alpha2
* @since v1-alpha9
*/
public WindowCreationFailureException(@NotNull String message) {
super(message);

View file

@ -23,6 +23,6 @@
* These aren't meant for the windowing subsystem,
* but instead for windowing APIs, which may throw them.
*
* @since v1-alpha2
* @since v1-alpha9
*/
package de.staropensource.engine.windowing.exception;
package de.staropensource.engine.rendering.exception;

View file

@ -20,6 +20,6 @@
/**
* Code of the windowing subsystem.
*
* @since v1-alpha0
* @since v1-alpha9
*/
package de.staropensource.engine.windowing;
package de.staropensource.engine.rendering;

View file

@ -17,43 +17,45 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.implementable;
package de.staropensource.engine.rendering.type;
import de.staropensource.engine.base.type.vector.Vec2i;
import de.staropensource.engine.windowing.WindowingSubsystem;
import de.staropensource.engine.windowing.exception.InvalidMonitorException;
import de.staropensource.engine.windowing.exception.NoMonitorsFoundException;
import lombok.AccessLevel;
import de.staropensource.engine.rendering.exception.InvalidMonitorException;
import de.staropensource.engine.rendering.exception.NoMonitorsFoundException;
import lombok.Getter;
import lombok.Setter;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.PointerBuffer;
import org.lwjgl.glfw.GLFWVidMode;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.UUID;
import static org.lwjgl.glfw.GLFW.*;
/**
* Abstract class for implementing monitors in a windowing API.
* <p>
* Note that monitors stop working unannounced when disconnected,
* call {@link #isConnected()} before using to avoid unexpected behaviour.
*
* @since v1-alpha2
* @since v1-alpha9
*/
@SuppressWarnings({ "JavadocDeclaration" })
public abstract class Monitor {
public final class Monitor {
/**
* Contains the unique identifier.
* <p>
* This identifier is unique to every monitor and does not change during runtime.
*
* @since v1-alpha2
* @since v1-alpha9
* -- GETTER --
* Returns the unique identifier.
* <p>
* This identifier is unique to every monitor and does not change during runtime.
*
* @return unique identifier
* @since v1-alpha2
* @since v1-alpha9
*/
@Getter
private final UUID uniqueIdentifier = UUID.randomUUID();
@ -63,47 +65,59 @@ public abstract class Monitor {
* <p>
* This identifier is used by the windowing API to refer to a monitor and may change during runtime.
*
* @since v1-alpha1
* @since v1-alpha9
* -- GETTER --
* Returns the monitor identifier.
* <p>
* This identifier is used by the windowing API to refer to a monitor and may change during runtime.
*
* @return monitor identifier
* @since v1-alpha2
* @since v1-alpha9
* -- SETTER --
* Sets the monitor identifier.
* <p>
* This identifier is used by the windowing API to refer to a monitor and may change during runtime.
*
* @param identifier new monitor identifier
* @since v1-alpha2
* @since v1-alpha9
*/
@Setter(AccessLevel.PROTECTED)
private String identifier = null;
private final long identifier;
/**
* Creates and initializes an instance of this abstract class.
*
* @since v1-alpha2
* @throws InvalidMonitorException if the monitor isn't connected
* @since v1-alpha9
*/
public Monitor() {}
public Monitor(long identifier) throws InvalidMonitorException {
this.identifier = identifier;
/**
* Returns all connected monitors.
*
* @return connected monitors
* @since v1-alpha2
*/
public static @NotNull LinkedHashSet<@NotNull Monitor> getMonitors() throws NoMonitorsFoundException {
return WindowingSubsystem.getInstance().getApi().getInternalApi().getMonitors();
checkConnected();
}
/**
* Checks if the monitor is actually connected.
* Returns a set of all connected monitors.
*
* @return connected monitors
* @since v1-alpha9
*/
public static @NotNull LinkedHashSet<@NotNull Monitor> getMonitors() throws NoMonitorsFoundException {
PointerBuffer monitors = glfwGetMonitors();
LinkedHashSet<@NotNull Monitor> output = new LinkedHashSet<>();
if (monitors == null)
throw new NoMonitorsFoundException();
while (monitors.hasRemaining())
output.add(new Monitor(monitors.get()));
return output;
}
/**
* Checks if this monitor is actually connected.
* If not, throws an {@link InvalidMonitorException}.
*
* @since v1-alpha2
* @since v1-alpha9
*/
public void checkConnected() throws InvalidMonitorException, NoMonitorsFoundException {
if (!isConnected())
@ -111,34 +125,48 @@ public abstract class Monitor {
}
/**
* Checks if the monitor is connected or not.
* Checks if this monitor is connected.
*
* @return connection status
* @since v1-alpha2
* @since v1-alpha9
*/
public abstract boolean isConnected() throws NoMonitorsFoundException;
public boolean isConnected() throws NoMonitorsFoundException {
return glfwGetMonitorName(identifier) != null;
}
/**
* Returns the monitor name.
* Returns the name of this monitor.
*
* @return monitor name
* @since v1-alpha2
* @since v1-alpha9
*/
public abstract @NotNull String getName() throws InvalidMonitorException, NoMonitorsFoundException;
public @NotNull String getName() throws InvalidMonitorException, NoMonitorsFoundException {
checkConnected();
return Objects.requireNonNull(glfwGetMonitorName(identifier));
}
/**
* Returns the monitor size.
* Returns size of this monitor.
*
* @return monitor size
* @since v1-alpha2
* @since v1-alpha9
*/
public abstract @NotNull Vec2i getSize() throws InvalidMonitorException, NoMonitorsFoundException;
public @NotNull Vec2i getSize() throws InvalidMonitorException, NoMonitorsFoundException {
checkConnected();
GLFWVidMode videoMode = Objects.requireNonNull(glfwGetVideoMode(identifier));
return new Vec2i(videoMode.width(), videoMode.height());
}
/**
* Returns the monitor refresh rate.
* Returns refresh rate of this monitor in hertz.
*
* @return monitor refresh rate
* @since v1-alpha2
* @since v1-alpha9
*/
public abstract short getRefreshRate() throws InvalidMonitorException, NoMonitorsFoundException;
public short getRefreshRate() throws InvalidMonitorException, NoMonitorsFoundException {
checkConnected();
return (short) Objects.requireNonNull(glfwGetVideoMode(identifier)).refreshRate();
}
}

View file

@ -17,590 +17,590 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.type.input;
package de.staropensource.engine.rendering.type.input;
/**
* Contains a list of keys which can be recognized by the engine.
*
* @since v1-alpha2
* @since v1-alpha9
*/
public enum Key {
/**
* An unknown key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
UNKNOWN_KEY,
/**
* An unknown mouse button.
*
* @since v1-alpha2
* @since v1-alpha9
*/
UNKNOWN_MOUSE,
UNKNOWN_MOUSE_BUTTON,
/**
* The left mouse button.
*
* @since v1-alpha2
* @since v1-alpha9
*/
MOUSE_LEFT,
/**
* The middle mouse button.
*
* @since v1-alpha2
* @since v1-alpha9
*/
MOUSE_MIDDLE,
/**
* The right mouse button.
*
* @since v1-alpha2
* @since v1-alpha9
*/
MOUSE_RIGHT,
/**
* The {@code ALT} modifier key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
ALT,
/**
* The {@code '} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
APOSTROPHE,
/**
* The {@code DOWN} arrow key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
ARROW_DOWN,
/**
* The {@code LEFT} arrow key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
ARROW_LEFT,
/**
* The {@code RIGHT} arrow key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
ARROW_RIGHT,
/**
* The {@code UP} arrow key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
ARROW_UP,
/**
* The {@code \} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
BACKSLASH,
/**
* The {@code BACKSPACE} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
BACKSPACE,
/**
* The left {@code [} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
BRACKET_LEFT,
/**
* The right {@code ]} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
BRACKET_RIGHT,
/**
* THE {@code CAPSLOCK} KEY.
*
* @since v1-alpha2
* @since v1-alpha9
*/
CAPS_LOCK,
/**
* The {@code ,} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
COMMA,
/**
* The left {@code CTRL} modifier key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
CONTROL_LEFT,
/**
* The right {@code CTRL} modifier key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
CONTROL_RIGHT,
/**
* The {@code DEL} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
DELETE,
/**
* The {@code END} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
END,
/**
* The {@code ENTER} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
ENTER,
/**
* The {@code =} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
EQUAL,
/**
* The {@code ESC} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
ESCAPE,
/**
* The {@code F1} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_1,
/**
* The {@code F2} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_2,
/**
* The {@code F3} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_3,
/**
* The {@code F4} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_4,
/**
* The {@code F5} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_5,
/**
* The {@code F6} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_6,
/**
* The {@code F7} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_7,
/**
* The {@code F8} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_8,
/**
* The {@code F9} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_9,
/**
* The {@code F10} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_10,
/**
* The {@code F11} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_11,
/**
* The {@code F12} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_12,
/**
* The {@code F13} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_13,
/**
* The {@code F14} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_14,
/**
* The {@code F15} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_15,
/**
* The {@code F16} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_16,
/**
* The {@code F17} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_17,
/**
* The {@code F18} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_18,
/**
* The {@code F19} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_19,
/**
* The {@code F20} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_20,
/**
* The {@code F21} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_21,
/**
* The {@code F22} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_22,
/**
* The {@code F23} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_23,
/**
* The {@code F24} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_24,
/**
* The {@code F25} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
FUNCTION_25,
/**
* The {@code `} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
GRAVE,
/**
* The {@code HOME} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
HOME,
/**
* THe {@code INS} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
INSERT,
/**
* The {@code +} key on your keypad.
*
* @since v1-alpha2
* @since v1-alpha9
*/
KEYPAD_ADD,
/**
* The {@code -} key on your keypad.
*
* @since v1-alpha2
* @since v1-alpha9
*/
KEYPAD_DECIMAL,
/**
* The {@code /} key on your keypad.
*
* @since v1-alpha2
* @since v1-alpha9
*/
KEYPAD_DIVIDE,
/**
* The {@code ENTER} key on your keypad.
*
* @since v1-alpha2
* @since v1-alpha9
*/
KEYPAD_ENTER,
/**
* The {@code =} key on your keypad.
*
* @since v1-alpha2
* @since v1-alpha9
*/
KEYPAD_EQUAL,
/**
* The {@code *} key on your keypad.
*
* @since v1-alpha2
* @since v1-alpha9
*/
KEYPAD_MULTIPLY,
/**
* The number {@code 0} key on your keypad.
*
* @since v1-alpha2
* @since v1-alpha9
*/
KEYPAD_NUMBER_0,
/**
* The number {@code 1} key on your keypad.
*
* @since v1-alpha2
* @since v1-alpha9
*/
KEYPAD_NUMBER_1,
/**
* The number {@code 2} key on your keypad.
*
* @since v1-alpha2
* @since v1-alpha9
*/
KEYPAD_NUMBER_2,
/**
* The number {@code 3} key on your keypad.
*
* @since v1-alpha2
* @since v1-alpha9
*/
KEYPAD_NUMBER_3,
/**
* The number {@code 4} key on your keypad.
*
* @since v1-alpha2
* @since v1-alpha9
*/
KEYPAD_NUMBER_4,
/**
* The number {@code 5} key on your keypad.
*
* @since v1-alpha2
* @since v1-alpha9
*/
KEYPAD_NUMBER_5,
/**
* The number {@code 6} key on your keypad.
*
* @since v1-alpha2
* @since v1-alpha9
*/
KEYPAD_NUMBER_6,
/**
* The number {@code 7} key on your keypad.
*
* @since v1-alpha2
* @since v1-alpha9
*/
KEYPAD_NUMBER_7,
/**
* The number {@code 8} key on your keypad.
*
* @since v1-alpha2
* @since v1-alpha9
*/
KEYPAD_NUMBER_8,
/**
* The number {@code 9} key on your keypad.
*
* @since v1-alpha2
* @since v1-alpha9
*/
KEYPAD_NUMBER_9,
/**
* The {@code -} key on your keypad.
*
* @since v1-alpha2
* @since v1-alpha9
*/
KEYPAD_SUBTRACT,
/**
* The letter {@code A} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_A,
/**
* The letter {@code B} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_B,
/**
* The letter {@code C} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_C,
/**
* The letter {@code D} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_D,
/**
* The letter {@code E} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_E,
/**
* The letter {@code F} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_F,
/**
* The letter {@code G} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_G,
/**
* The letter {@code H} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_H,
/**
* The letter {@code I} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_I,
/**
* The letter {@code J} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_J,
/**
* The letter {@code K} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_K,
/**
* The letter {@code L} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_L,
/**
* The letter {@code M} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_M,
/**
* The letter {@code N} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_N,
/**
* The letter {@code O} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_O,
/**
* The letter {@code P} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_P,
/**
* The letter {@code Q} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_Q,
/**
* The letter {@code R} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_R,
/**
* The letter {@code S} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_S,
/**
* The letter {@code T} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_T,
/**
* The letter {@code U} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_U,
/**
* The letter {@code V} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_V,
/**
* The letter {@code W} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_W,
/**
* The letter {@code X} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_X,
/**
* The letter {@code Y} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_Y,
/**
* The letter {@code Z} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
LETTER_Z,
/**
* The {@code MENU} key, which brings up the right click menu.
*
* @since v1-alpha2
* @since v1-alpha9
*/
MENU,
/**
@ -608,151 +608,151 @@ public enum Key {
* <p>
* Windows users will recognize this key as the Windows key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
META,
/**
* The {@code -} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
MINUS,
/**
* The number {@code 0}.
*
* @since v1-alpha2
* @since v1-alpha9
*/
NUMBER_0,
/**
* The number {@code 1}.
*
* @since v1-alpha2
* @since v1-alpha9
*/
NUMBER_1,
/**
* The number {@code 2}.
*
* @since v1-alpha2
* @since v1-alpha9
*/
NUMBER_2,
/**
* The number {@code 3}.
*
* @since v1-alpha2
* @since v1-alpha9
*/
NUMBER_3,
/**
* The number {@code 4}.
*
* @since v1-alpha2
* @since v1-alpha9
*/
NUMBER_4,
/**
* The number {@code 5}.
*
* @since v1-alpha2
* @since v1-alpha9
*/
NUMBER_5,
/**
* The number {@code 6}.
*
* @since v1-alpha2
* @since v1-alpha9
*/
NUMBER_6,
/**
* The number {@code 7}.
*
* @since v1-alpha2
* @since v1-alpha9
*/
NUMBER_7,
/**
* The number {@code 8}.
*
* @since v1-alpha2
* @since v1-alpha9
*/
NUMBER_8,
/**
* The number {@code 9}.
*
* @since v1-alpha2
* @since v1-alpha9
*/
NUMBER_9,
/**
* The {@code NUM} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
NUM_LOCK,
/**
* The {@code PAGE DOWN} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
PAGE_DOWN,
/**
* The {@code PAGE UP} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
PAGE_UP,
/**
* The {@code PAUSE} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
PAUSE,
/**
* The {@code .} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
PERIOD,
/**
* The {@code PRINT} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
PRINT,
/**
* The {@code SCROLL} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
SCROLL_LOCK,
/**
* The {@code ;} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
SEMICOLON,
/**
* The left {@code SHIFT} modifier key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
SHIFT_LEFT,
/**
* The right {@code SHIFT} modifier key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
SHIFT_RIGHT,
/**
* The {@code /} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
SLASH,
/**
* The {@code ENTER} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
SPACE,
/**
* The {@code TAB} key.
*
* @since v1-alpha2
* @since v1-alpha9
*/
TAB,
}

View file

@ -17,26 +17,26 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.type.input;
package de.staropensource.engine.rendering.type.input;
/**
* Contains in which state a key is.
*
* @since v1-alpha2
* @since v1-alpha9
*/
@SuppressWarnings({ "unused" })
public enum KeyState {
/**
* Indicates that a key is pressed.
*
* @since v1-alpha2
* @since v1-alpha9
*/
PRESSED,
/**
* Indicates that a key is released.
*
* @since v1-alpha2
* @since v1-alpha9
*/
RELEASED,
}

View file

@ -20,6 +20,6 @@
/**
* Data types related to input.
*
* @since v1-alpha2
* @since v1-alpha9
*/
package de.staropensource.engine.windowing.type.input;
package de.staropensource.engine.rendering.type.input;

View file

@ -20,6 +20,6 @@
/**
* Data types in form of enums and classes.
*
* @since v1-alpha1
* @since v1-alpha9
*/
package de.staropensource.engine.windowing.type;
package de.staropensource.engine.rendering.type;

View file

@ -17,53 +17,53 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.glfw.type;
package de.staropensource.engine.rendering.type.window;
/**
* Contains all available platforms which GLFW can be initialized with.
* Represents all available platforms.
*
* @since v1-alpha2
* @since v1-alpha9
*/
public enum GlfwPlatform {
public enum RenderingPlatform {
/**
* Allows GLFW to autodetect the platform to use.
* Allows the subsystem to autodetect the platform to use.
*
* @since v1-alpha2
* @since v1-alpha9
*/
ANY,
/**
* Prefer initializing with the Wayland platform.
*
* @since v1-alpha2
* @since v1-alpha9
*/
WAYLAND,
/**
* Prefer initializing with the X11 platform.
*
* @since v1-alpha2
* @since v1-alpha9
*/
X11,
/**
* Prefer initializing with the Win32 platform.
*
* @since v1-alpha2
* @since v1-alpha9
*/
WIN32,
/**
* Prefer initializing with the Cocoa platform.
*
* @since v1-alpha2
* @since v1-alpha9
*/
COCOA,
/**
* Prefer initializing without any platform.
*
* @since v1-alpha2
* @since v1-alpha9
*/
NONE
}

View file

@ -17,29 +17,30 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.type.window;
package de.staropensource.engine.rendering.type.window;
import de.staropensource.engine.windowing.WindowingSubsystemConfiguration;
import de.staropensource.engine.rendering.RenderingSubsystemConfiguration;
/**
* Controls how <a href="https://en.wikipedia.org/wiki/Screen_tearing#Vertical_synchronization">V-Sync</a> operates.
*
* @since v1-alpha1
* @since v1-alpha9
*/
public enum VsyncMode {
/**
* Disables V-Sync. The frame rate will be uncapped and will allow
* for processing an unlimited amount of frames (if not limited by
* {@link WindowingSubsystemConfiguration#maximumFramesPerSecond}).
* {@link RenderingSubsystemConfiguration#maximumFramesPerSecond}).
*
* @since v1-alpha1
* @since v1-alpha9
*/
OFF,
/**
* Enables V-Sync and will cap the window's frame rate at the refresh rate of the target monitor.
* Enables V-Sync and will cap the window's frame rate
* at the refresh rate of the target monitor.
*
* @since v1-alpha1
* @since v1-alpha9
*/
ON
}

View file

@ -17,18 +17,18 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.type.window;
package de.staropensource.engine.rendering.type.window;
/**
* Contains how a window should be displayed.
*
* @since v1-alpha1
* @since v1-alpha9
*/
public enum WindowMode {
/**
* Marks the window as hidden, making it invisible and unable to be interacted with.
*
* @since v1-alpha2
* @since v1-alpha9
*/
HIDDEN,
@ -36,7 +36,7 @@ public enum WindowMode {
* Marks the window as windowed, which
* will allow the user to drag around the window freely.
*
* @since v1-alpha1
* @since v1-alpha9
*/
WINDOWED,
@ -45,7 +45,7 @@ public enum WindowMode {
* summoned back into {@link #WINDOWED} mode by the user
* by (for example) clicking an icon or {@code ALT+TAB}-ing.
*
* @since v1-alpha2
* @since v1-alpha9
*/
MINIMIZED,
@ -53,7 +53,7 @@ public enum WindowMode {
* Same as {@link #WINDOWED}, but will make the window occupy
* most of the screen space, except for windows/bars/docks.
*
* @since v1-alpha2
* @since v1-alpha9
*/
MAXIMIZED,
@ -61,20 +61,25 @@ public enum WindowMode {
* Makes the window will have the same
* size as the monitor it is currently on.
*
* @since v1-alpha1
* @since v1-alpha9
*/
BORDERLESS_FULLSCREEN,
/**
* Makes the window occupy the entire monitor it is currently on
* without allowing other windows to occupy the same space.
* Makes the window occupy the entire
* monitor it is currently on without
* allowing other windows to occupy
* the same space.
* <p>
* This will increase rendering throughput as the window manager
* or compositor does not need to care about other windows occupying
* the same monitor. Use (and recommend) this mode if you/your users
* This will increase rendering
* throughput as the window manager
* or compositor does not need to
* care about other windows occupying
* the same monitor. Use (and recommend)
* this mode if you/your users
* want more frames per second.
*
* @since v1-alpha1
* @since v1-alpha9
*/
EXCLUSIVE_FULLSCREEN
}

View file

@ -20,6 +20,6 @@
/**
* Data types related to windows.
*
* @since v1-alpha1
* @since v1-alpha9
*/
package de.staropensource.engine.windowing.type.window;
package de.staropensource.engine.rendering.type.window;

View file

@ -0,0 +1,33 @@
/**
* The {@code rendering} subsystem, responsible for
* initializing and managing windows and rendering APIs.
*
* @since v1-alpha9
*/
module sosengine.rendering {
// Dependencies
// -> Engine
requires transitive sosengine.base;
// -> Libraries
requires transitive static lombok;
requires transitive org.jetbrains.annotations;
requires org.lwjgl.stb;
requires org.lwjgl.glfw;
requires org.lwjgl.bgfx;
// API access
exports de.staropensource.engine.rendering;
exports de.staropensource.engine.rendering.event;
exports de.staropensource.engine.rendering.exception;
exports de.staropensource.engine.rendering.type;
exports de.staropensource.engine.rendering.type.input;
exports de.staropensource.engine.rendering.type.window;
// Reflection access
opens de.staropensource.engine.rendering;
opens de.staropensource.engine.rendering.event;
opens de.staropensource.engine.rendering.exception;
opens de.staropensource.engine.rendering.type;
opens de.staropensource.engine.rendering.type.input;
opens de.staropensource.engine.rendering.type.window;
}

View file

@ -17,13 +17,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
rootProject.setName("sosengine")
rootProject.setName("sos!engine")
include("base")
include("testing")
include("ansi")
include("slf4j-compat")
include("notification")
include("windowing")
include("windowing:glfw")
include("rendering")
include("testapp")

View file

@ -39,10 +39,9 @@ dependencies {
// Project
implementation(project(":base"))
implementation(project(":windowing"))
implementation(project(":rendering"))
runtimeOnly(project(":ansi"))
runtimeOnly(project(":slf4j-compat"))
runtimeOnly(project(":windowing:glfw"))
}
// Fix delombok task

View file

@ -26,11 +26,11 @@ import de.staropensource.engine.base.implementable.helper.EventHelper;
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.WindowingSubsystem;
import de.staropensource.engine.windowing.event.InputEvent;
import de.staropensource.engine.windowing.implementable.Window;
import de.staropensource.engine.windowing.type.input.Key;
import de.staropensource.engine.windowing.type.input.KeyState;
import de.staropensource.engine.rendering.RenderingSubsystem;
import de.staropensource.engine.rendering.event.InputEvent;
import de.staropensource.engine.rendering.type.Window;
import de.staropensource.engine.rendering.type.input.Key;
import de.staropensource.engine.rendering.type.input.KeyState;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -119,10 +119,6 @@ public final class Main {
// Say hello to the world!
Logger.info("Hello world!");
// Choose windowing API to use
if (!WindowingSubsystem.getInstance().setApi())
Logger.crash("No windowing API is compatible");
// Create window
Window window;
try {
@ -139,12 +135,13 @@ public final class Main {
return;
}
if (window == null)
Logger.crash("'window' is null");
// Render loop
LinkedHashMap<@NotNull Window, @NotNull Throwable> renderLoopFailures = WindowingSubsystem
LinkedHashMap<@NotNull Window, @NotNull Throwable> renderLoopFailures = RenderingSubsystem
.getInstance()
.getApi()
.getManagement()
.runRenderLoopContinuously(() -> {
.runRenderLoop(() -> {
if (shutdown || window.isClosureRequested())
Engine.getInstance().shutdown();
});

View file

@ -7,5 +7,5 @@
open module sosengine.testapp {
// Dependencies
// -> Engine
requires sosengine.windowing;
requires sosengine.rendering;
}

View file

@ -1,109 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// Plugins
plugins {
id("java")
id("io.freefair.lombok") version("${pluginLombok}")
id("maven-publish")
}
// Dependencies
dependencies {
// Lombok
compileOnly("org.projectlombok:lombok:${dependencyLombok}")
annotationProcessor("org.projectlombok:lombok:${dependencyLombok}")
// JetBrains Annotations
compileOnly("org.jetbrains:annotations:${dependencyJetbrainsAnnotations}")
// Project
implementation(project(":base"))
}
// Javadoc configuration
javadoc {
outputs.upToDateWhen { false } // Force task execution
dependsOn(delombok) // Make sure the source is delomboked first
javadoc {
setClasspath(files(project.sourceSets.main.compileClasspath)) // Include dependencies
options {
if (new File(projectDir, "src/main/javadoc/theme.css").exists())
stylesheetFile = new File(projectDir, "src/main/javadoc/theme.css") // Theming is cool :3
setMemberLevel(JavadocMemberLevel.PUBLIC) // Only display public stuff
setOverview("src/main/javadoc/overview.html") // We want a custom overview page to greet the visitor
setLocale("en_US") //
addStringOption("Xwerror", "-quiet") // Fail build on warning
setJFlags([
"-Duser.language=en_US" // See above
])
}
}
}
// Include javadoc and source jar during publishing
java {
withJavadocJar()
withSourcesJar()
}
// Build publishing configuration
// Note: You can safely ignore any errors or warnings thrown by your IDE here
publishing {
repositories {
maven {
name = "staropensource"
url = uri("https://mvn.staropensource.de/engine")
credentials(org.gradle.api.credentials.PasswordCredentials)
authentication {
//noinspection GroovyAssignabilityCheck
basic (BasicAuthentication)
}
}
}
publications {
//noinspection GroovyAssignabilityCheck
maven (MavenPublication) {
groupId = group
artifactId = project.getName()
version = version
//noinspection GroovyAssignabilityCheck
from components.java
}
}
}
// Fix delombok task
delombok.doFirst {
File target = file("${project.projectDir}/src/main/module-info.java")
File source = file("${project.projectDir}/src/main/java/module-info.java")
target.delete()
source.renameTo(target)
}
delombok.doLast {
File target = file("${project.projectDir}/src/main/java/module-info.java")
File source = file("${project.projectDir}/src/main/module-info.java")
target.delete()
source.renameTo(target)
}

View file

@ -1,2 +0,0 @@
# The `glfw` subsystem
This subsystem provides a Windowing API using [LWJGL](https://lwjgl.org)'s [GLFW](https://glfw.org) bindings.

View file

@ -1 +0,0 @@
../../gradle

View file

@ -1 +0,0 @@
../../gradlew

View file

@ -1 +0,0 @@
../../gradlew.bat

View file

@ -1,218 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.glfw;
import de.staropensource.engine.base.annotation.EngineSubsystem;
import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.utility.information.EngineInformation;
import de.staropensource.engine.base.implementation.versioning.StarOpenSourceVersioningSystem;
import de.staropensource.engine.base.type.DependencyVector;
import de.staropensource.engine.base.utility.Miscellaneous;
import de.staropensource.engine.windowing.WindowingSubsystem;
import de.staropensource.engine.windowing.implementable.api.ApiClass;
import de.staropensource.engine.windowing.implementable.api.ApiInternalClass;
import de.staropensource.engine.windowing.implementable.api.ApiManagementClass;
import de.staropensource.engine.windowing.event.WindowingErrorEvent;
import de.staropensource.engine.windowing.exception.NotOnMainThreadException;
import de.staropensource.engine.windowing.glfw.implementation.GlfwInternalClass;
import de.staropensource.engine.windowing.glfw.implementation.GlfwManagementClass;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWErrorCallbackI;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import static org.lwjgl.glfw.GLFW.*;
/**
* The main class of the GLFW subsystem.
*
* @since v1-alpha2
*/
@EngineSubsystem
@SuppressWarnings({ "JavadocDeclaration" })
public final class GlfwSubsystem extends ApiClass {
/**
* Contains the class instance.
*
* @since v1-alpha0
* -- GETTER --
* Returns the class instance.
*
* @return class instance unless the subsystem is uninitialized
* @since v1-alpha0
*/
@Getter
private static GlfwSubsystem instance = null;
/**
* Contains the internal API class.
*
* @see ApiInternalClass
* @since v1-alpha4
* -- GETTER --
* {@inheritDoc}
*/
@Getter
private ApiInternalClass internalApi;
/**
* Contains the management class.
*
* @see ApiManagementClass
* @since v1-alpha4
* -- GETTER --
* {@inheritDoc}
*/
@Getter
private ApiManagementClass management;
/**
* The {@link GLFWErrorCallback} to use.
* <p>
* Only declared publicly for freeing during engine shutdown.
*
* @since v1-alpha2
*/
private GLFWErrorCallback errorCallback = null;
/**
* Initializes this subsystem.
*
* @since v1-alpha2
*/
public GlfwSubsystem() {
// Check if subsystem has already initialized
if (instance == null)
instance = this;
else
Logger.crash("The subsystem tried to initialize twice");
}
/** {@inheritDoc} */
@Override
public void initializeSubsystem() {
// Initialize configuration
new GlfwSubsystemConfiguration();
// Register API
WindowingSubsystem.getInstance().registerApi(this);
}
/** {@inheritDoc} */
@Override
public void initializeApi() {
Logger.verb("Initializing GLFW");
try {
if (!Miscellaneous.onMainThread()) {
Logger.crash("Unable to initialize GLFW on a non-main thread", new NotOnMainThreadException(), true);
return;
}
// Set error callback
errorCallback = GLFWErrorCallback.create(new GLFWErrorCallbackI() {
/**
* {@inheritDoc}
*/
@Override
public void invoke(int error, long description) {
new WindowingErrorEvent().callEvent(description + " (" + error + ")");
}
}).set();
// Set init hints
switch (GlfwSubsystemConfiguration.getInstance().getPlatform()) {
case ANY -> glfwInitHint(GLFW_PLATFORM, GLFW_ANY_PLATFORM);
case WAYLAND -> tryPlatform(GLFW_PLATFORM_WAYLAND);
case X11 -> tryPlatform(GLFW_PLATFORM_X11);
case WIN32 -> tryPlatform(GLFW_PLATFORM_WIN32);
case COCOA -> tryPlatform(GLFW_PLATFORM_COCOA);
case NONE -> glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_NULL);
}
glfwInitHint(GLFW_WAYLAND_LIBDECOR, GlfwSubsystemConfiguration.getInstance().isDisableLibdecor() ? GLFW_WAYLAND_DISABLE_LIBDECOR : GLFW_WAYLAND_PREFER_LIBDECOR);
// Initialize GLFW
if (!glfwInit())
Logger.crash("Failed to initialize GLFW");
// Initialize classes
internalApi = new GlfwInternalClass();
management = new GlfwManagementClass();
} catch (UnsatisfiedLinkError error) {
Logger.crash("Failed to load LWJGL native libraries", error);
}
}
/** {@inheritDoc} */
@Override
public void shutdownApi() {
Logger.verb("Terminating GLFW");
errorCallback.free();
if (Miscellaneous.onMainThread())
glfwTerminate();
else
Logger.crash("Unable to terminate GLFW on a non-main thread. Did you call Engine#shutdown or Logger#crash from another thread?", new NotOnMainThreadException(), true);
}
/** {@inheritDoc} */
@Override
public @NotNull String getName() {
return getApiName().toLowerCase(Locale.ROOT);
}
/** {@inheritDoc} */
@Override
public String getApiName() {
return "GLFW";
}
/** {@inheritDoc} */
@Override
public @NotNull DependencyVector getDependencyVector() {
Set<@NotNull String> dependencies = new HashSet<>();
dependencies.add("windowing");
return new DependencyVector.Builder()
.setIdentifier(getName())
.setVersioningSystem(StarOpenSourceVersioningSystem.class)
.setVersion(EngineInformation.getVersioningString())
.setDependencies(dependencies)
.build();
}
/**
* Checks if the specified platform is compatible,
* and if so, specifies it as the platform to use.
*
* @param platform platform to try
* @since v1-alpha2
*/
private void tryPlatform(int platform) {
if (glfwPlatformSupported(platform))
glfwInitHint(GLFW_PLATFORM, platform);
else
glfwInitHint(GLFW_PLATFORM, GLFW_ANY_PLATFORM);
}
}

View file

@ -1,139 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.glfw;
import de.staropensource.engine.base.implementable.Configuration;
import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.utility.PropertiesReader;
import de.staropensource.engine.windowing.glfw.type.GlfwPlatform;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Provides the GLFW subsystem configuration.
*
* @since v1-alpha2
*/
@Getter
@SuppressWarnings({ "JavadocDeclaration" })
public final class GlfwSubsystemConfiguration extends Configuration {
/**
* Contains the class instance.
*
* @since v1-alpha2
* -- GETTER --
* Returns the class instance.
*
* @return class instance unless {@link GlfwSubsystem} is uninitialized
* @since v1-alpha2
*/
@Getter
private static GlfwSubsystemConfiguration instance;
/**
* Defines prefix properties must begin with.
*
* @since v1-alpha2
* -- GETTER --
* Returns prefix properties must begin with.
*
* @return property group
* @since v1-alpha2
*/
private final @NotNull String group = "sosengine.windowing.glfw.";
/**
* Contains the platform GLFW will try to initialize with.
*
* @since v1-alpha2
* -- GETTER --
* Gets the value for {@link #platform}.
*
* @return variable value
* @see #platform
* @since v1-alpha2
*/
private GlfwPlatform platform;
/**
* If {@code true}, will disable support for
* <a href="https://gitlab.freedesktop.org/libdecor/libdecor">libdecor</a>.
* <p>
* Only affects the {@link GlfwPlatform#WAYLAND} platform.
*
* @since v1-alpha2
* -- GETTER --
* Gets the value for {@link #disableLibdecor}.
*
* @return variable value
* @see #disableLibdecor
* @since v1-alpha2
*/
private boolean disableLibdecor;
/**
* Creates and initializes an instance of this class.
*
* @see GlfwSubsystem
* @since v1-alpha6
*/
GlfwSubsystemConfiguration() {
instance = this;
loadDefaultConfiguration();
}
/** {@inheritDoc} */
@Override
protected void matchProperty(@NotNull PropertiesReader parser, @NotNull String property) {
switch (property) {
case "platform" -> {
try {
platform = GlfwPlatform.valueOf(parser.getString(group + property).toUpperCase());
} catch (IllegalArgumentException ignored) {
Logger.error("Platform " + parser.getString(group + property) + " is not valid");
}
}
case "disableLibdecor" -> disableLibdecor = parser.getBoolean(group + property);
}
}
/** {@inheritDoc} */
@Override
protected void processSettings(@NotNull PropertiesReader parser) {}
/** {@inheritDoc} */
@Override
public void loadDefaultConfiguration() {
platform = GlfwPlatform.ANY;
disableLibdecor = false;
}
/** {@inheritDoc} */
@Override
public @Nullable Object getSetting(@NotNull String setting) {
return switch (setting) {
case "platform" -> platform;
case "disableLibdecor" -> disableLibdecor;
default -> null;
};
}
}

View file

@ -1,25 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* Interfaces and abstract classes which can be used for implementing classes.
*
* @since v1-alpha6
*/
package de.staropensource.engine.windowing.glfw.implementable;

View file

@ -1,82 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.glfw.implementation;
import de.staropensource.engine.windowing.implementable.Monitor;
import de.staropensource.engine.windowing.implementable.api.ApiInternalClass;
import de.staropensource.engine.windowing.exception.NoMonitorsFoundException;
import lombok.Getter;
import lombok.Setter;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.PointerBuffer;
import java.util.LinkedHashSet;
import static org.lwjgl.glfw.GLFW.glfwGetMonitors;
/**
* The internal API class for GLFW-powered windowing APIs.
*
* @since v1-alpha2
*/
@Getter
@Setter
@SuppressWarnings({ "JavadocDeclaration" })
public final class GlfwInternalClass implements ApiInternalClass {
/**
* Contains a class which extends the {@link GlfwWindow} class.
*
* @since v1-alpha4
* -- GETTER --
* {@inheritDoc}
* -- SETTER --
* Sets a class which extends the {@link GlfwWindow} class.
*
* @param windowClass new window class
* @since v1-alpha4
*/
private @NotNull Class<? extends GlfwWindow> windowClass = GlfwWindow.class;
/**
* Creates and initializes an instance of this class.
*
* @since v1-alpha2
*/
public GlfwInternalClass() {}
/**
* Returns all connected monitors.
*
* @return connected monitors
* @since v1-alpha2
*/
@Override
public @NotNull LinkedHashSet<@NotNull Monitor> getMonitors() throws NoMonitorsFoundException {
PointerBuffer monitors = glfwGetMonitors();
LinkedHashSet<@NotNull Monitor> output = new LinkedHashSet<>();
if (monitors == null)
throw new NoMonitorsFoundException();
while (monitors.hasRemaining())
output.add(new GlfwMonitor(monitors.get()));
return output;
}
}

View file

@ -1,92 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.glfw.implementation;
import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.utility.Miscellaneous;
import de.staropensource.engine.windowing.implementable.api.ApiManagementClass;
import de.staropensource.engine.windowing.implementable.Window;
import de.staropensource.engine.windowing.exception.NotOnMainThreadException;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import static org.lwjgl.glfw.GLFW.*;
/**
* The abstract management class for GLFW-powered windowing APIs.
*
* @since v1-alpha2
*/
@Getter
public final class GlfwManagementClass extends ApiManagementClass {
/**
* Creates and initializes an instance of this class.
*
* @since v1-alpha2
*/
public GlfwManagementClass() {}
/** {@inheritDoc} */
@Override
public boolean mustRunOnMainThread() {
return true;
}
/** {@inheritDoc} */
@Override
public @NotNull LinkedHashMap<@NotNull Window, @NotNull Throwable> runRenderLoop() {
// Ensure running on the main thread
if (!Miscellaneous.onMainThread())
throw new NotOnMainThreadException();
LinkedHashMap<@NotNull Window, @NotNull Throwable> throwables = new LinkedHashMap<>();
// Update and render all windows
for (Window window : Window.getWindows()) {
if (!window.isRendering())
continue;
try {
window.updateState();
window.render();
} catch (Throwable throwable) {
Logger.error("Rendering window " + window + " failed: Threw throwable " + throwable.getClass().getName() + (throwable.getMessage() == null ? "" : ": " + throwable.getMessage()));
throwables.put(window, throwable);
}
}
// Poll for events
glfwPollEvents();
return throwables;
}
/** {@inheritDoc} */
@Override
public @NotNull LinkedHashMap<@NotNull Window, @NotNull Throwable> runRenderLoopContinuously(@NotNull Runnable frameCode) {
// Ensure running on the main thread
if (!Miscellaneous.onMainThread())
throw new NotOnMainThreadException();
return super.runRenderLoopContinuously(frameCode);
}
}

View file

@ -1,118 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.glfw.implementation;
import de.staropensource.engine.base.type.vector.Vec2i;
import de.staropensource.engine.windowing.implementable.Monitor;
import de.staropensource.engine.windowing.exception.InvalidMonitorException;
import lombok.SneakyThrows;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFWVidMode;
import java.util.Objects;
import static org.lwjgl.glfw.GLFW.*;
/**
* Represents a monitor. A GLFW-powered monitor. Wait... aren't monitors powered by... power?
*
* @since v1-alpha2
*/
@SuppressWarnings({ "JavadocDeclaration" })
public final class GlfwMonitor extends Monitor {
/**
* Contains the {@link #identifier} as a long.
*
* @since v1-alpha2
* -- GETTER --
* Returns the monitor identifier as a long.
*
* @return monitor identifier as a long
* @since v1-alpha2
*/
private final long identifierLong;
/**
* Creates and initializes an instance of this class.
*
* @param identifier glfw monitor pointer
* @since v1-alpha2
*/
@SneakyThrows
public GlfwMonitor(long identifier) throws InvalidMonitorException {
// Set identifier
setIdentifier(String.valueOf(identifier));
identifierLong = identifier;
// Check if connected
checkConnected();
}
/** {@inheritDoc} */
@SneakyThrows
public void checkConnected() throws InvalidMonitorException {
super.checkConnected();
}
/** {@inheritDoc} */
@Override
public boolean isConnected() {
return glfwGetMonitorName(identifierLong) != null;
}
/**
* Returns the monitor name.
*
* @return monitor name
* @since v1-alpha2
*/
@Override
public @NotNull String getName() throws InvalidMonitorException {
checkConnected();
return Objects.requireNonNull(glfwGetMonitorName(identifierLong));
}
/**
* Returns the monitor size.
*
* @return monitor size
* @since v1-alpha2
*/
@Override
public @NotNull Vec2i getSize() throws InvalidMonitorException {
checkConnected();
GLFWVidMode videoMode = Objects.requireNonNull(glfwGetVideoMode(identifierLong));
return new Vec2i(videoMode.width(), videoMode.height());
}
/**
* Returns the monitor refresh rate.
*
* @return monitor refresh rate
* @since v1-alpha2
*/
@Override
public short getRefreshRate() throws InvalidMonitorException {
checkConnected();
return (short) Objects.requireNonNull(glfwGetVideoMode(identifierLong)).refreshRate();
}
}

View file

@ -1,566 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.glfw.implementation;
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;
import de.staropensource.engine.windowing.event.InputEvent;
import de.staropensource.engine.windowing.event.RenderingErrorEvent;
import de.staropensource.engine.windowing.exception.NotOnMainThreadException;
import de.staropensource.engine.windowing.exception.WindowCreationFailureException;
import de.staropensource.engine.windowing.glfw.callback.KeyCallback;
import de.staropensource.engine.windowing.glfw.callback.MouseButtonCallback;
import de.staropensource.engine.windowing.implementable.Monitor;
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 lombok.Setter;
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.*;
/**
* Abstract class for implementing GLFW-powered windows in a windowing API.
*
* @since v1-alpha2
*/
@SuppressWarnings({ "JavadocDeclaration" })
public final class GlfwWindow extends Window {
/**
* Contains the {@link #identifier} used by GLFW.
*
* @since v1-alpha2
* -- GETTER --
* Returns the window identifier used by GLFW.
*
* @return GLFW identifier
* @since v1-alpha2
*/
@Getter
private long identifierLong;
/**
* Contains the code to execute in {@link #render()}.
*
* @since v1-alpha8
* -- GETTER --
* Returns the code to execute in {@link #render()}.
*
* @return rendering code
* @since v1-alpha8
* -- SETTER --
* Sets the code to execute in {@link #render()}
*
* @param renderCode new rendering code
* @since v1-alpha8
*/
@Getter
@Setter
private @NotNull Runnable renderCode = () -> glfwSwapBuffers(identifierLong);
/**
* Contains the {@link GLFWKeyCallback} used for emitting {@link InputEvent}s.
*
* @since v1-alpha2
*/
private GLFWKeyCallback keyCallback;
/**
* Contains the {@link GLFWMouseButtonCallback} used for emitting {@link InputEvent}s.
*
* @since v1-alpha2
*/
private GLFWMouseButtonCallback mouseButtonCallback;
// ------------------------------------------------ [ Window (de)initialization ] ------------------------------------------------ //
/**
* Creates and initializes an instance of this class.
*
* @param name name
* @param title title
* @param icons icons
* @param size size
* @param minimumSize minimum size
* @param maximumSize maximum size
* @param position position
* @param windowMode window mode
* @param monitor monitor
* @param resizable resizable flag
* @param borderless borderless flag
* @param focusable focusable flag
* @param onTop on top flag
* @param transparent transparency flag
* @param rendering rendering flag
* @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 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} */
@Override
protected void initializeWindow() {
createGlfwWindow();
}
/**
* (Re-)Creates the associated GLFW window.
*
* @since v1-alpha2
*/
public void createGlfwWindow() throws WindowCreationFailureException {
// Ensure running on the main thread
if (!Miscellaneous.onMainThread())
throw new NotOnMainThreadException();
// Get current focus and destroy existing window
boolean focused = true;
if (getIdentifier() != null) {
focused = isFocused();
closeGlfwWindow();
}
// Set window hints
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);
glfwWindowHint(GLFW_FOCUSED, Miscellaneous.getIntegerizedBoolean(focused));
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, Miscellaneous.getIntegerizedBoolean(isTransparent()));
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
glfwWindowHintString(GLFW_WAYLAND_APP_ID, getName());
glfwWindowHintString(GLFW_X11_CLASS_NAME, getName());
glfwWindowHintString(GLFW_X11_INSTANCE_NAME, getName());
// Create window
long identifier = glfwCreateWindow(getSize().getX(), getSize().getY(), getTitle(), MemoryUtil.NULL, MemoryUtil.NULL);
if (identifier == MemoryUtil.NULL) {
new RenderingErrorEvent().callEvent("Unable to create window: Identifier is null");
throw new WindowCreationFailureException();
}
// Set identifier
identifierLong = identifier;
setIdentifier(String.valueOf(identifier));
// Own context
ownContext();
// Set swap interval based on V-Sync mode setting
glfwSwapInterval(WindowingSubsystemConfiguration.getInstance().getVsyncMode() == VsyncMode.ON ? 1 : 0);
// Create callbacks
keyCallback = GLFWKeyCallback.create(new KeyCallback(this));
mouseButtonCallback = GLFWMouseButtonCallback.create(new MouseButtonCallback(this));
// Set callback
glfwSetKeyCallback(identifierLong, keyCallback);
glfwSetMouseButtonCallback(identifier, mouseButtonCallback);
// Update the window state
setIcons(getIcons());
setSize(getSize());
setMinimumSize(getMinimumSize());
setMaximumSize(getMaximumSize());
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();
mouseButtonCallback.close();
// Destroy the window
glfwDestroyWindow(identifierLong);
}
/** {@inheritDoc} */
public void terminate() {
setTerminated(true);
closeGlfwWindow();
}
// ------------------------------------------------ [ State updates ] ------------------------------------------------ //
/**
* Updates the window state.
*
* @since v1-alpha2
*/
@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();
// Update window mode
if (Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifierLong, GLFW_ICONIFIED)))
super.setWindowMode(WindowMode.MINIMIZED);
else if (Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifierLong, GLFW_MAXIMIZED)))
super.setWindowMode(WindowMode.MAXIMIZED);
else if (Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifierLong, GLFW_VISIBLE)))
super.setWindowMode(WindowMode.WINDOWED);
else if (Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifierLong, GLFW_VISIBLE)))
super.setWindowMode(WindowMode.HIDDEN);
// Update monitor
if (!getMonitor().isConnected()) {
Monitor newMonitor = null;
for (Monitor monitor : Monitor.getMonitors())
if (monitor.isConnected())
newMonitor = monitor;
if (newMonitor == null)
Logger.crash("Unable to set a new target monitor for window " + getUniqueIdentifier() + " as no monitors are connected to the system");
setMonitor(Objects.requireNonNull(newMonitor));
}
// Update vectors
try (MemoryStack stack = MemoryStack.stackPush()) {
IntBuffer width = stack.mallocInt(2);
IntBuffer height = stack.mallocInt(2);
glfwGetWindowSize(identifierLong, width, height);
super.setSize(new Vec2i(width.get(), height.get()));
glfwGetWindowPos(identifierLong, width, height);
super.setPosition(new Vec2i(width.get(), height.get()));
}
// Update booleans
super.setResizable(Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifierLong, GLFW_RESIZABLE)));
super.setOnTop(Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifierLong, GLFW_FLOATING)));
super.setTransparent(Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifierLong, GLFW_TRANSPARENT_FRAMEBUFFER)));
}
// ------------------------------------------------ [ Rendering ] ------------------------------------------------ //
/** {@inheritDoc} */
@Override
public void render() throws NotOnMainThreadException {
// Ensure the window is not terminated
if (isTerminated())
return;
// Ensure rendering is enabled
if (!isRendering())
return;
// Ensure running on the main thread
if (!Miscellaneous.onMainThread())
throw new NotOnMainThreadException();
renderCode.run();
}
// ------------------------------------------------ [ GLFW handling ] ------------------------------------------------ //
/**
* Updates the OpenGL context.
*
* @since v1-alpha2
*/
public void ownContext() {
glfwMakeContextCurrent(identifierLong);
}
// ------------------------------------------------ [ Information/Action methods ] ------------------------------------------------ //
/** {@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 Miscellaneous.getTristatedInteger(glfwGetWindowAttrib(identifierLong, GLFW_FOCUSED)).toBoolean();
}
/** {@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);
}
// ------------------------------------------------ [ Setter overrides ] ------------------------------------------------ //
/** {@inheritDoc} */
@Override
public void setName(@NotNull String name) {
// Ensure the window is not terminated
if (isTerminated())
return;
super.setName(name);
createGlfwWindow();
}
/** {@inheritDoc} */
@Override
public void setTitle(@NotNull String title) {
// Ensure the window is not terminated
if (isTerminated())
return;
super.setTitle(title);
glfwSetWindowTitle(identifierLong, title);
}
/** {@inheritDoc} */
@ApiStatus.Experimental
@Override
public void setIcons(@NotNull Path @Nullable [] icons) {
// Ensure the window is not terminated
if (isTerminated())
return;
this.icons = icons;
if (icons != null)
try (GLFWImage.Buffer iconsBuffer = GLFWImage.malloc(icons.length)) {
Logger.warn("GlfwWindow#setIcons is experimental and may cause engine or JVM crashes. Here be dragons!");
Logger.diag("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) {
Logger.diag("iterating icons » " + iconBuffers.size() + " » " + filepath);
// Load icon
Logger.diag("loading icon");
iconBuffers.add(STBImage.stbi_load(filepath.toAbsolutePath().toString(), width, height, channels, 4));
if (iconBuffers.getLast() == null) {
Logger.warn("Icon " + iconsBuffer.position() + " could not be loaded" + (STBImage.stbi_failure_reason() == null ? "" : ": " + STBImage.stbi_failure_reason()));
continue;
}
// Save into 'iconsBuffer'
Logger.diag("saving into buffer");
iconsBuffer
.position(iconsBuffer.position() + 1)
.width(width.get(0))
.height(height.get(0))
.pixels(iconBuffers.getLast());
}
Logger.diag("out of iteration");
// Set icons
Logger.diag("setting position");
iconsBuffer.position(0);
Logger.diag("setting icons");
Logger.flush();
glfwSetWindowIcon(identifierLong, iconsBuffer);
// Free icons
Logger.diag("freeing icons");
for (ByteBuffer buffer : iconBuffers)
if (buffer != null) {
Logger.diag("freeing buffer");
STBImage.stbi_image_free(buffer);
} else
Logger.diag("skipping null buffer");
}
}
/** {@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());
}
/** {@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());
}
/** {@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());
}
/** {@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());
}
/** {@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);
case WINDOWED -> {
glfwShowWindow(identifierLong);
glfwRestoreWindow(identifierLong);
}
case MINIMIZED -> {
glfwShowWindow(identifierLong);
glfwIconifyWindow(identifierLong);
}
case MAXIMIZED -> {
glfwShowWindow(identifierLong);
glfwRestoreWindow(identifierLong);
glfwMaximizeWindow(identifierLong);
}
case BORDERLESS_FULLSCREEN -> {
glfwShowWindow(identifierLong);
glfwRestoreWindow(identifierLong);
// TODO
}
case EXCLUSIVE_FULLSCREEN -> {
glfwShowWindow(identifierLong);
glfwRestoreWindow(identifierLong);
// TODO
}
}
}
/** {@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();
}
}

View file

@ -1,25 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* Implementations for various interfaces and abstract classes.
*
* @since v1-alpha6
*/
package de.staropensource.engine.windowing.glfw.implementation;

View file

@ -1,25 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* Code of the GLFW subsystem.
*
* @since v1-alpha2
*/
package de.staropensource.engine.windowing.glfw;

View file

@ -1,25 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* Data types in form of enums and classes.
*
* @since v1-alpha2
*/
package de.staropensource.engine.windowing.glfw.type;

View file

@ -1,25 +0,0 @@
/**
* The {@code glfw} subsystem and windowing API, which makes it
* possible to create {@link Window} using the GLFW library.
*
* @since v1-alpha4
*/
module sosengine.windowing.glfw {
// Dependencies
// -> Engine
requires transitive sosengine.base;
requires transitive sosengine.windowing;
// -> Libraries
requires transitive static lombok;
requires transitive org.jetbrains.annotations;
requires org.lwjgl.glfw;
requires org.lwjgl.stb;
// API access
exports de.staropensource.engine.windowing.glfw;
exports de.staropensource.engine.windowing.glfw.implementation;
// Reflection access
opens de.staropensource.engine.windowing.glfw;
opens de.staropensource.engine.windowing.glfw.implementation;
}

View file

@ -1,24 +0,0 @@
<!--
~ STAROPENSOURCE ENGINE SOURCE FILE
~ Copyright (c) 2024 The StarOpenSource Engine Authors
~ Licensed under the GNU Affero General Public License v3
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU Affero General Public License as
~ published by the Free Software Foundation, either version 3 of the
~ License, or (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU Affero General Public License for more details.
~
~ You should have received a copy of the GNU Affero General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<body>
<p>Welcome to the sos!engine API documentation!<br/>
You are currently in the documentation for the <b>glfw</b> subsystem, allowing Graphics APIs to easily implement window and monitor support.</p>
<p>This subsystem does not provide any utility for your application, as it's meant to be used by Graphics APIs and just implements interfaces and classes from the <b>graphics</b> subsystem.</p>
</body>

View file

@ -1 +0,0 @@
../../../../../src/main/javadoc/theme.css

View file

@ -1,235 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing;
import de.staropensource.engine.base.annotation.EngineSubsystem;
import de.staropensource.engine.base.annotation.EventListener;
import de.staropensource.engine.base.implementable.SubsystemClass;
import de.staropensource.engine.base.implementable.helper.EventHelper;
import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.utility.information.EngineInformation;
import de.staropensource.engine.base.implementation.versioning.StarOpenSourceVersioningSystem;
import de.staropensource.engine.base.event.InternalEngineShutdownEvent;
import de.staropensource.engine.base.type.DependencyVector;
import de.staropensource.engine.base.utility.Miscellaneous;
import de.staropensource.engine.windowing.implementable.api.ApiClass;
import de.staropensource.engine.windowing.event.InputEvent;
import de.staropensource.engine.windowing.event.RenderingErrorEvent;
import de.staropensource.engine.windowing.event.WindowingErrorEvent;
import de.staropensource.engine.windowing.event.WindowingShutdownEvent;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
/**
* Main class of the {@code windowing} subsystem.
*
* @since v1-alpha0
*/
@EngineSubsystem
@SuppressWarnings({ "JavadocDeclaration" })
public final class WindowingSubsystem extends SubsystemClass {
/**
* Contains the class instance.
*
* @since v1-alpha0
* -- GETTER --
* Returns the class instance.
*
* @return class instance unless the subsystem is uninitialized
* @since v1-alpha0
*/
@Getter
private static WindowingSubsystem instance = null;
/**
* Contains a list of all registered windowing APIs.
*
* @see ApiClass
* @since v1-alpha0
* -- GETTER --
* Returns a list of all registered windowing APIs.
*
* @return list of all registered windowing APIs
* @see ApiClass
* @since v1-alpha0
*/
@Getter
private final @NotNull Map<@NotNull String, @NotNull ApiClass> registeredApis = new HashMap<>();
/**
* Contains a reference to the active windowing API main class.
*
* @see ApiClass
* @since v1-alpha0
* -- GETTER --
* Returns a reference to the active windowing API main class.
*
* @return windowing API main class reference
* @see ApiClass
* @since v1-alpha0
*/
@Getter
private ApiClass api = null;
/**
* Initializes this subsystem.
*
* @since v1-alpha0
*/
public WindowingSubsystem() {
// Only allow one instance
if (instance == null)
instance = this;
else
Logger.crash("Only one instance of this class is allowed, use getInstance() instead of creating a new instance");
}
/** {@inheritDoc} */
@Override
public @NotNull String getName() {
return "windowing";
}
/** {@inheritDoc} */
@Override
public void initializeSubsystem() {
// Initialize WindowingSubsystemConfiguration and load it
new WindowingSubsystemConfiguration().loadConfiguration();
// Precompute event listeners
cacheEvents();
// Warn about subsystem and API instability
Logger.warn("The windowing subsystem is experimental. Subsystem and API stability are not guaranteed.");
}
/**
* Caches all windowing subsystem events.
*
* @since v1-alpha0
*/
public static void cacheEvents() {
EventHelper.cacheEvent(RenderingErrorEvent.class);
EventHelper.cacheEvent(WindowingShutdownEvent.class);
EventHelper.cacheEvent(WindowingErrorEvent.class);
EventHelper.cacheEvent(InputEvent.class);
}
/** {@inheritDoc} */
@Override
public @NotNull DependencyVector getDependencyVector() {
return new DependencyVector.Builder()
.setIdentifier(getName())
.setVersioningSystem(StarOpenSourceVersioningSystem.class)
.setVersion(EngineInformation.getVersioningString())
.build();
}
/**
* Shuts the subsystem down.
*
* @since v1-alpha0
*/
@EventListener(event = InternalEngineShutdownEvent.class)
@SuppressWarnings({ "unused" })
protected static void shutdownSubsystem() {
Logger.verb("Shutting down");
long shutdownTime = Miscellaneous.measureExecutionTime(() -> {
new WindowingShutdownEvent().callEvent();
if (instance.api != null)
instance.api.shutdownApi();
});
Logger.info("Shut down in " + shutdownTime + "ms");
}
/**
* Registers a windowing API.
*
* @param mainClass main class of the windowing API
* @since v1-alpha4
*/
public void registerApi(@NotNull ApiClass mainClass) {
Logger.verb("Registering windowing API " + mainClass.getApiName() + " (" + mainClass.getClass().getName() + ")");
Object[] output = Miscellaneous.getMapValues(registeredApis, mainClass).toArray();
if (output.length == 0 || output[0] == null)
registeredApis.put(mainClass.getApiName(), mainClass);
}
/**
* Chooses a windowing API to use automatically based on hardware support.
*
* @return if a compatible windowing API has been chosen
* @see #setApi(String)
* @since v1-alpha4
*/
public boolean setApi() {
Logger.verb("Choosing a windowing API");
if (registeredApis.isEmpty())
return false;
// Initialize first API in list.
api = registeredApis.get(registeredApis.keySet().toArray(new String[0])[0]);
try {
Logger.diag("Initializing windowing API \"" + api.getApiName() + "\"");
Logger.diag("Initialized windowing API \"" + api.getApiName() + "\" in " + Miscellaneous.measureExecutionTime(() -> api.initializeApi()) + "ms");
} catch (Throwable throwable) {
Logger.crash("Windowing API \"" + api.getApiName() + "\" failed to initialize", throwable, true);
throw throwable;
}
return true;
}
/**
* Sets the windowing API to use.
*
* @param name name of the windowing API
* @return if the windowing API has been found
* @see #setApi()
* @since v1-alpha4
*/
@SuppressWarnings({ "unused" })
public boolean setApi(@NotNull String name) {
if (!registeredApis.containsKey(name))
return false;
Logger.verb("Setting windowing API " + name);
if (api == null)
api = registeredApis.get(name);
else
Logger.crash("Unable to set windowing API: windowing API " + api.getApiName() + " already registered");
// Initialize API
Logger.diag("Initializing windowing API " + api.getApiName());
Logger.diag("Initialized windowing API " + api.getApiName() + " in " + Miscellaneous.measureExecutionTime(() -> api.initializeApi()) + "ms");
return true;
}
}

View file

@ -1,246 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing;
import de.staropensource.engine.base.implementable.Configuration;
import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.utility.PropertiesReader;
import de.staropensource.engine.windowing.event.RenderingErrorEvent;
import de.staropensource.engine.windowing.event.WindowingErrorEvent;
import de.staropensource.engine.windowing.implementable.api.ApiManagementClass;
import de.staropensource.engine.windowing.type.window.VsyncMode;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Provides the configuration of the windowing subsystem.
*
* @since v1-alpha0
*/
@Getter
@SuppressWarnings({ "JavadocDeclaration" })
public final class WindowingSubsystemConfiguration extends Configuration {
/**
* Contains the class instance.
*
* @since v1-alpha0
* -- GETTER --
* Returns the class instance.
*
* @return class instance unless {@link WindowingSubsystem} is uninitialized
* @since v1-alpha0
*/
@Getter
private static WindowingSubsystemConfiguration instance;
/**
* Defines the group every property must start with to be recognized as a subsystem configuration setting.
*
* @since v1-alpha0
* -- GETTER --
* Returns the group that every property must start with to be recognized as a subsystem configuration setting.
*
* @return property group
* @since v1-alpha0
*/
@Getter
public final @NotNull String group = "sosengine.windowing.";
/**
* If enabled, allows for unintentional behaviour and excess logging.
* Unless you want to debug or work on a sensitive part of the subsystem, don't enable this!
*
* @since v1-alpha0
* -- GETTER --
* Gets the value for {@link #debug}.
*
* @return variable value
* @see WindowingSubsystemConfiguration#debug
* @since v1-alpha0
*/
private boolean debug;
/**
* If enabled, will log all keys being pressed or released.
*
* @since v1-alpha2
* -- GETTER --
* Gets the value for {@link #debugInput}.
*
* @return variable value
* @see WindowingSubsystemConfiguration#debugInput
* @since v1-alpha2
*/
private boolean debugInput;
/**
* If enabled, will log the delta time average
* and FPS count to the console every second.
* <p>
* Is applied during rendering thread startup
* and will not be applied after.
*
* @since v1-alpha2
* -- GETTER --
* Gets the value for {@link #debugFrames}.
*
* @return variable value
* @see #debugFrames
* @since v1-alpha2
*/
private boolean debugFrames;
/**
* Causes windowing errors will be logged, if enabled.
*
* @see WindowingErrorEvent
* @since v1-alpha4
* -- GETTER --
* Gets the value for {@link #errorWindowingFailure}.
*
* @return variable value
* @see WindowingSubsystemConfiguration#errorWindowingFailure
* @since v1-alpha4
*/
private boolean errorWindowingFailure;
/**
* Causes rendering errors will be logged, if enabled.
*
* @see RenderingErrorEvent
* @since v1-alpha4
* -- GETTER --
* Gets the value for {@link #errorRenderingFailure}.
*
* @return variable value
* @see WindowingSubsystemConfiguration#errorRenderingFailure
* @since v1-alpha4
*/
private boolean errorRenderingFailure;
/**
* Contains how many frames can be rendered per second.
* <p>
* This setting determines if and how V-Sync will operate, which
* (if enabled) tries to synchronize the frame rate to the monitor's
* refresh rate. See {@link VsyncMode} for more information.
*
* @since v1-alpha2
* -- GETTER --
* Gets the value for {@link #vsyncMode}.
*
* @return variable value
* @see #vsyncMode
* @since v1-alpha2
*/
private VsyncMode vsyncMode;
/**
* Contains how many frames can be rendered per second.
* <p>
* This value will have no effect on windows with V-Sync enabled.
* Set to {@code 0} for no limit.
*
* @since v1-alpha2
* -- GETTER --
* Gets the value for {@link #maximumFramesPerSecond}.
*
* @return variable value
* @see #maximumFramesPerSecond
* @since v1-alpha2
*/
private int maximumFramesPerSecond;
/**
* Creates and initializes an instance of this class.
*
* @see WindowingSubsystem
* @since v1-alpha6
*/
WindowingSubsystemConfiguration() {
instance = this;
loadDefaultConfiguration();
}
/** {@inheritDoc} */
@Override
protected void matchProperty(@NotNull PropertiesReader parser, @NotNull String property) {
switch (property) {
case "debug" -> debug = parser.getBoolean(group + property);
case "debugInput" -> debugInput = parser.getBoolean(group + property);
case "debugFrames" -> debugFrames = parser.getBoolean(group + property);
case "errorWindowingFailure" -> errorWindowingFailure = parser.getBoolean(group + property);
case "errorRenderingFailure" -> errorRenderingFailure = parser.getBoolean(group + property);
case "vsyncMode" -> {
try {
vsyncMode = VsyncMode.valueOf(parser.getString(group + property).toUpperCase());
} catch (IllegalArgumentException exception) {
Logger.error("V-Sync mode " + parser.getString(group + property) + " is not valid");
}
}
case "maximumFramesPerSecond" -> maximumFramesPerSecond = parser.getInteger(group + property, true);
}
}
/** {@inheritDoc} */
@Override
protected void processSettings(@NotNull PropertiesReader parser) {
// Disable all debug options if 'debug' is disabled
if (!debug) {
debugInput = false;
debugFrames = false;
}
}
/** {@inheritDoc} */
@Override
public void loadDefaultConfiguration() {
debug = false;
debugInput = false;
debugFrames = false;
errorWindowingFailure = true;
errorRenderingFailure = true;
vsyncMode = VsyncMode.ON;
maximumFramesPerSecond = 60;
}
/** {@inheritDoc} */
@Override
public @Nullable Object getSetting(@NotNull String setting) {
switch (setting) {
case "debug" -> { return debug; }
case "debugInput" -> { return debugInput; }
case "debugFrames" -> { return debugFrames; }
case "errorWindowingFailure" -> { return errorWindowingFailure; }
case "errorRenderingFailure" -> { return errorRenderingFailure; }
case "vsyncMode" -> { return vsyncMode; }
case "maximumFramesPerSecond" -> { return maximumFramesPerSecond; }
default -> { return null; }
}
}
}

View file

@ -1,58 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.event;
import de.staropensource.engine.base.implementable.Event;
import de.staropensource.engine.base.implementable.helper.EventHelper;
import org.jetbrains.annotations.NotNull;
/**
* Called when an error occurs in the windowing API.
*
* @since v1-alpha0
*/
public final class WindowingErrorEvent implements Event {
/**
* Creates and initializes an instance of this event.
*
* @since v1-alpha0
*/
public WindowingErrorEvent() {}
/**
* {@inheritDoc}
*
* @deprecated use the {@code callEvent} method with arguments
* @see #callEvent(String)
*/
@Deprecated
@Override
public void callEvent() {}
/**
* Emits the event and calls all event listeners.
*
* @param error error description
* @since v1-alpha0
*/
public void callEvent(@NotNull String error) {
EventHelper.invokeAnnotatedMethods(getClass(), error);
}
}

View file

@ -1,44 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.event;
import de.staropensource.engine.base.implementable.Event;
import de.staropensource.engine.base.implementable.helper.EventHelper;
/**
* Called when the windowing API shuts down.
*
* @since v1-alpha0
*/
public final class WindowingShutdownEvent implements Event {
/**
* Creates and initializes an instance of this event.
*
* @since v1-alpha0
*/
public WindowingShutdownEvent() {}
/** {@inheritDoc} */
@Override
public void callEvent() {
EventHelper.invokeAnnotatedMethods(getClass());
}
}

View file

@ -1,84 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.implementable.api;
import de.staropensource.engine.base.Engine;
import de.staropensource.engine.base.implementable.SubsystemClass;
import org.jetbrains.annotations.NotNull;
/**
* Abstract class for building windowing API main classes.
*
* @since v1-alpha0
*/
public abstract class ApiClass extends SubsystemClass {
/**
* Creates and initializes an instance of this abstract class.
*
* @since v1-alpha2
*/
public ApiClass() {}
/**
* Initializes the windowing API.
*
* @since v1-alpha0
*/
public abstract void initializeApi();
/**
* Shuts the windowing API down.
* <p>
* Called when the engine shuts down.
*
* @see Engine#shutdown()
* @see Engine#shutdown(int)
* @since v1-alpha0
*/
public abstract void shutdownApi();
/**
* Returns the name of the windowing API.
*
* @return windowing API name
* @since v1-alpha0
*/
public abstract String getApiName();
/**
* Returns the windowing API's internal API access class.
*
* @return a {@link ApiInternalClass}
* @see ApiInternalClass
* @since v1-alpha2
*/
@NotNull
public abstract ApiInternalClass getInternalApi();
/**
* Returns the windowing API's management class.
*
* @return a {@link ApiManagementClass}
* @see ApiManagementClass
* @since v1-alpha0
*/
@NotNull
public abstract ApiManagementClass getManagement();
}

View file

@ -1,54 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.implementable.api;
import de.staropensource.engine.windowing.implementable.Monitor;
import de.staropensource.engine.windowing.implementable.Window;
import de.staropensource.engine.windowing.exception.NoMonitorsFoundException;
import org.jetbrains.annotations.NotNull;
import java.util.LinkedHashSet;
/**
* Interface for internal API access classes.
* These are used exclusively by the windowing
* subsystem's code.
*
* @since v1-alpha2
*/
public interface ApiInternalClass {
/**
* Returns the {@link Window} class of the windowing API.
*
* @return {@link Window} class
* @since v1-alpha2
*/
@NotNull
Class<? extends Window> getWindowClass();
/**
* Returns all connected monitors.
*
* @return connected monitors
* @since v1-alpha2
*/
@NotNull
LinkedHashSet<@NotNull Monitor> getMonitors() throws NoMonitorsFoundException;
}

View file

@ -1,125 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.windowing.implementable.api;
import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.utility.Math;
import de.staropensource.engine.base.utility.Miscellaneous;
import de.staropensource.engine.windowing.WindowingSubsystemConfiguration;
import de.staropensource.engine.windowing.implementable.Window;
import de.staropensource.engine.windowing.type.window.VsyncMode;
import org.jetbrains.annotations.NotNull;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicReference;
/**
* Interface for building windowing API management classes.
* These are used for interfacing with the windowing API directly.
*
* @since v1-alpha0
*/
public abstract class ApiManagementClass {
/**
* Creates and initializes an instance of this abstract class.
*
* @since v1-alpha2
*/
public ApiManagementClass() {}
/**
* Returns if this windowing API must be interacted with on the main thread.
*
* @return {@code true} if windowing API must be interacted with on the main thread, {@code false} otherwise
* @since v1-alpha2
*/
public abstract boolean mustRunOnMainThread();
/**
* Runs the render loop <b>once</b>.
* To run the render loop continuously, see {@link #runRenderLoopContinuously(Runnable)}.
*
* @return map of windows and their thrown throwables
* @since v1-alpha2
*/
public abstract LinkedHashMap<@NotNull Window, @NotNull Throwable> runRenderLoop();
/**
* Runs the render loop <b>for ever</b>.
* To run the render loop only once, see {@link #runRenderLoop()}.
* <p>
* Immediately returns with when a {@link #runRenderLoop()} call fails.
*
* @param frameCode code that should be invoked on during a frame. will be counted to frame time
* @return see {@link #runRenderLoop()}
* @since v1-alpha2
*/
public LinkedHashMap<@NotNull Window, @NotNull Throwable> runRenderLoopContinuously(@NotNull Runnable frameCode) {
// Define variables
AtomicReference<LinkedHashMap<@NotNull Window, @NotNull Throwable>> output = new AtomicReference<>(new LinkedHashMap<>()); // runRenderLoop output
long renderTime; // Amount of time spent rendering
long sleepDuration; // Time spent sleeping the thread
LinkedList<Long> splitDeltaTime = new LinkedList<>(); // Used for calculating the delta time (render time average over one second)
long reportDuration = System.currentTimeMillis() + 1000; // Used for determining when to report frame count and delta time
double deltaTime; // Contains the average render time over one second (delta time)
// Check if delta time and frame count shall be printed to console.
// Unless this code is ran 292 billion years into the future,
// this should sufficiently disable the reporting feature.
if (!WindowingSubsystemConfiguration.getInstance().isDebugFrames())
reportDuration = Long.MAX_VALUE;
// Run while the 'output' is empty
while (output.get().isEmpty()) {
renderTime = Miscellaneous.measureExecutionTime(() -> {
output.set(runRenderLoop());
frameCode.run();
});
if (WindowingSubsystemConfiguration.getInstance().getVsyncMode() != VsyncMode.OFF)
// V-Sync is enabled, no need for manual busy waiting
sleepDuration = 0L;
else
// Calculate amount of time the thread should spend sleeping
sleepDuration = (long) (1d / WindowingSubsystemConfiguration.getInstance().getMaximumFramesPerSecond() * 1000d) - renderTime;
// Add render and sleep time to list used for calculating the delta time value
splitDeltaTime.add(renderTime + sleepDuration);
// Busy wait unless V-Sync is enabled
if (WindowingSubsystemConfiguration.getInstance().getVsyncMode() == VsyncMode.OFF && WindowingSubsystemConfiguration.getInstance().getMaximumFramesPerSecond() >= 1) {
sleepDuration += System.currentTimeMillis();
while (System.currentTimeMillis() < sleepDuration)
Thread.onSpinWait();
}
// Calculate delta time and frame count every second
if (System.currentTimeMillis() >= reportDuration) {
deltaTime = Math.getMeanLong(splitDeltaTime); // Calculate delta time
Logger.diag("Delta time average: " + deltaTime + " | Frames/s: " + 1000 / deltaTime); // Print delta time and frame count to console
reportDuration = System.currentTimeMillis() + 1000; // Update 'reportDuration'
splitDeltaTime.clear(); // Clear 'splitDeltaTime' list
}
}
return output.get();
}
}

View file

@ -1,25 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* Contains classes starting with {@code Api*}.
*
* @since v1-alpha2
*/
package de.staropensource.engine.windowing.implementable.api;

View file

@ -1,27 +0,0 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* Interfaces and abstract classes which can be used for implementing classes.
* <p>
* These are not to be confused with data types. See {@link de.staropensource.engine.windowing.type}.
*
* @since v1-alpha0
*/
package de.staropensource.engine.windowing.implementable;

View file

@ -1,32 +0,0 @@
/**
* The {@code windowing} subsystem, which provides abstractions and
* APIs, which allow the creation and management of windows.
*
* @since v1-alpha4
*/
module sosengine.windowing {
// Dependencies
// -> Engine
requires transitive sosengine.base;
// -> Libraries
requires transitive static lombok;
requires transitive org.jetbrains.annotations;
// API access
exports de.staropensource.engine.windowing;
exports de.staropensource.engine.windowing.implementable;
exports de.staropensource.engine.windowing.implementable.api;
exports de.staropensource.engine.windowing.event;
exports de.staropensource.engine.windowing.exception;
exports de.staropensource.engine.windowing.type.input;
exports de.staropensource.engine.windowing.type.window;
// Reflection access
opens de.staropensource.engine.windowing;
opens de.staropensource.engine.windowing.implementable;
opens de.staropensource.engine.windowing.implementable.api;
opens de.staropensource.engine.windowing.event;
opens de.staropensource.engine.windowing.exception;
opens de.staropensource.engine.windowing.type.input;
opens de.staropensource.engine.windowing.type.window;
}