全屏切换流程

基本信息

属性 值
核心方法 ClientProxy#toggleFullScreen(boolean goFullScreen, int desiredMonitor)
状态位 public static boolean ClientProxy.currentState
窗口 API org.lwjglx.opengl.Display(lwjgl3ify)+ org.lwjgl.glfw.GLFW
声明的 GLFW 依赖 org.lwjgl:lwjgl-glfw:3.3.2
调用方 KeyInputEventHandler、DrawScreenEventHandler、registerKeyBindings、performStartupChecks

服务端代理 ServerProxy 把 toggleFullScreen 实现为空方法, 所以这个 mod 在专用服务器上不做任何事。

调用前的两个短路

if (Display.isFullscreen()) {
    currentState = true;
    LogHelper.warn("Display is actual fullscreen! Is Minecraft starting with the option set?");
}
if (currentState == goFullScreen && !Display.isFullscreen()) return;

第一段是"纠正":如果 LWJGL 报告自己在真全屏(这个 mod 不接受的状态), 就把 currentState 拉齐成 true 并告警。第二段是幂等保护—— 状态已经对得上且不是真全屏时直接返回。

完整步骤

# 操作 说明
1 保存 Display.getX/Y/Width/Height 仅当 goFullScreen 时存入 _savedWindowedBounds
2 glfwSetWindowAttrib(GLFW_DECORATED, goFullScreen ? GLFW_FALSE : GLFW_TRUE) 去边框就是这一行
3 getAppropriateScreenBounds(...) 见 显示器选择与边界计算
4 newBounds = goFullScreen ? screenBounds : _savedWindowedBounds 为 null 时退回 screenBounds
5 glfwMaximizeWindow / glfwRestoreWindow 二选一
6 glfwSetWindowSize(w, h) 按 newBounds
7 glfwSetWindowAttrib(GLFW_RESIZABLE, goFullScreen ? GLFW_FALSE : GLFW_TRUE) 全屏时禁止拖拽缩放
8 Display.setFullscreen(false) 关键:永远不让 LWJGL 自己的全屏生效
9 Display.update() 提交窗口变更
10 glfwSetWindowPos(x, y) 定位
11 callMinecraftResizeMethod(w, h) 让 Minecraft 重建 framebuffer
12 currentState = goFullScreen 收尾

第 8 步是整个 mod 的立足点:无边框窗口是"用 GLFW 改装饰 + 用 LWJGL 报告非全屏" 拼出来的,而不是 LWJGL 的全屏模式。

帧缓冲重建

callMinecraftResizeMethod(:128-146)不直接调 Minecraft.resize,而是反射:

Method resizeMethod = ReflectionHelper.findMethod(
    Minecraft.class, inst, new String[] { "func_71370_a", "resize" }, args);
if (resizeMethod != null) { Display.update(); resizeMethod.invoke(inst, w, h); }

同时给出混淆名 func_71370_a 和非混淆名 resize,以适配不同映射环境。 找不到方法时只 LogHelper.warn 一条含"是否为正确版本"的提示,不抛异常。

退出全屏时的边界回退

第 4 步里,若 _savedWindowedBounds 为 null(进全屏的那次调用没存下值), 就直接用当前的 screenBounds 当作窗口边界。 由于 if (goFullScreen) _savedWindowedBounds = currentCoordinates; 只在进入时写入, 正常流程不会出现 null;null 只可能来自"没经过进入分支就请求退出"。

触发路径不一致

同一个"切换"动作有两条触发路径,按键判定方式不一样:

路径 判定 触发条件
KeyInputEventHandler(游戏中) KeyBinding.isPressed() 标准"按下沿"
DrawScreenEventHandler(GUI 中) Keyboard.isKeyDown(keyCode) 按住,靠 _lastState 记录沿

含义是:GUI 打开时必须按住该键才能切一次(松开再按住触发下一次), 游戏中则是正常的一次按键。两条路径都调用同一个 toggleFullScreen, desiredMonitor 也都取 ConfigurationHandler.instance().getFullscreenMonitor()。

相关条目