在GLFW的无边框全屏窗口中,明明被设定为不隐藏光标,光标却仍然不可见
我最近开始了一个只用GLFW、GLAD和 GLM的游戏开发项目。一路顺风,直到我试图把窗口从普通的1280x720改为(无边框)全屏窗口:
我的光标在其他窗口上是可见且可用的,但在游戏聚焦时,光标在主显示器上会变得不可见。光标仍然可以在窗口后面正常移动,只是完全看不见。
通过Discord屏幕共享的朋友看到游戏在第一帧就像被冻结了一样,但却能看到我的光标在屏幕上移动,而我这边看到的游戏却很正常却没有光标。不太确定这意味着什么,但或许有帮助……
下面是负责创建窗口的代码。这是代码中唯一对窗口有操作的地方。即使(我认为)我明确把光标设为可见,我似乎仍然无法阻止它在聚焦的游戏窗口上被隐藏:
#include <GLFW/glfw3.h>
class WindowSystem {
public:
WindowSystem();
~WindowSystem();
[[nodiscard]] GLFWwindow* getWindow() const { return window; }
[[nodiscard]] bool keepWindowOpen() const { return !glfwWindowShouldClose(window); }
void windowSwap() const;
static int WIDTH;
static int HEIGHT;
private:
GLFWwindow* window;
};
#include <iostream>
#include <glad/glad.h>
#include "systems/core/WindowSystem.hpp"
// Defaults
int WindowSystem::WIDTH = 1280;
int WindowSystem::HEIGHT = 720;
WindowSystem::WindowSystem() {
if (!glfwInit()) std::cerr << "Failed to initialize GLFW" << std::endl;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
WIDTH = mode->width;
HEIGHT = mode->height;
glfwWindowHint(GLFW_RED_BITS, mode->redBits);
glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits);
glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits);
glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate);
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
window = glfwCreateWindow(WIDTH, HEIGHT, "MV_Game", nullptr, nullptr);
if (window == nullptr) std::cerr << "Failed to open GLFW window" << std::endl;
glfwMakeContextCurrent(window);
glfwSetWindowPos(window, 0, 0);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)))
std::cerr << "Failed to initialize GLAD" << std::endl;
}
WindowSystem::~WindowSystem() {
glfwDestroyWindow(window);
glfwTerminate();
}
void WindowSystem::windowSwap() const {
glfwSwapBuffers(window);
glfwPollEvents();
}
像 glfwGetKey、glfwGetCursorPos 和 glfwSetMouseButtonCallback 这样的函数虽然按预期完美工作,顺便说一句,是光标在明确被设置为不可见时却被隐藏了。我已经尝试了我能找到的所有方法,现在已经完全束手无策。要在我的电脑上复现,只需要下面这个main,当然还要GLFW和 GLAD。
#include <memory>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "systems/core/WindowSystem.hpp"
int main() {
const auto windowSystem = std::make_unique<WindowSystem>();
while (!glfwWindowShouldClose(windowSystem->getWindow())) {
glClear(GL_COLOR_BUFFER_BIT);
// Rendering stuff
windowSystem->windowSwap();
}
}
更新
虽然窗口初始化确实还有改进空间,但真正的元凶最终是Medal(裁剪软件)。虽然关闭Medal似乎可以修复光标问题,但这并不理想……此外,在Discord的屏幕共享中,窗口看起来仍然像是冻结。
解决方案
我认为光标这一行并不是这里的实际问题所在。
这个:
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
已经是让GLFW使用正常可见光标的正确方式了。所以如果这个:
std::cout << glfwGetInputMode(window, GLFW_CURSOR) << "\n";
打印出 GLFW_CURSOR_NORMAL,那么GLFW并不是故意隐藏它。
更大的问题在于你的窗口并没有真正创建为全屏。你是这样做的:
window = glfwCreateWindow(WIDTH, HEIGHT, "MV_Game", nullptr, nullptr);
因为monitor参数是 nullptr,这会创建一个普通的窗口模式窗口。然后你移除装饰并把它移动到 (0, 0),看起来像无边框全屏,实际上仍然只是一个普通的无装饰窗口。
先尝试把它真正创建为一个全屏窗口:
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
WIDTH = mode->width;
HEIGHT = mode->height;
glfwWindowHint(GLFW_RED_BITS, mode->redBits);
glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits);
glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits);
glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate);
window = glfwCreateWindow(WIDTH, HEIGHT, "MV_Game", monitor, nullptr);
if (!window)
throw std::runtime_error("Failed to open GLFW window");
glfwMakeContextCurrent(window);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
另外,为了真正的全屏,请移除以下这些:
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
glfwSetWindowPos(window, 0, 0);
它们是给普通窗口用的。全屏窗口不需要手动禁用装饰。
如果你真的想要伪无边框全屏,不要把 (0, 0) 硬编码。使用显示器的位置:
int mx, my;
glfwGetMonitorPos(monitor, &mx, &my);
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
window = glfwCreateWindow(WIDTH, HEIGHT, "MV_Game", nullptr, nullptr);
if (!window)
throw std::runtime_error("Failed to open GLFW window");
glfwSetWindowPos(window, mx, my);
glfwMakeContextCurrent(window);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
把 (0, 0) 硬编码在多显示器设置上可能会出错,即使大多数时候看起来没问题。
Discord的情况也让我觉得这不一定是你的输入代码的问题。Discord常常把游戏和光标作为分层捕获。因此朋友看到光标而游戏看起来像被冻结,并不能真正证明GLFW在隐藏它。听起来更像是Windows/GPU/合成器的问题。
接下来我会检查这些:
std::cout << glfwGetVersionString() << "\n";
std::cout << glfwGetInputMode(window, GLFW_CURSOR) << "\n";
std::cout << (glfwGetWindowMonitor(window) != nullptr) << "\n";
如果光标模式是正常的但仍然消失,就停止调试鼠标输入。更新GLFW,更新GPU驱动,禁用诸如Discord、NVIDIA、AMD、Xbox Game Bar等覆盖工具。这更像是平台/显示的问题,而不是GLFW的输入问题。