diff --git a/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/callbacks/KeyCallback.java b/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/callbacks/KeyCallback.java new file mode 100644 index 0000000..f9fe5be --- /dev/null +++ b/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/callbacks/KeyCallback.java @@ -0,0 +1,193 @@ +/* + * STAROPENSOURCE ENGINE SOURCE FILE + * Copyright (c) 2024 The StarOpenSource Engine Contributors + * 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 . + */ + +package de.staropensource.sosengine.graphics.glfw.callbacks; + +import de.staropensource.sosengine.graphics.classes.Window; +import de.staropensource.sosengine.graphics.events.InputEvent; +import de.staropensource.sosengine.graphics.glfw.classes.WindowCallback; +import de.staropensource.sosengine.graphics.types.input.Key; +import de.staropensource.sosengine.graphics.types.input.KeyState; +import org.jetbrains.annotations.NotNull; +import org.lwjgl.glfw.GLFWKeyCallbackI; + +import static org.lwjgl.glfw.GLFW.*; + +/** + * A {@link GLFWKeyCallbackI} implementation. + */ +@SuppressWarnings({ "unused" }) +public class KeyCallback extends WindowCallback implements GLFWKeyCallbackI { + /** + * A {@link InputEvent} instance, to avoid creating too many objects + * and making too many allocations, which would potentially decrease + * performance. + * + * @since v1-alpha2 + */ + private final InputEvent event = new InputEvent(); + + /** + * Constructs this class. + * + * @param window {@link Window} class + * @since v1-alpha2 + */ + public KeyCallback(@NotNull Window window) { + super(window); + } + + /** {@inheritDoc} */ + @Override + public void invoke(long window, int key, int scancode, int action, int mods) { + // Ignore GLFW_REPEAT action + if (action == GLFW_REPEAT) + return; + + event.callEvent( + // Attached window + getAttachedWindow(), + // Key + switch (key) { + case GLFW_KEY_UNKNOWN -> Key.UNKNOWN; + case GLFW_KEY_LEFT_ALT, GLFW_KEY_RIGHT_ALT -> Key.ALT; + case GLFW_KEY_APOSTROPHE -> Key.APOSTROPHE; + case GLFW_KEY_DOWN -> Key.ARROW_DOWN; + case GLFW_KEY_LEFT -> Key.ARROW_LEFT; + case GLFW_KEY_RIGHT -> Key.ARROW_RIGHT; + case GLFW_KEY_UP -> Key.ARROW_UP; + case GLFW_KEY_BACKSPACE -> Key.BACKSPACE; + case GLFW_KEY_LEFT_BRACKET -> Key.BRACKET_LEFT; + case GLFW_KEY_RIGHT_BRACKET -> Key.BRACKET_RIGHT; + case GLFW_KEY_CAPS_LOCK -> Key.CAPS_LOCK; + case GLFW_KEY_COMMA -> Key.COMMA; + case GLFW_KEY_LEFT_CONTROL -> Key.CONTROL_LEFT; + case GLFW_KEY_RIGHT_CONTROL -> Key.CONTROL_RIGHT; + case GLFW_KEY_DELETE -> Key.DELETE; + case GLFW_KEY_END -> Key.END; + case GLFW_KEY_ENTER -> Key.ENTER; + case GLFW_KEY_EQUAL -> Key.EQUAL; + case GLFW_KEY_ESCAPE -> Key.ESCAPE; + case GLFW_KEY_F1 -> Key.FUNCTION_1; + case GLFW_KEY_F2 -> Key.FUNCTION_2; + case GLFW_KEY_F3 -> Key.FUNCTION_3; + case GLFW_KEY_F4 -> Key.FUNCTION_4; + case GLFW_KEY_F5 -> Key.FUNCTION_5; + case GLFW_KEY_F6 -> Key.FUNCTION_6; + case GLFW_KEY_F7 -> Key.FUNCTION_7; + case GLFW_KEY_F8 -> Key.FUNCTION_8; + case GLFW_KEY_F9 -> Key.FUNCTION_9; + case GLFW_KEY_F10 -> Key.FUNCTION_10; + case GLFW_KEY_F11 -> Key.FUNCTION_11; + case GLFW_KEY_F12 -> Key.FUNCTION_12; + case GLFW_KEY_F13 -> Key.FUNCTION_13; + case GLFW_KEY_F14 -> Key.FUNCTION_14; + case GLFW_KEY_F15 -> Key.FUNCTION_15; + case GLFW_KEY_F16 -> Key.FUNCTION_16; + case GLFW_KEY_F17 -> Key.FUNCTION_17; + case GLFW_KEY_F18 -> Key.FUNCTION_18; + case GLFW_KEY_F19 -> Key.FUNCTION_19; + case GLFW_KEY_F20 -> Key.FUNCTION_20; + case GLFW_KEY_F21 -> Key.FUNCTION_21; + case GLFW_KEY_F22 -> Key.FUNCTION_22; + case GLFW_KEY_F23 -> Key.FUNCTION_23; + case GLFW_KEY_F24 -> Key.FUNCTION_24; + case GLFW_KEY_F25 -> Key.FUNCTION_25; + case GLFW_KEY_GRAVE_ACCENT -> Key.GRAVE; + case GLFW_KEY_HOME -> Key.HOME; + case GLFW_KEY_INSERT -> Key.INSERT; + case GLFW_KEY_KP_ADD -> Key.KEYPAD_ADD; + case GLFW_KEY_KP_DECIMAL -> Key.KEYPAD_DECIMAL; + case GLFW_KEY_KP_DIVIDE -> Key.KEYPAD_DIVIDE; + case GLFW_KEY_KP_ENTER -> Key.KEYPAD_ENTER; + case GLFW_KEY_KP_EQUAL -> Key.KEYPAD_EQUAL; + case GLFW_KEY_KP_MULTIPLY -> Key.KEYPAD_MULTIPLY; + case GLFW_KEY_KP_0 -> Key.KEYPAD_NUMBER_0; + case GLFW_KEY_KP_1 -> Key.KEYPAD_NUMBER_1; + case GLFW_KEY_KP_2 -> Key.KEYPAD_NUMBER_2; + case GLFW_KEY_KP_3 -> Key.KEYPAD_NUMBER_3; + case GLFW_KEY_KP_4 -> Key.KEYPAD_NUMBER_4; + case GLFW_KEY_KP_5 -> Key.KEYPAD_NUMBER_5; + case GLFW_KEY_KP_6 -> Key.KEYPAD_NUMBER_6; + case GLFW_KEY_KP_7 -> Key.KEYPAD_NUMBER_7; + case GLFW_KEY_KP_8 -> Key.KEYPAD_NUMBER_8; + case GLFW_KEY_KP_9 -> Key.KEYPAD_NUMBER_9; + case GLFW_KEY_KP_SUBTRACT -> Key.KEYPAD_SUBTRACT; + case GLFW_KEY_A -> Key.LETTER_A; + case GLFW_KEY_B -> Key.LETTER_B; + case GLFW_KEY_C -> Key.LETTER_C; + case GLFW_KEY_D -> Key.LETTER_D; + case GLFW_KEY_E -> Key.LETTER_E; + case GLFW_KEY_F -> Key.LETTER_F; + case GLFW_KEY_G -> Key.LETTER_G; + case GLFW_KEY_H -> Key.LETTER_H; + case GLFW_KEY_I -> Key.LETTER_I; + case GLFW_KEY_J -> Key.LETTER_J; + case GLFW_KEY_K -> Key.LETTER_K; + case GLFW_KEY_L -> Key.LETTER_L; + case GLFW_KEY_M -> Key.LETTER_M; + case GLFW_KEY_N -> Key.LETTER_N; + case GLFW_KEY_O -> Key.LETTER_O; + case GLFW_KEY_P -> Key.LETTER_P; + case GLFW_KEY_Q -> Key.LETTER_Q; + case GLFW_KEY_R -> Key.LETTER_R; + case GLFW_KEY_S -> Key.LETTER_S; + case GLFW_KEY_T -> Key.LETTER_T; + case GLFW_KEY_U -> Key.LETTER_U; + case GLFW_KEY_V -> Key.LETTER_V; + case GLFW_KEY_W -> Key.LETTER_W; + case GLFW_KEY_X -> Key.LETTER_X; + case GLFW_KEY_Y -> Key.LETTER_Y; + case GLFW_KEY_Z -> Key.LETTER_Z; + case GLFW_KEY_MENU -> Key.MENU; + case GLFW_KEY_LEFT_SUPER -> Key.META; + case GLFW_KEY_MINUS -> Key.MINUS; + case GLFW_KEY_0 -> Key.NUMBER_0; + case GLFW_KEY_1 -> Key.NUMBER_1; + case GLFW_KEY_2 -> Key.NUMBER_2; + case GLFW_KEY_3 -> Key.NUMBER_3; + case GLFW_KEY_4 -> Key.NUMBER_4; + case GLFW_KEY_5 -> Key.NUMBER_5; + case GLFW_KEY_6 -> Key.NUMBER_6; + case GLFW_KEY_7 -> Key.NUMBER_7; + case GLFW_KEY_8 -> Key.NUMBER_8; + case GLFW_KEY_9 -> Key.NUMBER_9; + case GLFW_KEY_NUM_LOCK -> Key.NUM_LOCK; + case GLFW_KEY_PAGE_DOWN -> Key.PAGE_DOWN; + case GLFW_KEY_PAGE_UP -> Key.PAGE_UP; + case GLFW_KEY_PAUSE -> Key.PAUSE; + case GLFW_KEY_PERIOD -> Key.PERIOD; + case GLFW_KEY_PRINT_SCREEN -> Key.PRINT; + case GLFW_KEY_SCROLL_LOCK -> Key.SCROLL_LOCK; + case GLFW_KEY_SEMICOLON -> Key.SEMICOLON; + case GLFW_KEY_LEFT_SHIFT -> Key.SHIFT_LEFT; + case GLFW_KEY_RIGHT_SHIFT -> Key.SHIFT_RIGHT; + case GLFW_KEY_SLASH -> Key.SLASH; + case GLFW_KEY_SPACE -> Key.SPACE; + case GLFW_KEY_TAB -> Key.TAB; + default -> throw new IllegalStateException("Key " + key + " is invalid"); + }, + // Key state + switch (action) { + case GLFW_PRESS -> KeyState.PRESSED; + case GLFW_RELEASE -> KeyState.RELEASED; + default -> throw new IllegalStateException("Action " + action + " is invalid"); + }); + } +} diff --git a/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/classes/GlfwWindow.java b/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/classes/GlfwWindow.java index 2726335..d8ec8e3 100644 --- a/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/classes/GlfwWindow.java +++ b/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/classes/GlfwWindow.java @@ -26,12 +26,15 @@ import de.staropensource.sosengine.base.utility.Miscellaneous; import de.staropensource.sosengine.graphics.GraphicsSubsystemConfiguration; import de.staropensource.sosengine.graphics.classes.Window; import de.staropensource.sosengine.graphics.events.GraphicsApiErrorEvent; +import de.staropensource.sosengine.graphics.events.InputEvent; +import de.staropensource.sosengine.graphics.glfw.callbacks.KeyCallback; import de.staropensource.sosengine.graphics.glfw.exceptions.NotOnMainThreadException; import de.staropensource.sosengine.graphics.glfw.exceptions.WindowCreationFailureException; import de.staropensource.sosengine.graphics.types.window.VsyncMode; import de.staropensource.sosengine.graphics.types.window.WindowMode; import lombok.Getter; import org.jetbrains.annotations.NotNull; +import org.lwjgl.glfw.GLFWKeyCallback; import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryUtil; @@ -61,7 +64,14 @@ public abstract class GlfwWindow extends Window { private long identifierLong; /** + * Contains the {@link GLFWKeyCallback} used for emitting {@link InputEvent}s. + * + * @since v1-alpha2 + */ + private GLFWKeyCallback keyCallback; + // ------------------------------------------------ [ Window (de)initialization ] ------------------------------------------------ // + /** * Creates a new window. * * @param name name @@ -147,6 +157,10 @@ public abstract class GlfwWindow extends Window { // Set swap interval based on isDisallowTearing setting glfwSwapInterval(Miscellaneous.getIntegerizedBoolean(GraphicsSubsystemConfiguration.getInstance().isDisallowTearing())); + // Set callbacks + keyCallback = GLFWKeyCallback.create(new KeyCallback(this)); + glfwSetKeyCallback(identifierLong, keyCallback); + // Update the window state setSize(getSize()); setMinimumSize(getMinimumSize()); diff --git a/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/classes/WindowCallback.java b/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/classes/WindowCallback.java new file mode 100644 index 0000000..de5d9a6 --- /dev/null +++ b/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/classes/WindowCallback.java @@ -0,0 +1,60 @@ +/* + * STAROPENSOURCE ENGINE SOURCE FILE + * Copyright (c) 2024 The StarOpenSource Engine Contributors + * 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 . + */ + +package de.staropensource.sosengine.graphics.glfw.classes; + +import de.staropensource.sosengine.graphics.classes.Window; +import lombok.Getter; +import org.jetbrains.annotations.NotNull; + +/** + * Abstract class used for easily implementing + * callbacks which require a {@link Window} instance. + * + * @since v1-alpha2 + */ +@SuppressWarnings({ "unused", "JavadocDeclaration", "JavadocBlankLines" }) +public abstract class WindowCallback { + /** + * Refers to the {@link Window} instance + * this callback is tied to. + * + * @since v1-alpha2 + * + * -- GETTER -- + * Returns the {@link Window} instance + * this callback is tied to. + * + * @return attached {@link Window} instance + * @since v1-alpha2 + */ + @NotNull + @Getter + private final Window attachedWindow; + + /** + * Constructs this class. + * + * @param window {@link Window} class + * @since v1-alpha2 + */ + public WindowCallback(@NotNull Window window) { + this.attachedWindow = window; + } +} diff --git a/graphics/src/main/java/de/staropensource/sosengine/graphics/events/InputEvent.java b/graphics/src/main/java/de/staropensource/sosengine/graphics/events/InputEvent.java index bed88eb..ad9dae0 100644 --- a/graphics/src/main/java/de/staropensource/sosengine/graphics/events/InputEvent.java +++ b/graphics/src/main/java/de/staropensource/sosengine/graphics/events/InputEvent.java @@ -21,10 +21,15 @@ package de.staropensource.sosengine.graphics.events; import de.staropensource.sosengine.base.classes.Event; import de.staropensource.sosengine.base.classes.helpers.EventHelper; +import de.staropensource.sosengine.base.logging.LoggerInstance; +import de.staropensource.sosengine.base.types.CodePart; +import de.staropensource.sosengine.base.types.logging.LogIssuer; +import de.staropensource.sosengine.graphics.GraphicsSubsystemConfiguration; import de.staropensource.sosengine.graphics.classes.Window; import de.staropensource.sosengine.graphics.types.input.Key; import de.staropensource.sosengine.graphics.types.input.KeyState; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Called when a key or button is pressed. @@ -33,6 +38,13 @@ import org.jetbrains.annotations.NotNull; */ @SuppressWarnings({ "unused" }) public class InputEvent implements Event { + /** + * Logger instance for this class. + * + * @since v1-alpha2 + */ + private final LoggerInstance logger = new LoggerInstance(new LogIssuer(InputEvent.class, CodePart.ENGINE)); + /** * {@inheritDoc} * @see #callEvent(Window, Key, KeyState) @@ -45,9 +57,14 @@ public class InputEvent implements Event { /** * Calls the event and notifies all annotated methods. * + * @param window window the input originated from. May be {@code null}, depending on the Graphics API + * @param key key + * @param state key state * @since v1-alpha0 */ - public void callEvent(@NotNull Window window, @NotNull Key key, @NotNull KeyState state) { + public void callEvent(@Nullable Window window, @NotNull Key key, @NotNull KeyState state) { + if (GraphicsSubsystemConfiguration.getInstance().isDebugInput()) + logger.diag("Got input event: window=" + (window == null ? "" : window.getName() + (window.getName() == null ? "" : "(" + window.getName() + ")") + "[" + window.hashCode() + "]") + " key=" + key.name() + " state=" + state.name()); EventHelper.invokeAnnotatedMethods(getClass(), window, key, state); } }