refactor: replaced dirty solution with real exclusive fullscreen

This commit is contained in:
deechael 2024-10-07 13:12:26 +08:00
parent 4fc4ee149e
commit 87f924ce8e
14 changed files with 367 additions and 292 deletions

View File

@ -4,10 +4,20 @@ import net.deechael.concentration.FullscreenMode;
public interface Config {
boolean customized();
FullscreenMode getFullscreenMode();
void setFullscreenMode(FullscreenMode fullscreenMode);
void save();
static boolean isBorderless() {
return ConfigProvider.INSTANCE.ensureLoaded().getFullscreenMode() == FullscreenMode.BORDERLESS;
}
static boolean isCustomized() {
return ConfigProvider.INSTANCE.ensureLoaded().customized();
}
}

View File

@ -0,0 +1,11 @@
package net.deechael.concentration.mixin;
import net.minecraft.client.main.Main;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(Main.class)
public class MainMixin {
}

View File

@ -0,0 +1,15 @@
package net.deechael.concentration.util;
import net.deechael.concentration.mixin.accessor.WindowAccessor;
import net.minecraft.client.Minecraft;
public final class Accessors {
public static WindowAccessor window() {
return ((WindowAccessor) (Object) Minecraft.getInstance().getWindow());
}
private Accessors() {
}
}

View File

@ -7,6 +7,7 @@
"mixins": [],
"client": [
"KeyboardHandlerMixin",
"MainMixin",
"VideoSettingsScreenMixin",
"accessor.WindowAccessor"
],

View File

@ -43,6 +43,11 @@ public class ConcentrationConfigFabric implements Config {
public int height = 600;
public FullscreenMode fullscreen = FullscreenMode.BORDERLESS;
@Override
public boolean customized() {
return this.customized;
}
@Override
public FullscreenMode getFullscreenMode() {
return this.fullscreen;

View File

@ -5,12 +5,16 @@ import com.mojang.blaze3d.platform.VideoMode;
import com.mojang.blaze3d.platform.Window;
import net.deechael.concentration.ConcentrationConstants;
import net.deechael.concentration.FullscreenMode;
import net.deechael.concentration.config.Config;
import net.deechael.concentration.fabric.ConcentrationFabricCaching;
import net.deechael.concentration.fabric.config.ConcentrationConfigFabric;
import net.deechael.concentration.mixin.accessor.WindowAccessor;
import net.minecraft.client.Minecraft;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWNativeWin32;
import org.lwjgl.system.windows.User32;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@ -27,112 +31,94 @@ public class GLFWMixin {
ConcentrationConstants.LOGGER.info("================= [Concentration Start] =================");
ConcentrationConstants.LOGGER.info("Trying to modify window monitor");
// Monitor is 0 means the game is windowed mode, so the expression means if is toggling to fullscreen
Window windowInstance = Minecraft.getInstance().getWindow();
WindowAccessor accessor = (WindowAccessor) (Object) windowInstance;
if (windowInstance.isFullscreen())
if (monitor == 0L)
monitor = windowInstance.findBestMonitor().getMonitor();
Monitor monitorInstance = accessor.getScreenManager().getMonitor(monitor);
ConcentrationConstants.LOGGER.info("Current fullscreen monitor is {}", monitor);
if (monitor != 0L) {
VideoMode currentMode = monitorInstance.getCurrentMode();
ConcentrationConstants.LOGGER.info("Modifying window size limits");
GLFW.glfwSetWindowSizeLimits(window, 0, 0, width, height);
GLFW.glfwSetWindowSizeLimits(window, 0, 0, currentMode.getWidth(), currentMode.getHeight());
}
// Because whether in fullscreen mode or windowed mode
// The final step is same
// So I extracted the value then execute the final step
long finalMonitor;
int finalWidth;
int finalHeight;
int finalX;
int finalY;
Window windowInstance = Minecraft.getInstance().getWindow();
WindowAccessor accessor = (WindowAccessor) (Object) windowInstance;
if (windowInstance != null && windowInstance.isFullscreen()) {
VideoMode currentMode = monitorInstance.getCurrentMode();
ConcentrationConfigFabric config = ConcentrationConfigFabric.getInstance();
// If the game started with fullscreen mode, when switching to windowed mode, it will be forced to move to the primary monitor
// Though size and position isn't be set at initialization, but I think the window should be at the initial monitor
// So save the monitor and use the monitor value when the size isn't cached
ConcentrationFabricCaching.lastMonitor = monitor;
// Lock caching, because when switching back, the window will be once resized to the maximum value and the cache value will be wrong
// Position won't be affected, so it doesn't need lock
ConcentrationFabricCaching.cacheSizeLock = true;
ConcentrationConstants.LOGGER.info("Locked size caching");
if (config.fullscreen == FullscreenMode.NATIVE) {
ConcentrationConstants.LOGGER.info("Fullscreen mode is native, apply now!");
if (monitor == 0L)
monitor = windowInstance.findBestMonitor().getMonitor();
finalExecute(window, monitor, xpos, ypos, width, height, -1);
finalMonitor = monitor;
finalX = monitorInstance.getX();
finalY = monitorInstance.getY();
finalWidth = currentMode.getWidth();
finalHeight = currentMode.getHeight();
ConcentrationConstants.LOGGER.info("================= [Concentration End] =================");
return;
}
} else {
ConcentrationConstants.LOGGER.info("Trying to switch to borderless fullscreen mode");
// Get the monitor the user want to use and get the relative position in the system
// The monitor is always non-null because when switching fullscreen mode, there must be a monitor to put the window
Monitor monitorInstance = accessor.getScreenManager().getMonitor(monitor);
ConcentrationConstants.LOGGER.info("Current fullscreen monitor is {}", monitor);
// Remove the title bar to prevent that user can see the title bar if they put their monitors vertically connected
GLFW.glfwSetWindowAttrib(window, GLFW.GLFW_DECORATED, GLFW.GLFW_FALSE);
ConcentrationConstants.LOGGER.info("Trying to remove the title bar");
if (ConcentrationConfigFabric.getInstance().customized) {
/*if (ConcentrationConfigFabric.getInstance().customized) {
ConcentrationConstants.LOGGER.info("Customization enabled, so replace the fullscreen size with customized size");
finalX = config.x + (config.related ? monitorInstance.getX() : 0);
finalY = config.y - (config.height == height ? 1 : 0) + (config.related ? monitorInstance.getY() : 0);
finalWidth = config.width;
finalHeight = config.height + (config.height == height ? 1 : 0);
} else {
// If we make the window not decorated and set the window size exactly the same with the screen size, it will become native fullscreen mode
// to prevent this, I enlarge the height by 1 pixel and move up the window by 1 pixel which won't affect anything (unless you have a screen
// which is added above the monitor which holds the game) and will have a good experience
// Actually this is a little bit dirty, needs to find a better way to solve it
} else */
{
finalMonitor = monitor;
finalX = monitorInstance.getX();
finalY = monitorInstance.getY() - 1;
finalWidth = width;
finalHeight = height + 1;
finalY = monitorInstance.getY();
finalWidth = currentMode.getWidth();
finalHeight = currentMode.getHeight();
}
accessor.setX(finalX);
accessor.setY(finalY);
accessor.setWidth(finalWidth);
accessor.setHeight(finalHeight);
}
} else {
ConcentrationConstants.LOGGER.info("Trying to switch to windowed mode");
// Re-add the title bar so user can move the window and minimize, maximize and close the window
ConcentrationConstants.LOGGER.info("Trying to add title bar back");
GLFW.glfwSetWindowAttrib(window, GLFW.GLFW_DECORATED, GLFW.GLFW_TRUE);
finalMonitor = 0L;
ConcentrationConstants.LOGGER.info("Trying to use cached value to resize the window");
// Make sure that Concentration has cached position and size, because position size won't be cached when the game starting in fullscreen mode
finalWidth = ConcentrationFabricCaching.cachedSize ? ConcentrationFabricCaching.cachedWidth : width;
finalHeight = ConcentrationFabricCaching.cachedSize ? ConcentrationFabricCaching.cachedHeight : height;
// To make sure that even starting with fullscreen mode can also make the window stay at the current monitor
// So I set two ways to ensure the position
if (ConcentrationFabricCaching.cachedPos) {
// If Concentration cached the pos, use the cached value
finalX = ConcentrationFabricCaching.cachedX;
finalY = ConcentrationFabricCaching.cachedY;
} else if (ConcentrationFabricCaching.lastMonitor != -1) {
// or else maybe the game started with fullscreen mode, so I don't need to care about the size
// only need to make sure that the position is in the correct monitor
Monitor monitorInstance = accessor.getScreenManager().getMonitor(ConcentrationFabricCaching.lastMonitor);
VideoMode videoMode = monitorInstance.getCurrentMode();
Monitor lastMonitor = accessor.getScreenManager().getMonitor(ConcentrationFabricCaching.lastMonitor);
VideoMode videoMode = lastMonitor.getCurrentMode();
finalX = (videoMode.getWidth() - finalWidth) / 2;
finalY = (videoMode.getHeight() - finalHeight) / 2;
} else {
// if both value are missed, use the default value to prevent errors
finalX = xpos;
finalY = ypos;
}
// Unlock caching, because user can change the window size now
ConcentrationFabricCaching.cacheSizeLock = false;
ConcentrationConstants.LOGGER.info("Unlocked size caching");
}
@ -140,12 +126,52 @@ public class GLFWMixin {
ConcentrationConstants.LOGGER.info("Window size: {}, {}, position: {}, {}", finalWidth, finalHeight, finalX, finalY);
ConcentrationConstants.LOGGER.info("Trying to resize and reposition the window");
finalExecute(window, 0L, finalX, finalY, finalWidth, finalHeight, -1);
finalExecute(window, finalMonitor, finalX, finalY, finalWidth, finalHeight, -1);
if (windowInstance.isFullscreen() && !(Config.isBorderless() && Config.isCustomized())) {
GLFW.glfwSetWindowAttrib(windowInstance.getWindow(), 0x20006, 1);
long hWnd = GLFWNativeWin32.glfwGetWin32Window(windowInstance.getWindow());
if (hWnd != 0) {
User32.SetWindowPos(
hWnd,
User32.HWND_TOPMOST,
windowInstance.getX(),
windowInstance.getY(),
windowInstance.getScreenWidth(),
windowInstance.getScreenHeight(),
1027
);
User32.SetWindowLongPtr(hWnd, -16, 0x960A0000L);
User32.SetWindowLongPtr(hWnd, -20, 0x40010L);
}
if (Config.isBorderless()) {
GLFW.glfwSetWindowAttrib(windowInstance.getWindow(), 0x20006, 0);
if (System.getProperty("os.name").contains("Windows")) {
if (hWnd != 0) {
User32.SetWindowPos(
hWnd,
User32.HWND_NOTOPMOST,
windowInstance.getX(),
windowInstance.getY(),
windowInstance.getScreenWidth(),
windowInstance.getScreenHeight(),
1027
);
User32.SetWindowLongPtr(hWnd, -16, 369229824);
User32.SetWindowLongPtr(hWnd, -20, 34340880);
}
}
}
}
ConcentrationConstants.LOGGER.info("================= [Concentration End] =================");
ci.cancel();
}
@Unique
private static void finalExecute(long window, long monitor, int xpos, int ypos, int width, int height, int refreshRate) {
long __functionAddress = GLFW.Functions.SetWindowMonitor;
if (CHECKS) {

View File

@ -6,9 +6,12 @@ import com.mojang.blaze3d.platform.VideoMode;
import com.mojang.blaze3d.platform.Window;
import net.deechael.concentration.ConcentrationConstants;
import net.deechael.concentration.FullscreenMode;
import net.deechael.concentration.config.Config;
import net.deechael.concentration.fabric.ConcentrationFabricCaching;
import net.deechael.concentration.fabric.config.ConcentrationConfigFabric;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWNativeWin32;
import org.lwjgl.system.windows.User32;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@ -41,6 +44,10 @@ public abstract class WindowMixin {
@Shadow
private int height;
@Shadow
@Final
private long window;
@Inject(method = "onMove", at = @At("HEAD"))
private void inject$onMove$head(long window, int x, int y, CallbackInfo ci) {
if (!this.fullscreen) {
@ -66,19 +73,21 @@ public abstract class WindowMixin {
}
@Redirect(method = "setMode", at = @At(value = "INVOKE", remap = false, target = "Lorg/lwjgl/glfw/GLFW;glfwSetWindowMonitor(JJIIIII)V"))
private void redirect$glfwSetWindowMonitor(long window, long monitor, int xpos, int ypos, int width, int height, int refreshRate) {
private void redirect$glfwSetWindowMonitor(long window, long monitor, int xpos, int ypos, int ignored$width, int ignored$height, int ignored$refreshRate) {
ConcentrationConstants.LOGGER.info("================= [Concentration Start] =================");
ConcentrationConstants.LOGGER.info("Trying to modify window monitor");
// Monitor is 0 means the game is windowed mode, so the expression means if is toggling to fullscreen
Monitor monitorInstance = this.screenManager.getMonitor(monitor);
ConcentrationConstants.LOGGER.info("Current fullscreen monitor is {}", monitor);
if (monitor != 0L) {
VideoMode currentMode = monitorInstance.getCurrentMode();
ConcentrationConstants.LOGGER.info("Modifying window size limits");
GLFW.glfwSetWindowSizeLimits(window, 0, 0, width, height);
GLFW.glfwSetWindowSizeLimits(window, 0, 0, currentMode.getWidth(), currentMode.getHeight());
}
// Because whether in fullscreen mode or windowed mode
// The final step is same
// So I extracted the value then execute the final step
long finalMonitor;
int finalWidth;
int finalHeight;
@ -86,90 +95,67 @@ public abstract class WindowMixin {
int finalY;
if (this.fullscreen) {
VideoMode currentMode = monitorInstance.getCurrentMode();
ConcentrationConfigFabric config = ConcentrationConfigFabric.getInstance();
// If the game started with fullscreen mode, when switching to windowed mode, it will be forced to move to the primary monitor
// Though size and position isn't be set at initialization, but I think the window should be at the initial monitor
// So save the monitor and use the monitor value when the size isn't cached
ConcentrationFabricCaching.lastMonitor = monitor;
// Lock caching, because when switching back, the window will be once resized to the maximum value and the cache value will be wrong
// Position won't be affected, so it doesn't need lock
ConcentrationFabricCaching.cacheSizeLock = true;
ConcentrationConstants.LOGGER.info("Locked size caching");
if (config.fullscreen == FullscreenMode.NATIVE) {
ConcentrationConstants.LOGGER.info("Fullscreen mode is native, apply now!");
GLFW.glfwSetWindowMonitor(window, monitor, xpos, ypos, width, height, -1);
ConcentrationConstants.LOGGER.info("================= [Concentration End] =================");
return;
}
ConcentrationConstants.LOGGER.info("Fullscreen mode is native");
finalMonitor = monitor;
finalX = monitorInstance.getX();
finalY = monitorInstance.getY();
finalWidth = currentMode.getWidth();
finalHeight = currentMode.getHeight();
} else {
ConcentrationConstants.LOGGER.info("Trying to switch to borderless fullscreen mode");
// Get the monitor the user want to use and get the relative position in the system
// The monitor is always non-null because when switching fullscreen mode, there must be a monitor to put the window
Monitor monitorInstance = this.screenManager.getMonitor(monitor);
ConcentrationConstants.LOGGER.info("Current fullscreen monitor is {}", monitor);
// Remove the title bar to prevent that user can see the title bar if they put their monitors vertically connected
/*if (ConcentrationConfigFabric.getInstance().customized) {
GLFW.glfwSetWindowAttrib(window, GLFW.GLFW_DECORATED, GLFW.GLFW_FALSE);
ConcentrationConstants.LOGGER.info("Trying to remove the title bar");
if (ConcentrationConfigFabric.getInstance().customized) {
finalMonitor = 0L;
ConcentrationConstants.LOGGER.info("Customization enabled, so replace the fullscreen size with customized size");
finalX = config.x + (config.related ? monitorInstance.getX() : 0);
finalY = config.y - (config.height == height ? 1 : 0) + (config.related ? monitorInstance.getY() : 0);
finalWidth = config.width;
finalHeight = config.height + (config.height == height ? 1 : 0);
} else {
// If we make the window not decorated and set the window size exactly the same with the screen size, it will become native fullscreen mode
// to prevent this, I enlarge the height by 1 pixel and move up the window by 1 pixel which won't affect anything (unless you have a screen
// which is added above the monitor which holds the game) and will have a good experience
// Actually this is a little bit dirty, needs to find a better way to solve it
} else */
{
finalMonitor = monitor;
finalX = monitorInstance.getX();
finalY = monitorInstance.getY() - 1;
finalWidth = width;
finalHeight = height + 1;
finalY = monitorInstance.getY();
finalWidth = currentMode.getWidth();
finalHeight = currentMode.getHeight();
}
this.x = finalX;
this.y = finalY;
this.width = finalWidth;
this.height = finalHeight;
}
} else {
ConcentrationConstants.LOGGER.info("Trying to switch to windowed mode");
// Re-add the title bar so user can move the window and minimize, maximize and close the window
ConcentrationConstants.LOGGER.info("Trying to add title bar back");
GLFW.glfwSetWindowAttrib(window, GLFW.GLFW_DECORATED, GLFW.GLFW_TRUE);
finalMonitor = 0L;
ConcentrationConstants.LOGGER.info("Trying to use cached value to resize the window");
// Make sure that Concentration has cached position and size, because position size won't be cached when the game starting in fullscreen mode
finalWidth = ConcentrationFabricCaching.cachedSize ? ConcentrationFabricCaching.cachedWidth : width;
finalHeight = ConcentrationFabricCaching.cachedSize ? ConcentrationFabricCaching.cachedHeight : height;
// To make sure that even starting with fullscreen mode can also make the window stay at the current monitor
// So I set two ways to ensure the position
if (ConcentrationFabricCaching.cachedPos) {
// If Concentration cached the pos, use the cached value
finalX = ConcentrationFabricCaching.cachedX;
finalY = ConcentrationFabricCaching.cachedY;
} else if (ConcentrationFabricCaching.lastMonitor != -1) {
// or else maybe the game started with fullscreen mode, so I don't need to care about the size
// only need to make sure that the position is in the correct monitor
Monitor monitorInstance = this.screenManager.getMonitor(ConcentrationFabricCaching.lastMonitor);
VideoMode videoMode = monitorInstance.getCurrentMode();
} else if (ConcentrationFabricCaching.lastMonitor != -1 && this.screenManager.getMonitor(ConcentrationFabricCaching.lastMonitor) != null) {
Monitor lastMonitor = this.screenManager.getMonitor(ConcentrationFabricCaching.lastMonitor);
VideoMode videoMode = lastMonitor.getCurrentMode();
finalX = (videoMode.getWidth() - finalWidth) / 2;
finalY = (videoMode.getHeight() - finalHeight) / 2;
} else {
// if both value are missed, use the default value to prevent errors
finalX = xpos;
finalY = ypos;
}
// Unlock caching, because user can change the window size now
ConcentrationFabricCaching.cacheSizeLock = false;
ConcentrationConstants.LOGGER.info("Unlocked size caching");
}
@ -177,14 +163,32 @@ public abstract class WindowMixin {
ConcentrationConstants.LOGGER.info("Window size: {}, {}, position: {}, {}", finalWidth, finalHeight, finalX, finalY);
ConcentrationConstants.LOGGER.info("Trying to resize and reposition the window");
GLFW.glfwSetWindowMonitor(window, 0L, finalX, finalY, finalWidth, finalHeight, -1);
GLFW.glfwSetWindowMonitor(window, finalMonitor, finalX, finalY, finalWidth, finalHeight, -1);
if (this.fullscreen && !(Config.isBorderless() && Config.isCustomized())) {
GLFW.glfwSetWindowAttrib(this.window, 0x20006, 1);
long hWnd = GLFWNativeWin32.glfwGetWin32Window(this.window);
if (hWnd != 0) {
User32.SetWindowPos(hWnd, User32.HWND_TOPMOST, this.x, this.y, this.width, this.height, 1027);
User32.SetWindowLongPtr(hWnd, -16, 0x960A0000L);
User32.SetWindowLongPtr(hWnd, -20, 0x40010L);
}
if (Config.isBorderless()) {
GLFW.glfwSetWindowAttrib(this.window, 0x20006, 0);
if (System.getProperty("os.name").contains("Windows")) {
if (hWnd != 0) {
User32.SetWindowPos(hWnd, User32.HWND_NOTOPMOST, this.x, this.y, this.width, this.height, 1027);
User32.SetWindowLongPtr(hWnd, -16, 369229824);
User32.SetWindowLongPtr(hWnd, -20, 34340880);
}
}
}
}
ConcentrationConstants.LOGGER.info("================= [Concentration End] =================");
}
@Redirect(method = "setMode", at = @At(value = "INVOKE", remap = false, target = "Lorg/lwjgl/glfw/GLFW;glfwGetWindowMonitor(J)J"))
private long redirect$glfwGetWindowMonitor(long window) {
return 1L;
}
}

View File

@ -71,6 +71,11 @@ public final class ConcentrationConfigForge implements Config {
private ConcentrationConfigForge() {
}
@Override
public boolean customized() {
return CUSTOMIZED.get();
}
@Override
public FullscreenMode getFullscreenMode() {
return FULLSCREEN.get();

View File

@ -6,8 +6,11 @@ import com.mojang.blaze3d.platform.VideoMode;
import com.mojang.blaze3d.platform.Window;
import net.deechael.concentration.ConcentrationConstants;
import net.deechael.concentration.FullscreenMode;
import net.deechael.concentration.config.Config;
import net.deechael.concentration.forge.config.ConcentrationConfigForge;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWNativeWin32;
import org.lwjgl.system.windows.User32;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@ -41,6 +44,9 @@ public abstract class WindowMixin {
@Shadow
private int height;
@Shadow
@Final
private long window;
@Unique
private long concentration$lastMonitor = -1;
@ -89,15 +95,17 @@ public abstract class WindowMixin {
ConcentrationConstants.LOGGER.info("================= [Concentration Start] =================");
ConcentrationConstants.LOGGER.info("Trying to modify window monitor");
// Monitor is 0 means the game is windowed mode, so the expression means if is toggling to fullscreen
Monitor monitorInstance = this.screenManager.getMonitor(monitor);
ConcentrationConstants.LOGGER.info("Current fullscreen monitor is {}", monitor);
if (monitor != 0L) {
VideoMode currentMode = monitorInstance.getCurrentMode();
ConcentrationConstants.LOGGER.info("Modifying window size limits");
GLFW.glfwSetWindowSizeLimits(window, 0, 0, width, height);
GLFW.glfwSetWindowSizeLimits(window, 0, 0, currentMode.getWidth(), currentMode.getHeight());
}
// Because whether in fullscreen mode or windowed mode
// The final step is same
// So I extracted the value then execute the final step
long finalMonitor;
int finalWidth;
int finalHeight;
@ -105,37 +113,25 @@ public abstract class WindowMixin {
int finalY;
if (this.fullscreen) {
VideoMode currentMode = monitorInstance.getCurrentMode();
ConcentrationConfigForge.ensureLoaded();
// If the game started with fullscreen mode, when switching to windowed mode, it will be forced to move to the primary monitor
// Though size and position isn't be set at initialization, but I think the window should be at the initial monitor
// So save the monitor and use the monitor value when the size isn't cached
this.concentration$lastMonitor = monitor;
// Lock caching, because when switching back, the window will be once resized to the maximum value and the cache value will be wrong
// Position won't be affected, so it doesn't need lock
this.concentration$cacheSizeLock = true;
ConcentrationConstants.LOGGER.info("Locked size caching");
if (ConcentrationConfigForge.FULLSCREEN.get() == FullscreenMode.NATIVE) {
ConcentrationConstants.LOGGER.info("Fullscreen mode is native, apply now!");
GLFW.glfwSetWindowMonitor(window, monitor, xpos, ypos, width, height, -1);
ConcentrationConstants.LOGGER.info("================= [Concentration End] =================");
return;
}
finalMonitor = monitor;
finalX = monitorInstance.getX();
finalY = monitorInstance.getY();
finalWidth = currentMode.getWidth();
finalHeight = currentMode.getHeight();
} else {
ConcentrationConstants.LOGGER.info("Trying to switch to borderless fullscreen mode");
// Get the monitor the user want to use and get the relative position in the system
// The monitor is always non-null because when switching fullscreen mode, there must be a monitor to put the window
Monitor monitorInstance = this.screenManager.getMonitor(monitor);
ConcentrationConstants.LOGGER.info("Current fullscreen monitor is {}", monitor);
// Remove the title bar to prevent that user can see the title bar if they put their monitors vertically connected
GLFW.glfwSetWindowAttrib(window, GLFW.GLFW_DECORATED, GLFW.GLFW_FALSE);
ConcentrationConstants.LOGGER.info("Trying to remove the title bar");
if (ConcentrationConfigForge.CUSTOMIZED.get()) {
/*if (ConcentrationConfigForge.CUSTOMIZED.get()) {
final boolean related = ConcentrationConfigForge.RELATED.get();
final int configX = ConcentrationConfigForge.X.get();
final int configY = ConcentrationConfigForge.Y.get();
@ -148,54 +144,40 @@ public abstract class WindowMixin {
finalY = configY - (configHeight == height ? 1 : 0) + (related ? monitorInstance.getY() : 0);
finalWidth = configWidth;
finalHeight = configHeight + (configHeight == height ? 1 : 0);
} else {
// If we make the window not decorated and set the window size exactly the same with the screen size, it will become native fullscreen mode
// to prevent this, I enlarge the height by 1 pixel and move up the window by 1 pixel which won't affect anything (unless you have a screen
// which is added above the monitor which holds the game) and will have a good experience
// Actually this is a little bit dirty, needs to find a better way to solve it
} else */
{
finalMonitor = monitor;
finalX = monitorInstance.getX();
finalY = monitorInstance.getY() - 1;
finalWidth = width;
finalHeight = height + 1;
finalY = monitorInstance.getY();
finalWidth = currentMode.getWidth();
finalHeight = currentMode.getHeight();
}
this.x = finalX;
this.y = finalY;
this.width = finalWidth;
this.height = finalHeight;
}
} else {
ConcentrationConstants.LOGGER.info("Trying to switch to windowed mode");
// Re-add the title bar so user can move the window and minimize, maximize and close the window
ConcentrationConstants.LOGGER.info("Trying to add title bar back");
GLFW.glfwSetWindowAttrib(window, GLFW.GLFW_DECORATED, GLFW.GLFW_TRUE);
finalMonitor = 0L;
ConcentrationConstants.LOGGER.info("Trying to use cached value to resize the window");
// Make sure that Concentration has cached position and size, because position size won't be cached when the game starting in fullscreen mode
finalWidth = concentration$cachedSize ? concentration$cachedWidth : width;
finalHeight = concentration$cachedSize ? concentration$cachedHeight : height;
// To make sure that even starting with fullscreen mode can also make the window stay at the current monitor
// So I set two ways to ensure the position
if (this.concentration$cachedPos) {
// If Concentration cached the pos, use the cached value
finalX = concentration$cachedX;
finalY = concentration$cachedY;
} else if (this.concentration$lastMonitor != -1) {
// or else maybe the game started with fullscreen mode, so I don't need to care about the size
// only need to make sure that the position is in the correct monitor
Monitor monitorInstance = this.screenManager.getMonitor(this.concentration$lastMonitor);
VideoMode videoMode = monitorInstance.getCurrentMode();
} else if (this.concentration$lastMonitor != -1 && this.screenManager.getMonitor(this.concentration$lastMonitor) != null) {
Monitor lastMonitor = this.screenManager.getMonitor(this.concentration$lastMonitor);
VideoMode videoMode = lastMonitor.getCurrentMode();
finalX = (videoMode.getWidth() - finalWidth) / 2;
finalY = (videoMode.getHeight() - finalHeight) / 2;
} else {
// if both value are missed, use the default value to prevent errors
finalX = xpos;
finalY = ypos;
}
// Unlock caching, because user can change the window size now
this.concentration$cacheSizeLock = false;
ConcentrationConstants.LOGGER.info("Unlocked size caching");
}
@ -203,14 +185,32 @@ public abstract class WindowMixin {
ConcentrationConstants.LOGGER.info("Window size: {}, {}, position: {}, {}", finalWidth, finalHeight, finalX, finalY);
ConcentrationConstants.LOGGER.info("Trying to resize and reposition the window");
GLFW.glfwSetWindowMonitor(window, 0L, finalX, finalY, finalWidth, finalHeight, -1);
GLFW.glfwSetWindowMonitor(window, finalMonitor, finalX, finalY, finalWidth, finalHeight, -1);
if (this.fullscreen && !(Config.isBorderless() && Config.isCustomized())) {
GLFW.glfwSetWindowAttrib(this.window, 0x20006, 1);
long hWnd = GLFWNativeWin32.glfwGetWin32Window(this.window);
if (hWnd != 0) {
User32.SetWindowPos(hWnd, User32.HWND_TOPMOST, this.x, this.y, this.width, this.height, 1027);
User32.SetWindowLongPtr(hWnd, -16, 0x960A0000L);
User32.SetWindowLongPtr(hWnd, -20, 0x40010L);
}
if (Config.isBorderless()) {
GLFW.glfwSetWindowAttrib(this.window, 0x20006, 0);
if (System.getProperty("os.name").contains("Windows")) {
if (hWnd != 0) {
User32.SetWindowPos(hWnd, User32.HWND_NOTOPMOST, this.x, this.y, this.width, this.height, 1027);
User32.SetWindowLongPtr(hWnd, -16, 369229824);
User32.SetWindowLongPtr(hWnd, -20, 34340880);
}
}
}
}
ConcentrationConstants.LOGGER.info("================= [Concentration End] =================");
}
@Redirect(method = "setMode", at = @At(value = "INVOKE", remap = false, target = "Lorg/lwjgl/glfw/GLFW;glfwGetWindowMonitor(J)J"))
private long redirect$glfwGetWindowMonitor(long window) {
return 1L;
}
}

View File

@ -1,4 +1,4 @@
version=2.0.1
version=2.1.0
group=net.deechael.concentration
java_version=21

View File

@ -5,7 +5,6 @@ import net.deechael.concentration.ConcentrationConstants;
import net.deechael.concentration.config.ConcentrationConfigScreen;
import net.deechael.concentration.neoforge.compat.EmbeddiumCompat;
import net.deechael.concentration.neoforge.config.ConcentrationConfigNeoForge;
import net.minecraft.client.Minecraft;
import net.minecraft.client.OptionInstance;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;

View File

@ -1,15 +1,9 @@
package net.deechael.concentration.neoforge.config;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.electronwill.nightconfig.core.io.WritingMode;
import net.deechael.concentration.ConcentrationConstants;
import net.deechael.concentration.FullscreenMode;
import net.deechael.concentration.config.Config;
import net.neoforged.fml.loading.FMLPaths;
import net.neoforged.neoforge.common.ModConfigSpec;
import java.nio.file.Path;
public final class ConcentrationConfigNeoForge implements Config {
public final static ConcentrationConfigNeoForge INSTANCE = new ConcentrationConfigNeoForge();
@ -71,6 +65,11 @@ public final class ConcentrationConfigNeoForge implements Config {
private ConcentrationConfigNeoForge() {
}
@Override
public boolean customized() {
return CUSTOMIZED.get();
}
@Override
public FullscreenMode getFullscreenMode() {
return FULLSCREEN.get();

View File

@ -6,8 +6,11 @@ import com.mojang.blaze3d.platform.VideoMode;
import com.mojang.blaze3d.platform.Window;
import net.deechael.concentration.ConcentrationConstants;
import net.deechael.concentration.FullscreenMode;
import net.deechael.concentration.config.Config;
import net.deechael.concentration.neoforge.config.ConcentrationConfigNeoForge;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWNativeWin32;
import org.lwjgl.system.windows.User32;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@ -41,6 +44,9 @@ public abstract class WindowMixin {
@Shadow
private int height;
@Shadow
@Final
private long window;
@Unique
private long concentration$lastMonitor = -1;
@ -89,15 +95,17 @@ public abstract class WindowMixin {
ConcentrationConstants.LOGGER.info("================= [Concentration Start] =================");
ConcentrationConstants.LOGGER.info("Trying to modify window monitor");
// Monitor is 0 means the game is windowed mode, so the expression means if is toggling to fullscreen
Monitor monitorInstance = this.screenManager.getMonitor(monitor);
ConcentrationConstants.LOGGER.info("Current fullscreen monitor is {}", monitor);
if (monitor != 0L) {
VideoMode currentMode = monitorInstance.getCurrentMode();
ConcentrationConstants.LOGGER.info("Modifying window size limits");
GLFW.glfwSetWindowSizeLimits(window, 0, 0, width, height);
GLFW.glfwSetWindowSizeLimits(window, 0, 0, currentMode.getWidth(), currentMode.getHeight());
}
// Because whether in fullscreen mode or windowed mode
// The final step is same
// So I extracted the value then execute the final step
long finalMonitor;
int finalWidth;
int finalHeight;
@ -105,42 +113,30 @@ public abstract class WindowMixin {
int finalY;
if (this.fullscreen) {
VideoMode currentMode = monitorInstance.getCurrentMode();
ConcentrationConfigNeoForge.ensureLoaded();
// If the game started with fullscreen mode, when switching to windowed mode, it will be forced to move to the primary monitor
// Though size and position isn't be set at initialization, but I think the window should be at the initial monitor
// So save the monitor and use the monitor value when the size isn't cached
this.concentration$lastMonitor = monitor;
// Lock caching, because when switching back, the window will be once resized to the maximum value and the cache value will be wrong
// Position won't be affected, so it doesn't need lock
this.concentration$cacheSizeLock = true;
ConcentrationConstants.LOGGER.info("Locked size caching");
if (ConcentrationConfigNeoForge.FULLSCREEN.get() == FullscreenMode.NATIVE) {
ConcentrationConstants.LOGGER.info("Fullscreen mode is native, apply now!");
GLFW.glfwSetWindowMonitor(window, monitor, xpos, ypos, width, height, -1);
ConcentrationConstants.LOGGER.info("================= [Concentration End] =================");
return;
}
finalMonitor = monitor;
finalX = monitorInstance.getX();
finalY = monitorInstance.getY();
finalWidth = currentMode.getWidth();
finalHeight = currentMode.getHeight();
} else {
ConcentrationConstants.LOGGER.info("Trying to switch to borderless fullscreen mode");
// Get the monitor the user want to use and get the relative position in the system
// The monitor is always non-null because when switching fullscreen mode, there must be a monitor to put the window
Monitor monitorInstance = this.screenManager.getMonitor(monitor);
ConcentrationConstants.LOGGER.info("Current fullscreen monitor is {}", monitor);
// Remove the title bar to prevent that user can see the title bar if they put their monitors vertically connected
GLFW.glfwSetWindowAttrib(window, GLFW.GLFW_DECORATED, GLFW.GLFW_FALSE);
ConcentrationConstants.LOGGER.info("Trying to remove the title bar");
if (ConcentrationConfigNeoForge.CUSTOMIZED.get()) {
final boolean related = ConcentrationConfigNeoForge.RELATED.get();
final int configX = ConcentrationConfigNeoForge.X.get();
final int configY = ConcentrationConfigNeoForge.Y.get();
final int configWidth = ConcentrationConfigNeoForge.WIDTH.get();
final int configHeight = ConcentrationConfigNeoForge.HEIGHT.get();
/*if (ConcentrationConfigForge.CUSTOMIZED.get()) {
final boolean related = ConcentrationConfigForge.RELATED.get();
final int configX = ConcentrationConfigForge.X.get();
final int configY = ConcentrationConfigForge.Y.get();
final int configWidth = ConcentrationConfigForge.WIDTH.get();
final int configHeight = ConcentrationConfigForge.HEIGHT.get();
ConcentrationConstants.LOGGER.info("Customization enabled, so replace the fullscreen size with customized size");
@ -148,54 +144,40 @@ public abstract class WindowMixin {
finalY = configY - (configHeight == height ? 1 : 0) + (related ? monitorInstance.getY() : 0);
finalWidth = configWidth;
finalHeight = configHeight + (configHeight == height ? 1 : 0);
} else {
// If we make the window not decorated and set the window size exactly the same with the screen size, it will become native fullscreen mode
// to prevent this, I enlarge the height by 1 pixel and move up the window by 1 pixel which won't affect anything (unless you have a screen
// which is added above the monitor which holds the game) and will have a good experience
// Actually this is a little bit dirty, needs to find a better way to solve it
} else */
{
finalMonitor = monitor;
finalX = monitorInstance.getX();
finalY = monitorInstance.getY() - 1;
finalWidth = width;
finalHeight = height + 1;
finalY = monitorInstance.getY();
finalWidth = currentMode.getWidth();
finalHeight = currentMode.getHeight();
}
this.x = finalX;
this.y = finalY;
this.width = finalWidth;
this.height = finalHeight;
}
} else {
ConcentrationConstants.LOGGER.info("Trying to switch to windowed mode");
// Re-add the title bar so user can move the window and minimize, maximize and close the window
ConcentrationConstants.LOGGER.info("Trying to add title bar back");
GLFW.glfwSetWindowAttrib(window, GLFW.GLFW_DECORATED, GLFW.GLFW_TRUE);
finalMonitor = 0L;
ConcentrationConstants.LOGGER.info("Trying to use cached value to resize the window");
// Make sure that Concentration has cached position and size, because position size won't be cached when the game starting in fullscreen mode
finalWidth = concentration$cachedSize ? concentration$cachedWidth : width;
finalHeight = concentration$cachedSize ? concentration$cachedHeight : height;
// To make sure that even starting with fullscreen mode can also make the window stay at the current monitor
// So I set two ways to ensure the position
if (this.concentration$cachedPos) {
// If Concentration cached the pos, use the cached value
finalX = concentration$cachedX;
finalY = concentration$cachedY;
} else if (this.concentration$lastMonitor != -1) {
// or else maybe the game started with fullscreen mode, so I don't need to care about the size
// only need to make sure that the position is in the correct monitor
Monitor monitorInstance = this.screenManager.getMonitor(this.concentration$lastMonitor);
VideoMode videoMode = monitorInstance.getCurrentMode();
} else if (this.concentration$lastMonitor != -1 && this.screenManager.getMonitor(this.concentration$lastMonitor) != null) {
Monitor lastMonitor = this.screenManager.getMonitor(this.concentration$lastMonitor);
VideoMode videoMode = lastMonitor.getCurrentMode();
finalX = (videoMode.getWidth() - finalWidth) / 2;
finalY = (videoMode.getHeight() - finalHeight) / 2;
} else {
// if both value are missed, use the default value to prevent errors
finalX = xpos;
finalY = ypos;
}
// Unlock caching, because user can change the window size now
this.concentration$cacheSizeLock = false;
ConcentrationConstants.LOGGER.info("Unlocked size caching");
}
@ -203,14 +185,32 @@ public abstract class WindowMixin {
ConcentrationConstants.LOGGER.info("Window size: {}, {}, position: {}, {}", finalWidth, finalHeight, finalX, finalY);
ConcentrationConstants.LOGGER.info("Trying to resize and reposition the window");
GLFW.glfwSetWindowMonitor(window, 0L, finalX, finalY, finalWidth, finalHeight, -1);
GLFW.glfwSetWindowMonitor(window, finalMonitor, finalX, finalY, finalWidth, finalHeight, -1);
if (this.fullscreen && !(Config.isBorderless() && Config.isCustomized())) {
GLFW.glfwSetWindowAttrib(this.window, 0x20006, 1);
long hWnd = GLFWNativeWin32.glfwGetWin32Window(this.window);
if (hWnd != 0) {
User32.SetWindowPos(hWnd, User32.HWND_TOPMOST, this.x, this.y, this.width, this.height, 1027);
User32.SetWindowLongPtr(hWnd, -16, 0x960A0000L);
User32.SetWindowLongPtr(hWnd, -20, 0x40010L);
}
if (Config.isBorderless()) {
GLFW.glfwSetWindowAttrib(this.window, 0x20006, 0);
if (System.getProperty("os.name").contains("Windows")) {
if (hWnd != 0) {
User32.SetWindowPos(hWnd, User32.HWND_NOTOPMOST, this.x, this.y, this.width, this.height, 1027);
User32.SetWindowLongPtr(hWnd, -16, 369229824);
User32.SetWindowLongPtr(hWnd, -20, 34340880);
}
}
}
}
ConcentrationConstants.LOGGER.info("================= [Concentration End] =================");
}
@Redirect(method = "setMode", at = @At(value = "INVOKE", remap = false, target = "Lorg/lwjgl/glfw/GLFW;glfwGetWindowMonitor(J)J"))
private long redirect$glfwGetWindowMonitor(long window) {
return 1L;
}
}