Skip to content

Commit

Permalink
Add borderless fullscreen, make fullscreen choose the appropriate mon…
Browse files Browse the repository at this point in the history
…itor (#162)

* Add borderless fullscreen, make fullscreen choose the appropriate monitor

* Replace wildcard imports

---------

Co-authored-by: Raven Szewczyk <[email protected]>
  • Loading branch information
SKProCH and eigenraven authored Jul 27, 2024
1 parent c96a1f1 commit 0e16c68
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 7 deletions.
12 changes: 12 additions & 0 deletions src/main/java/me/eigenraven/lwjgl3ify/core/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public class Config {
public static boolean WINDOW_START_MAXIMIZED = false, WINDOW_START_FOCUSED = true, WINDOW_START_ICONIFIED = false;
public static boolean WINDOW_CENTERED = true;
public static boolean WINDOW_DECORATED = true;
public static boolean WINDOW_BORDERLESS_REPLACES_FULLSCREEN = false;
public static boolean WINDOW_BORDERLESS_WINDOWS_COMPATIBILITY = true;
public static boolean OPENGL_DEBUG_CONTEXT = false;
public static boolean OPENGL_SRGB_CONTEXT = false;
public static boolean OPENGL_DOUBLEBUFFER = true;
Expand Down Expand Up @@ -120,6 +122,16 @@ public static void reloadConfigObject() {
WINDOW_START_ICONIFIED = config
.getBoolean("iconified", CATEGORY_WINDOW, WINDOW_START_ICONIFIED, "Start iconified?");
WINDOW_CENTERED = config.getBoolean("centered", CATEGORY_WINDOW, WINDOW_CENTERED, "Start centered?");
WINDOW_BORDERLESS_REPLACES_FULLSCREEN = config.getBoolean(
"borderless",
CATEGORY_WINDOW,
WINDOW_BORDERLESS_REPLACES_FULLSCREEN,
"Should exclusive fullscreen mode replaced with borderless fullscreen mode");
WINDOW_BORDERLESS_WINDOWS_COMPATIBILITY = config.getBoolean(
"borderlessWindowsCompatibility",
CATEGORY_WINDOW,
WINDOW_BORDERLESS_WINDOWS_COMPATIBILITY,
"Windows-only - should borderless window have height increased by 1 to solve flickering on un-focusing");
WINDOW_DECORATED = config.getBoolean(
"decorated",
CATEGORY_WINDOW,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ public List<String> getMixins(Set<String> loadedCoreMods) {
// Improved KeyBinding handling to handle dead keys
mixins.add("game.MixinMinecraftKeyBinding");

// Adds the borderless mode
mixins.add("game.MixinBorderlessWindow");

// STB replacements for vanilla functions
if (Config.MIXIN_STBI_TEXTURE_LOADING) {
LOGGER.info("Enabling STB texture loading mixin");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package me.eigenraven.lwjgl3ify.mixins.game;

import static org.lwjgl.glfw.GLFW.*;

import net.minecraft.client.Minecraft;

import org.lwjglx.opengl.Display;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import me.eigenraven.lwjgl3ify.core.Config;

@Mixin(Minecraft.class)
public class MixinBorderlessWindow {

@Inject(method = "Lnet/minecraft/client/Minecraft;toggleFullscreen()V", at = @At("HEAD"), cancellable = true)
public void toggleFullscreen(CallbackInfo ci) {
if (Config.WINDOW_BORDERLESS_REPLACES_FULLSCREEN) {
ci.cancel();

Display.toggleBorderless();
}
}
}
159 changes: 152 additions & 7 deletions src/main/java/org/lwjglx/opengl/Display.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Optional;

import net.minecraft.client.Minecraft;

import org.lwjgl.PointerBuffer;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWCharCallback;
import org.lwjgl.glfw.GLFWCursorPosCallback;
Expand All @@ -33,6 +39,9 @@
import org.lwjglx.input.KeyCodes;
import org.lwjglx.input.Keyboard;
import org.lwjglx.input.Mouse;
import org.lwjglx.util.Rectangle;

import com.github.bsideup.jabel.Desugar;

import me.eigenraven.lwjgl3ify.Lwjgl3ify;
import me.eigenraven.lwjgl3ify.api.InputEvents;
Expand All @@ -49,6 +58,7 @@ public class Display {
private static boolean displayDirty = false;
private static boolean displayResizable = false;
private static boolean startFullscreen = false;
private static boolean borderlessInsteadOfFullscreen = true;

private static DisplayMode mode = new DisplayMode(854, 480);
private static DisplayMode desktopDisplayMode = new DisplayMode(854, 480);
Expand Down Expand Up @@ -113,7 +123,6 @@ public class Display {
*
* @param pixelFormat Describes the minimum specifications the context must fulfill.
* @param sharedDrawable The Drawable to share context with. (optional, may be null)
*
* @throws org.lwjglx.LWJGLException
*/
public static void create(PixelFormat pixelFormat, Drawable sharedDrawable) {
Expand Down Expand Up @@ -187,7 +196,7 @@ public static void create(PixelFormat pixelFormat, ContextAttribs attribs, long
glfwWindowHintString(GLFW_COCOA_FRAME_NAME, Config.COCOA_FRAME_NAME);

glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE); // request a non-hidpi framebuffer on Retina displays
// on MacOS
// on MacOS

if (Config.WINDOW_CENTERED) {
glfwWindowHint(GLFW_POSITION_X, (monitorWidth - mode.getWidth()) / 2);
Expand Down Expand Up @@ -240,7 +249,7 @@ public void invoke(long window, int key, int scancode, int action, int mods) {
}
}
if (key > GLFW_KEY_SPACE && key <= GLFW_KEY_GRAVE_ACCENT) { // Handle keys have a char. Exclude space to
// avoid extra input when switching IME
// avoid extra input when switching IME

/*
* AltGr and LAlt require special consideration.
Expand Down Expand Up @@ -285,7 +294,7 @@ public void invoke(long window, int key, int scancode, int action, int mods) {
Keyboard.addGlfwKeyEvent(window, key, scancode, action, mods, (char) (key & 0x1f));
cancelNextChar = true; // Cancel char event from ctrl key since its already handled here
} else if (action > 0) { // Delay press and repeat key event to actual char input. There is ALWAYS a
// char after them
// char after them
ingredientKeyEvent = new Keyboard.KeyEvent(
KeyCodes.glfwToLwjgl(key),
'\0',
Expand Down Expand Up @@ -677,6 +686,59 @@ public static void setDisplayModeAndFullscreen(DisplayMode mode) {
private static int savedX[] = new int[1], savedY[] = new int[1];
private static int savedW[] = new int[1], savedH[] = new int[1];

public static PositionedGLFWVidMode getTargetFullscreenMonitor() {
int x = savedX[0] + (savedW[0] / 2);
int y = savedY[0] + (savedH[0] / 2);

PointerBuffer monitors = glfwGetMonitors();
assert monitors != null;
ArrayList<PositionedGLFWVidMode> monitorInfos = new ArrayList<>(monitors.limit());
for (int i = 0; i < monitors.limit(); i++) {
long monitor = monitors.get(i);

PositionedGLFWVidMode monitorInfo = getPositionedMonitorInfo(monitor);
monitorInfos.add(monitorInfo);

if (monitorInfo.bounds.contains(x, y)) {
return monitorInfo;
}
}

// If the center of the screen doesn't contains in any monitors, try to look by intersect area
Rectangle windowBounds = new Rectangle(savedX[0], savedY[0], savedW[0], savedH[0]);
Optional<PositionedGLFWVidMode> targetMonitor = monitorInfos.stream()
.filter(
o -> !o.bounds.intersection(windowBounds, null)
.isEmpty())
.max(
Comparator.comparingInt(
o -> o.bounds.intersection(windowBounds, null)
.getArea()));

return targetMonitor.orElse(getPositionedMonitorInfo(glfwGetPrimaryMonitor()));
}

private static PositionedGLFWVidMode getPositionedMonitorInfo(long monitorId) {
IntBuffer posX = BufferUtils.createIntBuffer(1);
IntBuffer posY = BufferUtils.createIntBuffer(1);

glfwGetMonitorPos(monitorId, posX, posY);
int x = posX.get(0);
int y = posY.get(0);

GLFWVidMode vidmode = glfwGetVideoMode(monitorId);
assert vidmode != null;
return new PositionedGLFWVidMode(
x,
y,
new Rectangle(x, y, vidmode.width(), vidmode.height()),
monitorId,
vidmode);
}

@Desugar
public record PositionedGLFWVidMode(int x, int y, Rectangle bounds, long monitorId, GLFWVidMode vidMode) {}

public static void setFullscreen(boolean fullscreen) {
final long window = getWindow();
if (window == 0) {
Expand All @@ -687,17 +749,100 @@ public static void setFullscreen(boolean fullscreen) {
if (currentState == fullscreen) {
return;
}

glfwSetWindowSizeLimits(window, 0, 0, GLFW_DONT_CARE, GLFW_DONT_CARE);
if (fullscreen) {
glfwGetWindowPos(window, savedX, savedY);
glfwGetWindowSize(window, savedW, savedH);
long monitorId = glfwGetPrimaryMonitor();
final GLFWVidMode vidMode = glfwGetVideoMode(monitorId);
glfwSetWindowMonitor(window, monitorId, 0, 0, vidMode.width(), vidMode.height(), vidMode.refreshRate());
PositionedGLFWVidMode monitorInfo = getTargetFullscreenMonitor();
GLFWVidMode vidMode = monitorInfo.vidMode;
glfwSetWindowMonitor(
window,
monitorInfo.monitorId,
0,
0,
vidMode.width(),
vidMode.height(),
vidMode.refreshRate());
Minecraft.getMinecraft()
.resize(vidMode.width(), vidMode.height());
} else {
glfwSetWindowSize(window, savedW[0], savedH[0]);
glfwSetWindowMonitor(window, NULL, savedX[0], savedY[0], savedW[0], savedH[0], 0);
}
}

public static void toggleBorderless() {
setBorderless(!isBorderless());
}

public static void setBorderless(boolean toBorderless) {
final long window = getWindow();
if (window == NULL) {
return;
}
if (toBorderless) {
glfwGetWindowPos(window, savedX, savedY);
glfwGetWindowSize(window, savedW, savedH);
PositionedGLFWVidMode monitorInfo = getTargetFullscreenMonitor();
GLFWVidMode vidMode = monitorInfo.vidMode;

int height = vidMode.height();

// Fix bothered from
// https://github.com/Kir-Antipov/cubes-without-borders/blob/b38306bf17d3f0936475a3a28c4ee2be4e881a62/src/main/java/dev/kir/cubeswithoutborders/mixin/WindowMixin.java#L130
// There's a bug that causes a fullscreen window to flicker when it loses focus.
// As far as I know, this is relevant for Windows and X11 desktops.
// Fuck X11 - it's a perpetually broken piece of legacy.
// However, we do need to implement a fix for Windows desktops, as they
// are not going anywhere in the foreseeable future (sadly enough).
// This "fix" involves not bringing a window into a "proper" fullscreen mode,
// but rather stretching it 1 pixel beyond the screen's supported resolution.
if (Config.WINDOW_BORDERLESS_WINDOWS_COMPATIBILITY && System.getProperty("os.name")
.toLowerCase()
.contains("win")) {
height = height + 1;
}

glfwSetWindowSizeLimits(window, 0, 0, vidMode.width(), height);
glfwSetWindowSize(window, vidMode.width(), height);
glfwSetWindowMonitor(
window,
NULL,
monitorInfo.x,
monitorInfo.y,
vidMode.width(),
height,
vidMode.refreshRate());
} else {
glfwSetWindowSizeLimits(window, 0, 0, GLFW_DONT_CARE, GLFW_DONT_CARE);
glfwSetWindowSize(window, savedW[0], savedH[0]);
glfwSetWindowMonitor(window, NULL, savedX[0], savedY[0], savedW[0], savedH[0], 0);
}
}

public static boolean isBorderless() {
long window = Display.getWindow();
long windowMonitor = glfwGetWindowMonitor(Display.getWindow());
if (Display.getWindow() != 0 && windowMonitor == NULL) {
IntBuffer windowX = BufferUtils.createIntBuffer(1);
IntBuffer windowY = BufferUtils.createIntBuffer(1);
IntBuffer windowWidth = BufferUtils.createIntBuffer(1);
IntBuffer windowHeight = BufferUtils.createIntBuffer(1);

glfwGetWindowPos(window, windowX, windowY);
glfwGetWindowSize(window, windowWidth, windowHeight);

Display.PositionedGLFWVidMode monitorInfo = Display.getTargetFullscreenMonitor();
GLFWVidMode vidMode = monitorInfo.vidMode();

return windowX.get(0) == monitorInfo.x() && windowY.get(0) == monitorInfo.y()
&& windowWidth.get(0) == vidMode.width()
&& (windowHeight.get(0) >= vidMode.height());
}
return false;
}

public static boolean isFullscreen() {
if (getWindow() != 0) {
return glfwGetWindowMonitor(getWindow()) != NULL;
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/org/lwjglx/util/Rectangle.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ public void setBounds(ReadableRectangle r) {
height = r.getHeight();
}

/*
* Returns the area of this rectangle
*/
public int getArea() {
return width * height;
}

/*
* (Overrides)
* @see com.shavenpuppy.jglib.ReadableRectangle#getBounds(com.shavenpuppy.jglib.Rectangle)
Expand Down

0 comments on commit 0e16c68

Please sign in to comment.