Compare commits

..

No commits in common. "develop" and "stable" have entirely different histories.

71 changed files with 3076 additions and 1910 deletions

3
.gitignore vendored
View file

@ -42,6 +42,3 @@ bin/
### Mac OS ###
.DS_Store
### Java ###
hs_err_pid*.log

View file

@ -19,11 +19,8 @@
package de.staropensource.engine.base;
import de.staropensource.engine.base.annotation.EngineSubsystem;
import de.staropensource.engine.base.event.LogEvent;
import de.staropensource.engine.base.implementable.Configuration;
import de.staropensource.engine.base.implementable.ShortcodeParser;
import de.staropensource.engine.base.implementable.SubsystemClass;
import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.logging.backend.async.LoggingThread;
import de.staropensource.engine.base.type.EngineState;
@ -69,11 +66,11 @@ public final class EngineConfiguration extends Configuration {
private static EngineConfiguration instance;
/**
* Contains the configuration prefix.
* Contains prefix properties must begin with.
*
* @since v1-alpha0
* -- GETTER --
* Returns the configuration prefix.
* Returns prefix properties must begin with.
*
* @return property group
* @since v1-alpha0
@ -82,196 +79,132 @@ public final class EngineConfiguration extends Configuration {
/**
* 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}.
* If enabled, allows for unintentional behaviour
* and excess logging. Unless you want to debug or work
* on a sensitive part of the engine, don't enable this!
*
* @since v1-alpha0
* -- 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}.
* Gets the value for {@link #debug}.
*
* @return debugging enabled?
* @return variable value
* @see #debug
* @since v1-alpha0
*/
private boolean debug;
/**
* Contains whether or not to log
* events being emitted.
* <p>
* This will cause all events to
* be logged, with the exception
* of the {@link LogEvent}.
* If enabled, all called events will be logged.
*
* @since v1-alpha0
* -- GETTER --
* Returns whether or not to log
* events being emitted.
* <p>
* This will cause all events to
* be logged, with the exception
* of the {@link LogEvent}.
* Gets the value for {@link #debugEvents}.
*
* @return detailed event logging enabled?
* @return variable value
* @see #debugEvents
* @since v1-alpha0
*/
private boolean debugEvents;
/**
* Contains whether or not to automatically discover
* and initialize any class extending {@link SubsystemClass}
* whilst being annotated with {@link EngineSubsystem}.
* If enabled, will try to automatically initialize every
* subsystem found though reflection.
* <p>
* This mechanism may fail in certain situations, where
* manual subsystem initialization may be desired. Make
* sure to disable this setting before engine startup
* and then initialize all subsystems manually.
* This however may fail in certain situation, where manual
* subsystem initialization may be required. For this reason,
* this can be turned off before the engine initializes. Please
* note though that dependency resolution between subsystems
* won't be performed, be careful when initializing subsystems manually.
*
* @see Engine
* @since v1-alpha5
* @since v1-alpha2
* -- GETTER --
* Returns whether or not to automatically discover
* and initialize any class extending {@link SubsystemClass}
* whilst being annotated with {@link EngineSubsystem}.
* <p>
* This mechanism may fail in certain situations, where
* manual subsystem initialization may be desired. Make
* sure to disable this setting before engine startup
* and then initialize all subsystems manually.
* Gets the value for {@link #initialPerformSubsystemInitialization}.
*
* @return automatically discover and initialize subsystems?
* @return variable value
* @see #initialPerformSubsystemInitialization
* @since v1-alpha5
*/
private boolean initialPerformSubsystemInitialization;
/**
* Contains a set of class names to try to load
* and initialize as subsystems. Will only take effect
* if {@link #initialPerformSubsystemInitialization} is
* turned off.
* Will try to load the specified classes as subsystems,
* if reflective classpath scanning is disabled.
*
* @since v1-alpha5
* -- GETTER --
* Returns a set of class names to try to load
* and initialize as subsystems. Will only take effect
* if {@link #getInitialIncludeSubsystemClasses()} is
* turned off.
* Gets the value for {@link #initialIncludeSubsystemClasses}.
*
* @return set of class names to try and initialize as subsystems
* @return variable value
* @see #initialIncludeSubsystemClasses
* @since v1-alpha5
*/
private Set<@NotNull String> initialIncludeSubsystemClasses;
/**
* Contains whether or not to complain about invalid
* shortcodes.
* <p>
* Requires the active log level to be set at least
* to {@link LogLevel#SILENT_WARNING} to have effect.
* If enabled, will cause {@link ShortcodeParser} to print
* invalid shortcodes as {@link LogLevel#SILENT_WARNING}s.
*
* @see ShortcodeParser
* @see #logLevel
* @since v1-alpha0
* -- GETTER --
* Returns whether or not to complain about invalid
* shortcodes.
* <p>
* Requires the active log level to be set at least
* to {@link LogLevel#SILENT_WARNING} to have effect.
* Gets the value for {@link #errorShortcodeParser}.
*
* @return complain about invalid shortcodes?
* @see #getLogLevel()
* @return variable value
* @see #errorShortcodeParser
* @since v1-alpha0
*/
private boolean errorShortcodeParser;
/**
* Contains if to log asynchronously.
* <p>
* If enabled, will cause a logging thread
* to spawn. All log messages will be queued
* and printed after a set delay
* ({@link #logPollingSpeed}).
* Highly recommended to keep enabled, or
* the performance of your application will
* very likely suffer.
* If enabled, will makes the {@link Logger} work asynchronous,
* in a separate platform thread. Don't disable unless you want
* your application to run <b>extremely</b> slowly.
*
* @see #logPollingSpeed
* @see Thread
* @since v1-alpha0
* -- GETTER --
* Returns if to log asynchronously.
* <p>
* If enabled, will cause a logging thread
* to spawn. All log messages will be queued
* and printed after a set delay
* ({@link #getLogPollingSpeed()}).
* Highly recommended to keep enabled, or
* the performance of your application will
* very likely suffer.
* Gets the value for {@link #optimizeLogging}.
*
* @return log asynchronously?
* @see #getLogPollingSpeed()
* @return variable value
* @see #optimizeLogging
* @since v1-alpha0
*/
private boolean optimizeLogging;
/**
* Contains whether or not to emit events
* asynchronously.
* <p>
* This will cause a
* <a href="https://openjdk.org/jeps/444">VirtualThread</a>
* to spawn every time an event is emitted.
* If enabled, will make all events asynchronous,
* in separate virtual threads. Don't disable unless you
* want your application to run slower.
*
* @see VirtualThread
* @since v1-alpha0
* -- GETTER --
* Contains whether or not to emit events
* asynchronously.
* <p>
* This will cause a
* <a href="https://openjdk.org/jeps/444">VirtualThread</a>
* to spawn every time an event is emitted.
* Gets the value for {@link #optimizeEvents}.
*
* @return emit events asynchronously?
* @return variable value
* @see #optimizeEvents
* @since v1-alpha0
*/
private boolean optimizeEvents;
/**
* Contains the minimum allowed log level.
* <p>
* The priority list is as follows (from high to low priority):
* <ul>
* <li>{@link LogLevel#CRASH}</li>
* <li>{@link LogLevel#ERROR}</li>
* <li>{@link LogLevel#WARNING}</li>
* <li>{@link LogLevel#INFORMATIONAL}</li>
* <li>{@link LogLevel#SILENT_WARNING}</li>
* <li>{@link LogLevel#VERBOSE}</li>
* <li>{@link LogLevel#DIAGNOSTIC}</li>
* </ul>
* Contains which logger levels are allowed
* by setting the minimum logger level.
*
* @see Logger
* @since v1-alpha0
* -- GETTER --
* Returns the minimum allowed log level.
* <p>
* The priority list is as follows (from high to low priority):
* <ul>
* <li>{@link LogLevel#CRASH}</li>
* <li>{@link LogLevel#ERROR}</li>
* <li>{@link LogLevel#WARNING}</li>
* <li>{@link LogLevel#INFORMATIONAL}</li>
* <li>{@link LogLevel#SILENT_WARNING}</li>
* <li>{@link LogLevel#VERBOSE}</li>
* <li>{@link LogLevel#DIAGNOSTIC}</li>
* </ul>
* Gets the value for {@link #logLevel}.
*
* @return minimum allowed log level
* @return variable value
* @see #logLevel
* @since v1-alpha0
*/
private LogLevel logLevel;
@ -293,118 +226,75 @@ public final class EngineConfiguration extends Configuration {
* <li><code>lineNumber</code></li>
* </ul>
*
* @see Logger
* @since v1-alpha8
* -- GETTER --
* Returns a comma-separated list of optional
* features to add to the final log output.
* <p>
* Available features (in order of appearance):
* <ul>
* <li><code>formatting</code></li>
* <li><code>runtime</code></li>
* <li><code>date</code></li>
* <li><code>time</code></li>
* <li><code>shortIssuerClass</code></li>
* <li><code>moduleName</code></li>
* <li><code>moduleVersion</code> (requires <code>moduleName</code>)</li>
* <li><code>methodName</code></li>
* <li><code>lineNumber</code></li>
* </ul>
* Gets the value for {@link #logFeatures}
*
* @return optional features to enable
* @return variable value
* @see #logFeatures
* @since v1-alpha8
*/
private Set<@NotNull String> logFeatures;
/**
* Contains how fast the logging thread will
* poll for queued messages in milliseconds.
* This also causes messages to be buffered.
* poll for queued messages. This also causes
* messages to be buffered.
* <p>
* Only applies if {@code optimizeLogging} is turned on.
* Values below {@code 1} will poll for queued messages
* as fast as it can. This however has pretty much no
* benefit. Leave it at {@code 5}, it works quite well.
* Values below {@code 1} will poll for queued
* messages as fast as it can. This however has pretty much
* no benefit. Leave it at {@code 5}, it works quite well.
*
* @see #optimizeLogging
* @since v1-alpha4
* -- GETTER --
* Contains how fast the logging thread will
* poll for queued messages, in milliseconds.
* This also causes messages to be buffered.
* <p>
* Only applies if {@code optimizeLogging} is turned on.
* Values below {@code 1} will poll for queued messages
* as fast as it can. This however has pretty much no
* benefit. Leave it at {@code 5}, it works quite well.
* Gets the value for {@link #logPollingSpeed}.
*
* @return logging thread polling speed in milliseconds
* @see #isOptimizeLogging()
* @return variable value
* @see #logPollingSpeed
* @since v1-alpha4
*/
private int logPollingSpeed;
/**
* Contains whether or not to forcefully write
* to the standard output instead of the
* standard error stream.
* <p>
* This only applies to the {@link LogLevel#ERROR} and
* {@link LogLevel#CRASH} log levels, as these use
* the standard error stream by default.
* If enabled, will force sos!engine's logging infrastructure to use
* <a href="https://www.man7.org/linux/man-pages/man3/stderr.3.html">the standard output</a>
* instead of <a href="https://www.man7.org/linux/man-pages/man3/stderr.3.html">the standard error</a>
* for logging the {@code ERROR} and {@code CRASH} log levels.
*
* @see <a href="https://man7.org/linux/man-pages/man3/stderr.3.html">man page about standard streams</a>
* @since v1-alpha0
* -- GETTER --
* Contains whether or not to forcefully write
* to the standard output instead of the
* standard error stream.
* <p>
* This only applies to the {@link LogLevel#ERROR} and
* {@link LogLevel#CRASH} log levels, as these use
* the standard error stream by default.
* Gets the value for {@link #logForceStandardOutput}.
*
* @return force use stdout?
* @see <a href="https://man7.org/linux/man-pages/man3/stderr.3.html">man page about standard streams</a>
* @return variable value
* @see #logForceStandardOutput
* @since v1-alpha0
*/
private boolean logForceStandardOutput;
/**
* Contains if to truncate the full path
* of a class when invoking using their
* {@link #toString()} method.
* Will truncate the path of types when using
* their {@code toString} method.
* <p>
* Here's an example: Lets say that you have a
* {@link Vec2f} instance and want to convert
* it to a String. You can do that by using
* {@link Vec2f}'s {@link Vec2f#toString()}
* method. With this flag disabled it will
* return
* {@code de.staropensource.engine.base.types.vectors.}{@link Vec2i}{@code (x=64 y=64)}.
* With this flag enabled however the method will return
* Here's an example: Lets say that you
* have a {@link Vec2f} and to convert it
* to a String, which you can do with
* {@link Vec2f#toString()}. With this flag
* disabled it would return
* {@code de.staropensource.engine.base.types.vectors.}{@link Vec2i}{@code (x=64 y=64)},
* with it however it would just return
* {@link Vec2i}{@code (x=64 y=64)},
* which is much smaller.
*
* @since v1-alpha2
* -- GETTER --
* Returns if to truncate the full path
* of a class when invoking using their
* {@link #toString()} method.
* <p>
* Here's an example: Lets say that you have a
* {@link Vec2f} instance and want to convert
* it to a String. You can do that by using
* {@link Vec2f}'s {@link Vec2f#toString()}
* method. With this flag disabled it will
* return
* {@code de.staropensource.engine.base.types.vectors.}{@link Vec2i}{@code (x=64 y=64)}.
* With this flag enabled however the method will return
* {@link Vec2i}{@code (x=64 y=64)},
* which is much smaller.
* Gets the value for {@link #hideFullTypePath}.
*
* @return truncate class paths?
* @return variable value
* @see #hideFullTypePath
* @since v1-alpha2
*/
private boolean hideFullTypePath;

View file

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

View file

@ -78,7 +78,7 @@ This avoids unnecessary bloat, having too many dependencies in your project whil
size and memory footprint of your project.
## Official subsystems
Besides the `base` engine, there are two stable subsystem and two experimental subsystems.
Besides the `base` engine, there are two stable subsystem and three experimental subsystems.
There may be other subsystems out there. Please note though that they are not maintained
by the StarOpenSource Project directly and are not automatically updated with the engine.
@ -86,7 +86,8 @@ by the StarOpenSource Project directly and are not automatically updated with th
- [`ansi`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/ansi): Provides an ANSI logger and a ShortcodeParserSkeleton implementation for all your terminal formatting needs
- [`slf4j-compat`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/slf4j-compat): Provides a [SLF4J](https://slf4j.org/) compatibility logger for redirecting all log calls to the engine's logging system
### Experimental
- [`rendering`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/rendering): Provides an API for creating, managing and rendering windows
- [`windowing`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/windowing): Provides abstract APIs for creating and managing windows as well as monitors
- [`windowing-glfw`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/windowing/glfw): Windowing API; allows using [GLFW](https://glfw.org) for creating windows
- [`notification`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/notification): Provides an API for sending and receiving notifications inside a program
## API documentation

View file

@ -15,11 +15,11 @@
"typecheck": "tsc"
},
"dependencies": {
"@docusaurus/core": "3.6.1",
"@docusaurus/plugin-client-redirects": "^3.6.1",
"@docusaurus/plugin-content-docs": "^3.6.1",
"@docusaurus/plugin-sitemap": "^3.6.1",
"@docusaurus/preset-classic": "3.6.1",
"@docusaurus/core": "3.6.0",
"@docusaurus/plugin-client-redirects": "^3.6.0",
"@docusaurus/plugin-content-docs": "^3.6.0",
"@docusaurus/plugin-sitemap": "^3.6.0",
"@docusaurus/preset-classic": "3.6.0",
"@mdx-js/react": "^3.0.1",
"clsx": "^2.1.0",
"prism-react-renderer": "^2.3.1",
@ -27,9 +27,9 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.6.1",
"@docusaurus/tsconfig": "3.6.1",
"@docusaurus/types": "3.6.1",
"@docusaurus/module-type-aliases": "3.6.0",
"@docusaurus/tsconfig": "3.6.0",
"@docusaurus/types": "3.6.0",
"@types/node": "^20.12.5",
"typescript": "~5.4.4"
},

View file

@ -21,28 +21,28 @@
versioningCodename=Sugarcookie
versioningVersion=1
versioningType=alpha
versioningTyperelease=9
versioningTyperelease=8
versioningFork=
# Java
javaSource=21
javaSource=22
javaTarget=21
# Plugins
pluginShadow=8.1.8
pluginLombok=8.10.2
pluginShadow=8.1.7
pluginLombok=8.6
pluginGitProperties=2.4.2
pluginNativeImage=v1.4.1
pluginNativeImage=v1.4.0
# Dependencies
dependencyLombok=1.18.34
dependencyJetbrainsAnnotations=26.0.1
dependencyLombok=1.18.32
dependencyJetbrainsAnnotations=24.1.0
dependencyJansi=2.4.1
dependencyReflections=0.10.2
dependencySlf4j=2.0.16
dependencyLwjgl=3.3.4
dependencySlf4j=2.0.13
dependencyLwjgl=3.3.3
dependencyLwjglNatives=
dependencyJunit=5.11.3
dependencyJunit=5.11.0-M2
# etc
group = de.staropensource.engine

View file

@ -1,323 +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.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

@ -1,311 +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.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

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

View file

@ -39,9 +39,10 @@ dependencies {
// Project
implementation(project(":base"))
implementation(project(":rendering"))
implementation(project(":windowing"))
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.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 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 lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -119,6 +119,10 @@ 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 {
@ -135,13 +139,12 @@ public final class Main {
return;
}
if (window == null)
Logger.crash("'window' is null");
// Render loop
LinkedHashMap<@NotNull Window, @NotNull Throwable> renderLoopFailures = RenderingSubsystem
LinkedHashMap<@NotNull Window, @NotNull Throwable> renderLoopFailures = WindowingSubsystem
.getInstance()
.runRenderLoop(() -> {
.getApi()
.getManagement()
.runRenderLoopContinuously(() -> {
if (shutdown || window.isClosureRequested())
Engine.getInstance().shutdown();
});

View file

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

109
windowing/build.gradle Normal file
View file

@ -0,0 +1,109 @@
/*
* 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)
}

2
windowing/glfw/README.md Normal file
View file

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

View file

@ -61,17 +61,16 @@ dependencies {
// LWJGL
implementation(platform("org.lwjgl:lwjgl-bom:${dependencyLwjgl}"))
implementation("org.lwjgl:lwjgl")
implementation("org.lwjgl:lwjgl-stb")
implementation("org.lwjgl:lwjgl-glfw")
implementation("org.lwjgl:lwjgl-bgfx")
implementation("org.lwjgl:lwjgl-stb")
runtimeOnly("org.lwjgl:lwjgl::${dependencyLwjglNatives}")
runtimeOnly("org.lwjgl:lwjgl-stb::${dependencyLwjglNatives}")
runtimeOnly("org.lwjgl:lwjgl-glfw::${dependencyLwjglNatives}")
runtimeOnly("org.lwjgl:lwjgl-bgfx::${dependencyLwjglNatives}")
runtimeOnly("org.lwjgl:lwjgl-stb::${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
@ -97,6 +96,26 @@ 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()

1
windowing/glfw/gradle Symbolic link
View file

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

1
windowing/glfw/gradlew vendored Symbolic link
View file

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

1
windowing/glfw/gradlew.bat vendored Symbolic link
View file

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

View file

@ -0,0 +1,218 @@
/*
* 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

@ -0,0 +1,139 @@
/*
* 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

@ -17,13 +17,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.rendering.callback;
package de.staropensource.engine.windowing.glfw.callback;
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 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 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-alpha9
* @since v1-alpha2
*/
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-alpha9
* @since v1-alpha2
*/
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-alpha9
* @since v1-alpha2
*/
public KeyCallback(@NotNull Window window) {
super(window);

View file

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

View file

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

View file

@ -17,9 +17,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.rendering.callback;
package de.staropensource.engine.windowing.glfw.implementable;
import de.staropensource.engine.rendering.type.Window;
import de.staropensource.engine.windowing.implementable.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-alpha9
* @since v1-alpha2
*/
@Getter
@SuppressWarnings({ "JavadocDeclaration" })
@ -36,13 +36,13 @@ public class WindowCallback {
* Refers to the {@link Window} instance
* this callback is tied to.
*
* @since v1-alpha9
* @since v1-alpha2
* -- GETTER --
* Returns the {@link Window} instance
* this callback is tied to.
*
* @return attached {@link Window} instance
* @since v1-alpha9
* @since v1-alpha2
*/
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-alpha9
* @since v1-alpha2
*/
public WindowCallback(@NotNull Window window) {
this.attachedWindow = window;

View file

@ -0,0 +1,25 @@
/*
* 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

@ -0,0 +1,82 @@
/*
* 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

@ -0,0 +1,92 @@
/*
* 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

@ -0,0 +1,118 @@
/*
* 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

@ -0,0 +1,566 @@
/*
* 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

@ -0,0 +1,25 @@
/*
* 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

@ -0,0 +1,25 @@
/*
* 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

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

View file

@ -0,0 +1,25 @@
/*
* 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

@ -0,0 +1,25 @@
/**
* 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

@ -0,0 +1,24 @@
<!--
~ 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

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

View file

@ -0,0 +1,235 @@
/*
* 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

@ -0,0 +1,246 @@
/*
* 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

@ -17,28 +17,28 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.rendering.event;
package de.staropensource.engine.windowing.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.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 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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Called when a key or button is pressed.
*
* @since v1-alpha9
* @since v1-alpha2
*/
public final class InputEvent implements Event {
/**
* Creates and initializes an instance of this event.
*
* @since v1-alpha9
* @since v1-alpha2
*/
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-alpha9
* @since v1-alpha0
*/
public void callEvent(@Nullable Window window, @NotNull Key key, @NotNull KeyState state) {
if (RenderingSubsystemConfiguration.getInstance().isDebugInput())
if (WindowingSubsystemConfiguration.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,28 +17,27 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.rendering.event;
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;
/**
* Emitted when a rendering error occurs.
* Called when an error occurs in the renderer API (e.g. OpenGL, Vulkan).
*
* @since v1-alpha9
* @since v1-alpha0
*/
public final class RenderingErrorEvent implements Event {
/**
* Creates and initializes an instance of this event.
*
* @since v1-alpha9
* @since v1-alpha0
*/
public RenderingErrorEvent() {}
/**
* {@inheritDoc}
*
* @deprecated use the {@code callEvent} method with arguments
* @see #callEvent(String)
*/
@ -50,7 +49,7 @@ public final class RenderingErrorEvent implements Event {
* Emits the event and calls all event listeners.
*
* @param error error description
* @since v1-alpha9
* @since v1-alpha0
*/
public void callEvent(@NotNull String error) {
EventHelper.invokeAnnotatedMethods(getClass(), error);

View file

@ -0,0 +1,58 @@
/*
* 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

@ -0,0 +1,44 @@
/*
* 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

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

View file

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

View file

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

View file

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

View file

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

View file

@ -17,45 +17,43 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.rendering.type;
package de.staropensource.engine.windowing.implementable;
import de.staropensource.engine.base.type.vector.Vec2i;
import de.staropensource.engine.rendering.exception.InvalidMonitorException;
import de.staropensource.engine.rendering.exception.NoMonitorsFoundException;
import de.staropensource.engine.windowing.WindowingSubsystem;
import de.staropensource.engine.windowing.exception.InvalidMonitorException;
import de.staropensource.engine.windowing.exception.NoMonitorsFoundException;
import lombok.AccessLevel;
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-alpha9
* @since v1-alpha2
*/
@SuppressWarnings({ "JavadocDeclaration" })
public final class Monitor {
public abstract class Monitor {
/**
* Contains the unique identifier.
* <p>
* This identifier is unique to every monitor and does not change during runtime.
*
* @since v1-alpha9
* @since v1-alpha2
* -- GETTER --
* Returns the unique identifier.
* <p>
* This identifier is unique to every monitor and does not change during runtime.
*
* @return unique identifier
* @since v1-alpha9
* @since v1-alpha2
*/
@Getter
private final UUID uniqueIdentifier = UUID.randomUUID();
@ -65,59 +63,47 @@ public final class Monitor {
* <p>
* This identifier is used by the windowing API to refer to a monitor and may change during runtime.
*
* @since v1-alpha9
* @since v1-alpha1
* -- 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-alpha9
* @since v1-alpha2
* -- 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-alpha9
* @since v1-alpha2
*/
private final long identifier;
@Setter(AccessLevel.PROTECTED)
private String identifier = null;
/**
* Creates and initializes an instance of this abstract class.
*
* @throws InvalidMonitorException if the monitor isn't connected
* @since v1-alpha9
* @since v1-alpha2
*/
public Monitor(long identifier) throws InvalidMonitorException {
this.identifier = identifier;
checkConnected();
}
public Monitor() {}
/**
* Returns a set of all connected monitors.
* Returns all connected monitors.
*
* @return connected monitors
* @since v1-alpha9
* @since v1-alpha2
*/
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;
return WindowingSubsystem.getInstance().getApi().getInternalApi().getMonitors();
}
/**
* Checks if this monitor is actually connected.
* Checks if the monitor is actually connected.
* If not, throws an {@link InvalidMonitorException}.
*
* @since v1-alpha9
* @since v1-alpha2
*/
public void checkConnected() throws InvalidMonitorException, NoMonitorsFoundException {
if (!isConnected())
@ -125,48 +111,34 @@ public final class Monitor {
}
/**
* Checks if this monitor is connected.
* Checks if the monitor is connected or not.
*
* @return connection status
* @since v1-alpha9
* @since v1-alpha2
*/
public boolean isConnected() throws NoMonitorsFoundException {
return glfwGetMonitorName(identifier) != null;
}
public abstract boolean isConnected() throws NoMonitorsFoundException;
/**
* Returns the name of this monitor.
* Returns the monitor name.
*
* @return monitor name
* @since v1-alpha9
* @since v1-alpha2
*/
public @NotNull String getName() throws InvalidMonitorException, NoMonitorsFoundException {
checkConnected();
return Objects.requireNonNull(glfwGetMonitorName(identifier));
}
public abstract @NotNull String getName() throws InvalidMonitorException, NoMonitorsFoundException;
/**
* Returns size of this monitor.
* Returns the monitor size.
*
* @return monitor size
* @since v1-alpha9
* @since v1-alpha2
*/
public @NotNull Vec2i getSize() throws InvalidMonitorException, NoMonitorsFoundException {
checkConnected();
GLFWVidMode videoMode = Objects.requireNonNull(glfwGetVideoMode(identifier));
return new Vec2i(videoMode.width(), videoMode.height());
}
public abstract @NotNull Vec2i getSize() throws InvalidMonitorException, NoMonitorsFoundException;
/**
* Returns refresh rate of this monitor in hertz.
* Returns the monitor refresh rate.
*
* @return monitor refresh rate
* @since v1-alpha9
* @since v1-alpha2
*/
public short getRefreshRate() throws InvalidMonitorException, NoMonitorsFoundException {
checkConnected();
return (short) Objects.requireNonNull(glfwGetVideoMode(identifier)).refreshRate();
}
public abstract short getRefreshRate() throws InvalidMonitorException, NoMonitorsFoundException;
}

View file

@ -0,0 +1,84 @@
/*
* 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

@ -0,0 +1,54 @@
/*
* 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

@ -0,0 +1,125 @@
/*
* 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

@ -0,0 +1,25 @@
/*
* 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

@ -0,0 +1,27 @@
/*
* 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

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

View file

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

View file

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

View file

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

View file

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

View file

@ -17,30 +17,29 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.rendering.type.window;
package de.staropensource.engine.windowing.type.window;
import de.staropensource.engine.rendering.RenderingSubsystemConfiguration;
import de.staropensource.engine.windowing.WindowingSubsystemConfiguration;
/**
* Controls how <a href="https://en.wikipedia.org/wiki/Screen_tearing#Vertical_synchronization">V-Sync</a> operates.
*
* @since v1-alpha9
* @since v1-alpha1
*/
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 RenderingSubsystemConfiguration#maximumFramesPerSecond}).
* {@link WindowingSubsystemConfiguration#maximumFramesPerSecond}).
*
* @since v1-alpha9
* @since v1-alpha1
*/
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-alpha9
* @since v1-alpha1
*/
ON
}

View file

@ -17,18 +17,18 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.rendering.type.window;
package de.staropensource.engine.windowing.type.window;
/**
* Contains how a window should be displayed.
*
* @since v1-alpha9
* @since v1-alpha1
*/
public enum WindowMode {
/**
* Marks the window as hidden, making it invisible and unable to be interacted with.
*
* @since v1-alpha9
* @since v1-alpha2
*/
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-alpha9
* @since v1-alpha1
*/
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-alpha9
* @since v1-alpha2
*/
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-alpha9
* @since v1-alpha2
*/
MAXIMIZED,
@ -61,25 +61,20 @@ public enum WindowMode {
* Makes the window will have the same
* size as the monitor it is currently on.
*
* @since v1-alpha9
* @since v1-alpha1
*/
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-alpha9
* @since v1-alpha1
*/
EXCLUSIVE_FULLSCREEN
}

View file

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

View file

@ -0,0 +1,32 @@
/**
* 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;
}