最可靠方式是调用系统原生API:Windows用EnumDisplaySettings传NULL和ENUM_CURRENT_SETTINGS获取DEVMODE.dmDisplayFrequency;Linux用xrandr --verbose解析带*的refresh:行;macOS用CGDisplayModeGetRefreshRate配合CGMainDisplayID,且需定期重查或监听系统事件以应对动态变化。

Windows平台用 EnumDisplaySettings 获取主显示器刷新率
直接调用 WinAPI 的 EnumDisplaySettings 是最可靠的方式,它读取当前活动显示模式的实际参数,不是猜测或硬编码值。
常见错误是传入错误的设备名(比如用 "\\.\DISPLAY1" 而不是 NULL 或 ENUM_CURRENT_SETTINGS),导致返回 0 或错误刷新率(如 0 或 60 即使实际是 144)。
- 第一个参数传
NULL表示主显示器;若需枚举多屏,用EnumDisplayDevices先获取设备名 - 第二个参数必须为
ENUM_CURRENT_SETTINGS(常量值 -1),不能用具体模式索引,否则可能拿到过时或非激活设置 -
DEVMODE.dmDisplayFrequency字段才是真实刷新率,单位是 Hz,注意该字段仅在dmFields & DM_DISPLAYFREQUENCY为真时有效
DEVMODE dm = {};
dm.dmSize = sizeof(dm);
if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm)) {
if (dm.dmFields & DM_DISPLAYFREQUENCY) {
int refreshRate = dm.dmDisplayFrequency; // 如 144
}
}
Linux下通过 xrandr 解析输出(X11 环境)
xrandr 是 X11 下最通用的手段,但输出格式不固定,需解析文本而非依赖二进制接口——Wayland 下此方法失效,需换用专用协议(如 wp-desktop-delta)。
容易踩的坑是没过滤当前激活输出(* 标记),或误把“+”、“-”后缀频率(如 144.00*+)当字符串直接截取,导致解析失败。
立即学习“C++免费学习笔记(深入)”;
- 执行
xrandr --verbose(不是--query),因为只有--verbose包含refresh:行 - 匹配形如
144.00*+的行,*表示当前模式,+表示首选,优先取带*的那一行 - 用空格分隔后取第 2 字段(如
144.00*+→ 去掉非数字字符后转浮点)
示例 shell 提取逻辑:xrandr --verbose | grep "*" | grep "refresh:" | head -n1 | awk '{print }' | tr -d '+*'
macOS 用 CGDisplayModeGetRefreshRate 获取活跃模式刷新率
macOS 不暴露传统“刷新率”概念,但自 macOS 10.15 起,CGDisplayModeGetRefreshRate 可返回实际驱动的刷新率(对 Pro Display XDR、M1/M2 MacBook Pro 高刷屏有效)。
关键限制:该函数只对当前启用的显示模式有效;若应用未适配高刷(如未声明 NSHighResolutionCapable 或未启用 metalPresentsWithVSync),系统可能降频到 60Hz 并返回 60 —— 这是行为正确,不是 API 错误。
- 先用
CGMainDisplayID()获取主屏 ID,再用CGDisplayCopyDisplayMode()获取当前模式对象 - 必须检查返回值是否非 NULL,某些虚拟屏或远程桌面场景下可能为空
-
CGDisplayModeGetRefreshRate返回double,但精度有限(通常只精确到整数 Hz)
CGDirectDisplayID display = CGMainDisplayID();
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(display);
if (mode) {
double rate = CGDisplayModeGetRefreshRate(mode);
CFRelease(mode); // 必须释放
}
跨平台封装要注意刷新率动态变化
刷新率不是静态配置——用户可能随时切换缩放、分辨率或启用/禁用高刷(如 macOS 的“自动切换刷新率”),硬缓存一次结果会导致后续帧率逻辑错乱。
真正健壮的做法是:在渲染循环中定期(如每秒一次)重新查询,或监听系统事件(Windows 的 WM_DISPLAYCHANGE、macOS 的 CGDisplayRegisterReconfigurationCallback)触发重读。
尤其注意:游戏引擎或视频播放器若只在启动时查一次,遇到用户中途切到 120Hz 模式却仍按 60Hz 做 vsync,就会出现撕裂或卡顿——这不是代码写错了,是漏了动态响应。


















