From d7fbc6e93ba9971a574162da6da19d8ec339957b Mon Sep 17 00:00:00 2001 From: Speiger Date: Thu, 16 May 2024 12:02:31 +0200 Subject: [PATCH] Start of the Device API --- .../input/divices/AbstractDevice.java | 69 +++++ .../rendering/input/divices/InputDevice.java | 8 + .../rendering/input/divices/Mouse.java | 102 +++++++ .../input/window/IWindowListener.java | 11 + .../rendering/input/window/Monitor.java | 12 +- .../rendering/input/window/VideoMode.java | 10 +- .../rendering/input/window/Window.java | 255 +++++++++++++----- .../rendering/input/window/WindowManager.java | 52 +++- 8 files changed, 438 insertions(+), 81 deletions(-) create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/divices/AbstractDevice.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/divices/InputDevice.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/divices/Mouse.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/window/IWindowListener.java diff --git a/src/main/java/speiger/src/coreengine/rendering/input/divices/AbstractDevice.java b/src/main/java/speiger/src/coreengine/rendering/input/divices/AbstractDevice.java new file mode 100644 index 0000000..3bf83ef --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/divices/AbstractDevice.java @@ -0,0 +1,69 @@ +package speiger.src.coreengine.rendering.input.divices; + +import java.util.Deque; +import java.util.Objects; +import java.util.concurrent.ConcurrentLinkedDeque; + +import speiger.src.collections.longs.maps.impl.concurrent.Long2ObjectConcurrentOpenHashMap; +import speiger.src.collections.longs.maps.interfaces.Long2ObjectMap; +import speiger.src.coreengine.rendering.input.window.IWindowListener.Reason; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.utils.eventbus.Event; +import speiger.src.coreengine.utils.eventbus.EventBus; + +public abstract class AbstractDevice implements InputDevice { + protected Long2ObjectMap> queues = new Long2ObjectConcurrentOpenHashMap<>(); + protected Long2ObjectMap windowData = new Long2ObjectConcurrentOpenHashMap<>(); + protected EventBus bus; + + public void init(EventBus bus) { + this.bus = bus; + } + + public void register(Window window) { + registerCallbacks(window); + long id = window.id(); + queues.putIfAbsent(id, new ConcurrentLinkedDeque<>()); + T data = createData(id); + if(data == null) return; + windowData.put(id, data); + } + + protected void registerCallbacks(Window window) { + window.addListener(this::onClose); + } + + private void onClose(Window window, Reason reason) { + if(reason == Reason.CLOSING) { + queues.remove(window.id()); + windowData.remove(window.id()); + } + } + + protected abstract T createData(long windowId); + protected abstract void process(E task); + + protected void pushEvent(Event event) { + bus.post(event); + } + + protected void push(long windowId, E task) { + Objects.requireNonNull(task); + Deque queue = queues.get(windowId); + if(queue == null) return; + queue.add(task); + } + + @Override + public void processInput(long windowId) { + Deque queue = queues.get(windowId); + if(queue == null) return; + while(!queue.isEmpty()) { + process(queue.poll()); + } + } + + protected T getData(long windowId) { + return windowData.get(windowId); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/divices/InputDevice.java b/src/main/java/speiger/src/coreengine/rendering/input/divices/InputDevice.java new file mode 100644 index 0000000..8608f92 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/divices/InputDevice.java @@ -0,0 +1,8 @@ +package speiger.src.coreengine.rendering.input.divices; + +import speiger.src.coreengine.rendering.input.window.Window; + +public interface InputDevice { + public void register(Window window); + public void processInput(long windowId); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/divices/Mouse.java b/src/main/java/speiger/src/coreengine/rendering/input/divices/Mouse.java new file mode 100644 index 0000000..370fe4b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/divices/Mouse.java @@ -0,0 +1,102 @@ +package speiger.src.coreengine.rendering.input.divices; + +import org.lwjgl.glfw.GLFW; + +import speiger.src.collections.ints.sets.IntOpenHashSet; +import speiger.src.collections.ints.sets.IntSet; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.rendering.input.divices.Mouse.MouseData; +import speiger.src.coreengine.rendering.input.divices.Mouse.MouseTask; +import speiger.src.coreengine.rendering.input.window.Window; + +public class Mouse extends AbstractDevice { + + @Override + protected MouseData createData(long windowId) { return new MouseData(); } + protected void process(MouseTask task) { task.process(this); } + + @Override + protected void registerCallbacks(Window window) { + super.registerCallbacks(window); + window.addCallback(this::move, GLFW::glfwSetCursorPosCallback); + window.addCallback(this::click, GLFW::glfwSetMouseButtonCallback); + window.addCallback(this::enter, GLFW::glfwSetCursorEnterCallback); + window.addCallback(this::scroll, GLFW::glfwSetScrollCallback); + } + + private void move(long window, double x, double y) { + push(window, new Move(window, x, y)); + } + + private void click(long window, int button, int action, int mods) { + push(window, new Click(window, button, action, mods)); + } + + private void enter(long window, boolean enter) { + push(window, new Enter(window, enter)); + } + + private void scroll(long window, double xoffset, double yoffset) { + push(window, new Scroll(window, xoffset, yoffset)); + } + + public static class MouseData { + IntSet buttons = new IntOpenHashSet(); + Vec2i position = Vec2i.mutable(); + Vec2i movement = Vec2i.mutable(); + Vec2i scroll = Vec2i.mutable(); + boolean active = true; + } + + public interface MouseTask { + public void process(Mouse mouse); + } + + private record Move(long window, double x, double y) implements MouseTask { + @Override + public void process(Mouse mouse) { + MouseData data = mouse.getData(window); + int xOff = (int)(x - data.position.x()); + int yOff = (int)(y - data.position.y()); + data.position.set((int)x, (int)y); + //TODO Post event! + } + + } + private record Click(long window, int button, int action, int mods) implements MouseTask { + + @Override + public void process(Mouse mouse) { + MouseData data = mouse.getData(window); + if(action == GLFW.GLFW_PRESS) data.buttons.add(action); + else if(action == GLFW.GLFW_RELEASE) data.buttons.remove(action); + //TODO post event + } + + } + + private record Scroll(long window, double xoffset, double yoffset) implements MouseTask { + + @Override + public void process(Mouse mouse) { + //TODO post event + } + + } + private record Enter(long window, boolean enter) implements MouseTask { + + @Override + public void process(Mouse mouse) { + MouseData data = mouse.getData(window); + data.active = enter; + if(!enter) { + //TODO decide if this should also push phantom events clearing the pressed keys? + data.buttons.clear(); + data.movement.negate(); + data.position.negate(); + data.scroll.negate(); + } + } + + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/input/window/IWindowListener.java b/src/main/java/speiger/src/coreengine/rendering/input/window/IWindowListener.java new file mode 100644 index 0000000..9616c11 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/window/IWindowListener.java @@ -0,0 +1,11 @@ +package speiger.src.coreengine.rendering.input.window; + +public interface IWindowListener { + + public void onChanged(Window window, Reason reason); + + public static enum Reason { + CHANGE, + CLOSING; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/window/Monitor.java b/src/main/java/speiger/src/coreengine/rendering/input/window/Monitor.java index 69c099a..015b591 100644 --- a/src/main/java/speiger/src/coreengine/rendering/input/window/Monitor.java +++ b/src/main/java/speiger/src/coreengine/rendering/input/window/Monitor.java @@ -23,11 +23,11 @@ public class Monitor this.id = id; GLFWVidMode.Buffer buffer = GLFW.glfwGetVideoModes(id); for(int i = buffer.limit() - 1;i >= 0;--i) { - VideoMode videomode = new VideoMode(this, buffer.position(i)); + VideoMode videomode = new VideoMode(buffer.position(i)); if(videomode.redBits() >= 8 && videomode.greenBits() >= 8 && videomode.blueBits() >= 8) modes.add(videomode); } - defaultMode = new VideoMode(this, GLFW.glfwGetVideoMode(id)); + defaultMode = new VideoMode(GLFW.glfwGetVideoMode(id)); int[] xPos = new int[1]; int[] yPos = new int[1]; GLFW.glfwGetMonitorPos(id, xPos, yPos); @@ -40,13 +40,19 @@ public class Monitor public String name() { return GLFW.glfwGetMonitorName(id); } public int size() { return modes.size(); } public VideoMode getMode(int index) { return modes.get(index); } - public boolean contains(VideoMode mode) { return modes.indexOf(mode) != -1; } + public boolean has(VideoMode mode) { return modes.indexOf(mode) != -1; } public List videoModes() { return modes.unmodifiable(); } public VideoMode defaultMode() { return defaultMode; } public int xOffset() { return xOffset; } public int yOffset() { return yOffset; } //@formatter:on + public int getOverlap(int minX, int minY, int maxX, int maxY) { + int x = Math.max(0, Math.clamp(maxX, xOffset, xOffset + defaultMode.width()) - Math.clamp(minX, xOffset, xOffset + defaultMode.width())); + int y = Math.max(0, Math.clamp(maxY, yOffset, yOffset + defaultMode.height()) - Math.clamp(minY, yOffset, yOffset + defaultMode.height())); + return x * y; + } + @Override public boolean equals(Object obj) { return obj instanceof Monitor mon && mon.id == id; } @Override diff --git a/src/main/java/speiger/src/coreengine/rendering/input/window/VideoMode.java b/src/main/java/speiger/src/coreengine/rendering/input/window/VideoMode.java index 41c41f9..195fad2 100644 --- a/src/main/java/speiger/src/coreengine/rendering/input/window/VideoMode.java +++ b/src/main/java/speiger/src/coreengine/rendering/input/window/VideoMode.java @@ -2,13 +2,13 @@ package speiger.src.coreengine.rendering.input.window; import org.lwjgl.glfw.GLFWVidMode; -public record VideoMode(Monitor monitor, int width, int height, int redBits, int greenBits, int blueBits, int refreshrate) { +public record VideoMode(int width, int height, int redBits, int greenBits, int blueBits, int refreshrate) { - public VideoMode(Monitor monitor, GLFWVidMode buffer) { - this(monitor, buffer.width(), buffer.height(), buffer.redBits(), buffer.greenBits(), buffer.blueBits(), buffer.refreshRate()); + public VideoMode(GLFWVidMode buffer) { + this(buffer.width(), buffer.height(), buffer.redBits(), buffer.greenBits(), buffer.blueBits(), buffer.refreshRate()); } - public VideoMode(Monitor monitor, GLFWVidMode.Buffer buffer) { - this(monitor, buffer.width(), buffer.height(), buffer.redBits(), buffer.greenBits(), buffer.blueBits(), buffer.refreshRate()); + public VideoMode(GLFWVidMode.Buffer buffer) { + this(buffer.width(), buffer.height(), buffer.redBits(), buffer.greenBits(), buffer.blueBits(), buffer.refreshRate()); } } \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/input/window/Window.java b/src/main/java/speiger/src/coreengine/rendering/input/window/Window.java index 43a7ec3..0bf5a1a 100644 --- a/src/main/java/speiger/src/coreengine/rendering/input/window/Window.java +++ b/src/main/java/speiger/src/coreengine/rendering/input/window/Window.java @@ -8,25 +8,29 @@ import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.system.CallbackI; import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.math.vector.ints.Vec4i; +import speiger.src.coreengine.rendering.input.window.IWindowListener.Reason; import speiger.src.coreengine.rendering.input.window.WindowCallback.ReloadFunction; import speiger.src.coreengine.rendering.input.window.WindowManager.WindowBuilder; +import speiger.src.coreengine.rendering.utils.GLStateTracker; import speiger.src.coreengine.utils.collections.FlagHolder; public class Window { - static final int FLAG_VISIBLE = 1; - static final int FLAG_VSYNC = 2; - static final int FLAG_FOCUS = 4; - static final int FLAG_CPU_FPS_CAP = 8; - static final int FLAG_FULL_SCREEN = 16; - static final int FLAG_MAXIMIZED = 32; - static final int FLAG_BORDERLESS = 64; - static final int FLAG_CLOSE = 128; - static final int FLAG_WINDOW_CHANGE = 256; - static final int FLAG_FLOATING = 512; + static final int VISIBLE = 1; + static final int VSYNC = 2; + static final int FOCUS = 4; + static final int CPU_FPS_CAP = 8; + static final int FULL_SCREEN = 16; + static final int MAXIMIZED = 32; + static final int BORDERLESS = 64; + static final int RESIZABLE = 128; + static final int FLOATING = 256; + static final int CLOSE = 512; + static final int WINDOW_CHANGE = 1024; WindowManager manager; FlagHolder flags = new FlagHolder(); long id; - VideoMode mode; + VideoMode fullScreenMode; String title = ""; int x; int y; @@ -36,34 +40,36 @@ public class Window { int frameWidth; int frameHeight; - int windowX; - int windowY; - int windowWidth; - int windowHeight; + Vec4i[] backup = new Vec4i[] {Vec4i.mutable(), Vec4i.mutable()}; + int backupIndex = 0; final int antialiasing; List callbacks = new ObjectArrayList<>(); + List listeners = new ObjectArrayList<>(); GLCapabilities capabilities; protected Window(WindowBuilder builder) { manager = builder.manager; title = builder.title; - width = builder.width; - height = builder.height; + frameWidth = width = builder.width; + frameHeight = height = builder.height; antialiasing = builder.antiAlis; - mode = builder.mode; - flags.setFlag(FLAG_BORDERLESS, builder.borderless); - flags.setFlag(FLAG_FULL_SCREEN, builder.fullScreen); - flags.setFlag(FLAG_FLOATING, builder.floating); - flags.setFlag(FLAG_VSYNC, builder.vsync); - flags.setFlag(FLAG_CPU_FPS_CAP, builder.fpsCap); + fullScreenMode = builder.fullScreenTarget; + flags.setFlag(BORDERLESS, builder.borderless); + flags.setFlag(FULL_SCREEN, builder.fullScreen); + flags.setFlag(FLOATING, builder.floating); + flags.setFlag(RESIZABLE, builder.resizable); + flags.setFlag(VSYNC, builder.vsync); + flags.setFlag(CPU_FPS_CAP, builder.fpsCap); createDefaultWindowHints(); GLFW.glfwWindowHint(GLFW.GLFW_SAMPLES, antialiasing); - GLFW.glfwWindowHint(GLFW.GLFW_DECORATED, flags.isFlagNotSet(FLAG_FULL_SCREEN) && flags.isFlagSet(FLAG_BORDERLESS) ? 0 : 1); - GLFW.glfwWindowHint(GLFW.GLFW_FLOATING, flags.isFlagNotSet(FLAG_FULL_SCREEN) && flags.isFlagSet(FLAG_FLOATING) ? 1 : 0); - GLFW.glfwWindowHint(GLFW.GLFW_MAXIMIZED, flags.isFlagNotSet(FLAG_FULL_SCREEN) && flags.isFlagSet(FLAG_MAXIMIZED) ? 1 : 0); - if(mode == null || mode.monitor() == null) throw new IllegalStateException("Monitor or Video Mode is missing: "+mode); - Monitor monitor = mode.monitor(); + GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, flags.isFlagNotSet(RESIZABLE) ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE); + GLFW.glfwWindowHint(GLFW.GLFW_DECORATED, flags.isFlagNotSet(FULL_SCREEN) && flags.isFlagSet(BORDERLESS) ? GLFW.GLFW_FALSE : GLFW.GLFW_TRUE); + GLFW.glfwWindowHint(GLFW.GLFW_FLOATING, flags.isFlagNotSet(FULL_SCREEN) && flags.isFlagSet(FLOATING) ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE); + GLFW.glfwWindowHint(GLFW.GLFW_MAXIMIZED, flags.isFlagNotSet(FULL_SCREEN) && flags.isFlagSet(MAXIMIZED) ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE); + Monitor monitor = manager.getMonitor(builder.monitor); + if(monitor == null) throw new IllegalStateException("Monitor is missing: "+monitor); + VideoMode mode = monitor.defaultMode(); boolean fullscreen = builder.fullScreen; id = GLFW.glfwCreateWindow(fullscreen ? mode.width() : width, fullscreen ? mode.height() : height, title, builder.fullScreen ? monitor.id() : 0, manager.getPrimaryWindow()); if(id == 0) throw new IllegalStateException("Window Couldn't be Created"); @@ -74,14 +80,14 @@ public class Window { x = monitor.xOffset() + (builder.center ? (mode.width() / 2) - (width / 2) : 0); y = monitor.yOffset() + (builder.center ? (mode.height() / 2) - (height / 2) : 0); if(!fullscreen) GLFW.glfwSetWindowPos(id, x, y); - GLFW.glfwSwapInterval(flags.isFlagSet(FLAG_VSYNC) ? 1 : 0); + GLFW.glfwSwapInterval(flags.isFlagSet(VSYNC) ? 1 : 0); fetchWindowBounds(); + updateViewport(); } protected void createDefaultWindowHints() { GLFW.glfwDefaultWindowHints(); GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE); - GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_TRUE); GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4); GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 0); GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE); @@ -89,7 +95,31 @@ public class Window { } protected void createWindowListeners() { - + addCallback(this::framebuffer, GLFW::glfwSetFramebufferSizeCallback); + addCallback(this::focused, GLFW::glfwSetWindowFocusCallback); + addCallback(this::position, GLFW::glfwSetWindowPosCallback); + addCallback(this::bounds, GLFW::glfwSetWindowSizeCallback); + } + + private void framebuffer(long window, int width, int height) { + frameWidth = width; + frameHeight = height; + } + + private void focused(long window, boolean focused) { + manager.updateFocus(this, focused); + } + + private void position(long window, int x, int y) { + this.x = x; + this.y = y; + flags.setFlag(WINDOW_CHANGE); + } + + private void bounds(long window, int width, int height) { + this.width = width; + this.height = height; + flags.setFlag(WINDOW_CHANGE); } protected void fetchWindowBounds() { @@ -100,11 +130,51 @@ public class Window { this.frameHeight = height[0]; } + public void updateViewport() { + flags.clearFlag(WINDOW_CHANGE); + GLStateTracker.instance().viewPort.setDefault(0, 0, frameWidth, frameHeight); + for(int i = 0,m=listeners.size();i void addCallback(T listener, ReloadFunction function) { + public WindowCallback addCallback(T listener, ReloadFunction function) { WindowCallback callback = new WindowCallback(listener, (ReloadFunction)function); callbacks.add(callback); callback.load(id); + return callback; + } + + public void removeCallback(WindowCallback callback) { + if(callbacks.remove(callback)) { + callback.destroy(); + } + } + + public void addListener(IWindowListener listener) { + listeners.add(listener); + } + + public void removeListener(IWindowListener listener) { + listeners.remove(listener); } public void title(String name) { @@ -114,76 +184,127 @@ public class Window { } public void vsync(boolean vsync) { - if(!flags.setFlag(FLAG_VSYNC, vsync)) return; + if(!flags.setFlag(VSYNC, vsync)) return; GLFW.glfwSwapInterval(vsync ? 1 : 0); } public void fpsCap(boolean fpsCap) { - flags.setFlag(FLAG_CPU_FPS_CAP, fpsCap); + flags.setFlag(CPU_FPS_CAP, fpsCap); } public void visible(boolean visible) { - if(!flags.setFlag(FLAG_VISIBLE, visible)) return; + if(!flags.setFlag(VISIBLE, visible)) return; if(visible) GLFW.glfwShowWindow(id); else GLFW.glfwHideWindow(id); } public void floating(boolean floating) { - if(flags.isFlagNotSet(FLAG_FULL_SCREEN) && flags.setFlag(FLAG_FLOATING, floating)) { + if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(FLOATING, floating)) { GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_FLOATING, floating ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE); } } public void maximized(boolean maximized) { - if(flags.isFlagNotSet(FLAG_FULL_SCREEN) && flags.setFlag(FLAG_MAXIMIZED, maximized)) { + if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(MAXIMIZED, maximized)) { if(maximized) { - windowX = x; - windowY = y; - windowWidth = width; - windowHeight = height; + backupSize(); GLFW.glfwMaximizeWindow(id); } else { - x = windowX; - y = windowY; - width = windowWidth; - height = windowHeight; GLFW.glfwRestoreWindow(id); + restoreSize(); } fetchWindowBounds(); } } public void resizeable(boolean resizeable) { - + if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(RESIZABLE, resizeable)) { + GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_RESIZABLE, resizeable ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE); + } } public void borderless(boolean borderless) { - + if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(BORDERLESS, borderless)) { + GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_DECORATED, borderless ? GLFW.GLFW_FALSE : GLFW.GLFW_TRUE); + if(flags.isFlagSet(MAXIMIZED)) { + GLFW.glfwRestoreWindow(id); + GLFW.glfwMaximizeWindow(id); + fetchWindowBounds(); + } + } } public void fullscreen(boolean fullscreen) { - + if(flags.setFlag(FULL_SCREEN, fullscreen)) { + if(fullscreen) { + Monitor monitor = manager.getMonitorForWindow(this); + if(monitor == null) { + flags.clearFlag(FULL_SCREEN); + return; + } + backupSize(); + VideoMode mode = fullScreenMode == null || !monitor.has(fullScreenMode) ? monitor.defaultMode() : fullScreenMode; + x = 0; + y = 0; + width = mode.width(); + height = mode.height(); + GLFW.glfwSetWindowMonitor(id, monitor.id(), 0, 0, mode.width(), mode.height(), mode.refreshrate()); + } + else + { + restoreSize(); + GLFW.glfwSetWindowMonitor(id, 0, x, y, width, height, -1); + if(flags.isFlagSet(BORDERLESS)) { + GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_DECORATED, GLFW.GLFW_FALSE); + } + if(flags.isFlagSet(MAXIMIZED)) { + GLFW.glfwMaximizeWindow(id); + } + } + fetchWindowBounds(); + updateViewport(); + GLFW.glfwSwapInterval(flags.isFlagSet(VSYNC) ? 1 : 0); + } + } + + protected void backupSize() { + if(backupIndex >= backup.length) return; + backup[backupIndex++].set(x, y, width, height); + } + + protected void restoreSize() { + if(backupIndex == 0) return; + Vec4i prev = backup[--backupIndex]; + x = prev.x(); + y = prev.y(); + width = prev.z(); + height = prev.w(); } public boolean position(int x, int y) { - return false; + if(flags.isAnyFlagSet(MAXIMIZED | FULL_SCREEN) || (this.x == x && this.y == y)) return false; + this.x = x; + this.y = y; + GLFW.glfwSetWindowPos(id, x, y); + flags.setFlag(WINDOW_CHANGE); + return true; } public boolean size(int width, int height) { - return false; + if(flags.isAnyFlagSet(MAXIMIZED | FULL_SCREEN) || (this.width == width && this.height == height)) return false; + this.width = width; + this.height = height; + GLFW.glfwSetWindowSize(id, width, height); + flags.setFlag(WINDOW_CHANGE); + return true; } - public boolean width(int width) { - return false; - } - - public boolean height(int height) { - return false; - } + public boolean width(int width) { return size(width, height); } + public boolean height(int height) { return size(width, height); } public long id() { return id; } - public VideoMode mode() { return mode; } + public VideoMode desiredFullScreen() { return fullScreenMode; } public int x() { return x; } public int y() { return y; } @@ -191,16 +312,16 @@ public class Window { public int height() { return frameHeight; } public int screenWidth() { return width; } public int screenHeight() { return height; } - public boolean changed() { return flags.isFlagSet(FLAG_WINDOW_CHANGE); } + public boolean changed() { return flags.isFlagSet(WINDOW_CHANGE); } public String title() { return title; } - public boolean isVsync() { return false; } - public boolean isFPSCapped() { return false; } - public boolean isVisible() { return false; } - public boolean isFloating() { return false; } - public boolean isMaximized() { return false; } - public boolean isResizeable() { return false; } - public boolean isBorderless() { return false; } - public boolean isFullscreen() { return false; } + public boolean isVsync() { return flags.isFlagSet(VSYNC); } + public boolean shouldFPSCap() { return flags.isFlagSet(CPU_FPS_CAP); } + public boolean isVisible() { return flags.isFlagSet(VISIBLE); } + public boolean isFloating() { return flags.isFlagSet(FLOATING); } + public boolean isMaximized() { return flags.isFlagSet(MAXIMIZED); } + public boolean isResizeable() { return flags.isFlagSet(RESIZABLE); } + public boolean isBorderless() { return flags.isFlagSet(BORDERLESS); } + public boolean isFullscreen() { return flags.isFlagSet(FULL_SCREEN); } public int antialiasing() { return antialiasing; } } diff --git a/src/main/java/speiger/src/coreengine/rendering/input/window/WindowManager.java b/src/main/java/speiger/src/coreengine/rendering/input/window/WindowManager.java index 1491236..49843fe 100644 --- a/src/main/java/speiger/src/coreengine/rendering/input/window/WindowManager.java +++ b/src/main/java/speiger/src/coreengine/rendering/input/window/WindowManager.java @@ -35,26 +35,55 @@ public class WindowManager { } void addWindow(Window window) { - windows.put(window.id, window); + windows.put(window.id(), window); } void updateWindow(long oldId) { Window prev = windows.remove(oldId); if(prev == null) return; - windows.put(prev.id, prev); + windows.put(prev.id(), prev); + } + + void updateFocus(Window window, boolean focus) { + if(focus) this.activeWindow = window; + else if(activeWindow == window) activeWindow = null; } public long getPrimaryWindow() { return primaryWindow == null ? 0 : primaryWindow.id; } + public Monitor getMonitor(long id) { + return monitors.get(id); + } + + public Monitor getPriamryMonitor() { + return getMonitor(GLFW.glfwGetPrimaryMonitor()); + } + public Monitor getMonitorForWindow(Window window) { - return null; + long current = GLFW.glfwGetWindowMonitor(window.id()); + if(current != 0L) return getMonitor(current); + int minX = window.x(); + int minY = window.y(); + int maxX = minX + window.screenWidth(); + int maxY = minY + window.screenHeight(); + int largest = 0; + Monitor mon = null; + for(Monitor monitor : monitors.values()) { + int next = monitor.getOverlap(minX, minY, maxX, maxY); + if(next > largest) { + largest = next; + mon = monitor; + } + } + return mon; } public static class WindowBuilder { WindowManager manager; - VideoMode mode; + long monitor; + VideoMode fullScreenTarget; String title = ""; int width = 640; int height = 480; @@ -63,6 +92,7 @@ public class WindowManager { boolean fpsCap; boolean fullScreen; boolean maximized; + boolean resizable = true; boolean floating; boolean borderless; boolean center = true; @@ -70,7 +100,7 @@ public class WindowManager { private WindowBuilder(WindowManager manager) { this.manager = manager; - mode = manager.monitors.get(GLFW.glfwGetPrimaryMonitor()).defaultMode(); + monitor = GLFW.glfwGetPrimaryMonitor(); } public WindowBuilder title(String title) { @@ -121,6 +151,11 @@ public class WindowManager { return this; } + public WindowBuilder resizeable(boolean resizable) { + this.resizable = resizable; + return this; + } + public WindowBuilder centered(boolean center) { this.center = center; this.maximized &= !center; @@ -130,7 +165,12 @@ public class WindowManager { public WindowBuilder monitor(Monitor monitor) { if(monitor == null || monitor.defaultMode() == null) return this; - this.mode = monitor.defaultMode(); + this.monitor = monitor.id(); + return this; + } + + public WindowBuilder fullScreenMode(VideoMode mode) { + this.fullScreenTarget = mode; return this; }