全屏切换流程
基本信息
| 属性 | 值 |
|---|---|
| 核心方法 | 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()。
相关条目
- 显示器选择与边界计算 - 第 3 步的分支优先级
- 启动初始化 - 启动时如何触发第一次切换
- 全屏键位接管 -
currentState与toggleFullScreen的关系 - 配置项 -
enableFullscreenWindowed总开关