Start of the Device API

This commit is contained in:
Speiger 2024-05-16 12:02:31 +02:00
parent 00671c00ff
commit d7fbc6e93b
8 changed files with 438 additions and 81 deletions

View File

@ -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<T, E> implements InputDevice {
protected Long2ObjectMap<Deque<E>> queues = new Long2ObjectConcurrentOpenHashMap<>();
protected Long2ObjectMap<T> 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<E> queue = queues.get(windowId);
if(queue == null) return;
queue.add(task);
}
@Override
public void processInput(long windowId) {
Deque<E> queue = queues.get(windowId);
if(queue == null) return;
while(!queue.isEmpty()) {
process(queue.poll());
}
}
protected T getData(long windowId) {
return windowData.get(windowId);
}
}

View File

@ -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);
}

View File

@ -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<MouseData, MouseTask> {
@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();
}
}
}
}

View File

@ -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;
}
}

View File

@ -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<VideoMode> 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

View File

@ -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());
}
}

View File

@ -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<WindowCallback> callbacks = new ObjectArrayList<>();
List<IWindowListener> 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<m;i++) {
listeners.get(i).onChanged(this, Reason.CHANGE);
}
}
public void update() {
if(flags.isFlagSet(WINDOW_CHANGE)) updateViewport();
}
public void finishFrame() {
GLFW.glfwSwapBuffers(id);
}
public void destroy() {
for(int i = 0,m=listeners.size();i<m;i++) {
listeners.get(i).onChanged(this, Reason.CLOSING);
}
GLFW.glfwDestroyWindow(id);
callbacks.forEach(WindowCallback::destroy);
callbacks.clear();
}
@SuppressWarnings("unchecked")
public <T extends CallbackI> void addCallback(T listener, ReloadFunction<T> function) {
public <T extends CallbackI> WindowCallback addCallback(T listener, ReloadFunction<T> function) {
WindowCallback callback = new WindowCallback(listener, (ReloadFunction<CallbackI>)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; }
}

View File

@ -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;
}