如何使SDL窗口不可移动?C语言SDL2+OpenGL游戏开发疑问
嘿,我之前做SDL2项目的时候也碰到过这个问题——SDL2确实没提供直接让窗口不可移动的内置标志位,但我们可以绕个弯,调用对应平台的原生API来实现,毕竟SDL底层也是靠系统窗口管理器干活的。下面分Windows和Linux两个最常用的平台给你说具体怎么做:
Windows平台实现方案
首先得引入Windows的头文件,注意要放在SDL头文件前面,避免编译冲突:
#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <SDL2/SDL.h> #include <SDL2/SDL_opengl.h>
创建好SDL窗口之后,我们需要拿到它的原生Windows窗口句柄,然后通过修改窗口过程来拦截拖动操作:
// 全局变量保存原窗口过程 WNDPROC g_oldWindowProc = NULL; // 自定义窗口过程,拦截拖动相关消息 LRESULT CALLBACK CustomWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { // 当系统检测到鼠标点击标题栏时,我们把它伪装成点击窗口内容区域 if (uMsg == WM_NCHITTEST) { LRESULT hitResult = DefWindowProc(hwnd, uMsg, wParam, lParam); if (hitResult == HTCAPTION) { return HTCLIENT; } return hitResult; } // 其他消息交给原窗口处理 return CallWindowProc(g_oldWindowProc, hwnd, uMsg, wParam, lParam); } int main(int argc, char* argv[]) { SDL_Init(SDL_INIT_VIDEO); // 创建你的OpenGL窗口(参数换成你自己的) SDL_Window* window = SDL_CreateWindow( "固定位置的游戏窗口", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN ); // 获取Windows窗口的原生句柄 SDL_SysWMinfo wmInfo; SDL_VERSION(&wmInfo.version); if (SDL_GetWindowWMInfo(window, &wmInfo)) { HWND hwnd = wmInfo.info.win.window; // 替换窗口过程为我们自定义的 g_oldWindowProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)CustomWindowProc); // 额外:可以去掉窗口的最大化和可缩放按钮,让窗口看起来更“固定” LONG windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); windowStyle &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX); SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle); // 刷新窗口样式 SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); } // 这里放你的OpenGL初始化和游戏循环代码... SDL_DestroyWindow(window); SDL_Quit(); return 0; }
原理很简单:通过拦截WM_NCHITTEST消息,把用户点击标题栏的操作伪装成点击窗口内容区域,这样系统就不会触发窗口拖动的逻辑了。
Linux(X11)平台实现方案
Linux下我们用X11的API来告诉窗口管理器“这个窗口位置是固定的,别让用户拖动”:
#include <SDL2/SDL.h> #include <SDL2/SDL_opengl.h> #include <X11/Xlib.h> int main(int argc, char* argv[]) { SDL_Init(SDL_INIT_VIDEO); SDL_Window* window = SDL_CreateWindow( "固定位置的游戏窗口", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN ); // 获取X11窗口的原生信息 SDL_SysWMinfo wmInfo; SDL_VERSION(&wmInfo.version); if (SDL_GetWindowWMInfo(window, &wmInfo)) { Display* display = wmInfo.info.x11.display; Window x11Window = wmInfo.info.x11.window; // 申请并设置窗口大小提示,标记位置为程序固定 XSizeHints* sizeHints = XAllocSizeHints(); if (sizeHints != NULL) { XGetWMNormalHints(display, x11Window, sizeHints, NULL); // 开启PPosition标志,通知窗口管理器位置不可改 sizeHints->flags |= PPosition; XSetWMNormalHints(display, x11Window, sizeHints); XFree(sizeHints); } } // 这里放你的OpenGL初始化和游戏循环代码... SDL_DestroyWindow(window); SDL_Quit(); return 0; }
这个方法是通过X11的XSizeHints设置PPosition标志,主流窗口管理器(GNOME、KDE、XFCE等)都会遵守这个设置,禁止用户拖动窗口。
编译注意事项
- Windows平台编译时,需要链接
user32.lib(一般IDE会自动处理,手动编译的话加-luser32)。 - Linux平台编译时,要链接X11库,编译命令里加
-lX11。
内容的提问来源于stack exchange,提问作者Zacarias Fifa
相关产品推荐
相关产品推荐

