windows上最可靠方式是先用enumdisplaydevices获取主显示器名,再调用enumdisplaysettings读取dmdisplayfrequency;linux推荐xrandr --verbose解析;macos必须用cgdisplaymodegetrefreshrate;跨平台时需注意api返回值未必反映真实生效刷新率。

Windows平台用 EnumDisplaySettings 查刷新率最可靠
在 Windows 上,EnumDisplaySettings 是获取当前显示器刷新率的官方且稳定方式。它不依赖显卡驱动私有接口,也不需要 DirectX 或 DXGI 初始化,适合轻量级程序或配置工具。
常见错误是直接调用 EnumDisplaySettings 时传入错误的设备名(比如硬编码 "\\.\DISPLAY1"),导致返回 FALSE 或数据为 0。正确做法是先用 EnumDisplayDevices 获取主显示器的真实名称。
- 调用
EnumDisplayDevices(NULL, 0, &dd, 0)获取第一个显示设备(通常是主屏),dd.DeviceName才是后续EnumDisplaySettings的合法lpszDeviceName -
DEVMODE.dmDisplayFrequency字段即为垂直刷新率(单位:Hz),但仅当dmFields & DM_DISPLAYFREQUENCY为真时才有效 - 如果想查所有支持的刷新率,需循环调用
EnumDisplaySettings并递增iModeNum,直到返回FALSE;注意从 0 开始,不是 1
Linux 下通过 xrandr 命令解析比 X11 API 更简单
在 X11 环境中,直接调用 Xlib 的 XRRGetScreenInfo 等函数容易出错:需手动管理资源、处理屏幕缩放、多屏时序混乱,且 Wayland 会完全失效。对大多数 CLI 工具或脚本来说,跑一次 xrandr --verbose 并 grep 解析更稳。
关键点在于识别当前激活的模式行——它带 * 标记,且紧跟在 connected 行之后;刷新率藏在类似 60.00*+ 这样的字段里,* 表示当前使用,+ 表示首选。
- 命令示例:
xrandr --verbose | grep -A1 " connected" | grep "*" | awk '{print $NF}' | sed 's/[^0-9.]//g' - 不要只匹配
60.00这类字面值,因为不同分辨率下同个物理屏可能报告不同刷新率(如 1920x1080@60 vs 1280x720@120) - 若系统禁用了
xrandr(如某些嵌入式镜像),或运行在 Wayland(GNOME/KDE 默认),该方法失效;此时只能查/sys/class/display-timing/(极少数内核启用)或放弃
macOS 必须用 CGDisplayModeGetRefreshRate,别碰私有 API
macOS 没有公开的 C++ 系统 API 直接暴露刷新率,CGDisplayModeGetRefreshRate 是唯一被 Apple 文档明确支持的方式。试图从 CGDisplayModeGetIOKitDisplayMode 提取 EDID 或解析 IOSurface 属性,不仅不稳定,而且在 macOS 12+ 上大概率 crash。
注意:该函数返回的是浮点数,但实际精度有限——多数 Retina 屏报告 60.0 或 59.97,ProMotion 屏(如 MacBook Pro 14"/16")会返回 120.0,但仅当启用了“自动切换刷新率”且内容帧率匹配时才生效。
- 必须先用
CGMainDisplayID()或CGGetActiveDisplayList获取有效 display ID,不能用硬编码数字 - 返回值为 0.0 表示失败(如 display ID 无效、权限不足、或当前处于睡眠状态),需做 fallback 处理(例如默认设为 60)
- 该函数线程安全,但频繁调用(如每帧)无意义——刷新率是静态配置,除非用户手动在系统设置里更改
跨平台封装要注意刷新率是否“真实生效”
很多库(如 SDL2、GLFW)提供 SDL_GetDesktopDisplayMode 或 glfwGetVideoMode,它们返回的 refreshRate 字段其实是“显卡声称支持的最大值”,而非当前实际使用的值。尤其在 macOS ProMotion 或 Windows 自适应同步(FreeSync/G-Sync)开启时,这个值和真实帧间隔严重不符。
真正需要动态帧率信息(如游戏 vsync 控制、视频播放器调度)时,不能依赖初始化时读到的刷新率,而应结合 VSYNC 状态、垂直空白中断信号(需内核模块)、或高精度计时器测得的实际帧间隔。
- 例如:连续采集 60 帧的
std::chrono::steady_clock::now()时间戳,算出平均帧间隔再倒推 Hz,比任何 API 返回值都接近真实 - Windows 上
DXGI_OUTPUT_DESC.RefreshRate和EnumDisplaySettings结果一致,但仅反映桌面模式,不反映全屏独占或 HDR 模式下的动态调整 - 别把“显示器标称刷新率”和“应用能稳定输出的帧率”混为一谈——后者受 CPU/GPU 负载、驱动队列、合成器延迟等多重影响
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











