getsystemmetrics(sm_cxscreen/sm_cyscreen)返回主屏逻辑像素尺寸(受dpi缩放影响),物理分辨率需结合enumdisplaymonitors、getmonitorinfo和getdpiformonitor计算,缩放比例为dpi/96.0×100%。

Windows 下用 GetSystemMetrics 和 GetDpiForWindow 获取分辨率与缩放
在 Windows 上,GetSystemMetrics(SM_CXSCREEN) 和 GetSystemMetrics(SM_CYSCREEN) 返回的是**逻辑像素尺寸**(即 DPI 缩放后、应用程序看到的尺寸),不是物理分辨率。如果你要获取原始物理分辨率(比如 3840×2160),得用 EnumDisplayMonitors + GetMonitorInfo 拿 rcMonitor,再结合 GetDpiForMonitor 算出缩放比例。
缩放值通常以百分比形式呈现(如 125%、150%),但 Windows API 返回的是 DPI 值(如 120、144)。换算公式是:scale_percent = (dpi / 96.0) * 100(默认 DPI 是 96)。
常见错误:只调 GetSystemMetrics 就以为拿到了物理分辨率;或在多显示器场景下只查主屏,漏掉其他显示器的 HMONITOR。
- 主屏逻辑宽高:
GetSystemMetrics(SM_CXSCREEN)/SM_CYSCREEN) - 主屏物理宽高:用
EnumDisplayMonitors枚举后对第一个HMONITOR调GetMonitorInfo,读rcMonitor.right - rcMonitor.left - 当前窗口缩放:推荐用
GetDpiForWindow(hwnd)(Win10 1703+),比GetDpiForMonitor更准,尤其窗口跨屏时 - 注意:必须链接
user32.lib,且程序需声明 DPI 感知(否则系统会强制模拟缩放,GetDpiForWindow返回 96)
macOS 下用 NSScreen 获取分辨率和 backingScaleFactor
macOS 不叫“缩放”,而是通过 backingScaleFactor 控制像素密度(1.0=普通屏,2.0=Retina)。它决定了 1 个点(point)对应几个像素(pixel)。所以逻辑分辨率(frame.size) × backingScaleFactor = 物理像素分辨率。
常见误区:直接读 [NSScreen mainScreen].frame 得到的是点坐标,不是像素;忽略多屏下不同屏幕可能有不同 backingScaleFactor(比如外接 4K 显示器设为“更多空间”,backingScaleFactor 可能是 2.0 或 3.0)。
- 主屏逻辑尺寸:
[[NSScreen mainScreen] frame].size - 主屏物理像素尺寸:
CGSizeMake([[NSScreen mainScreen] frame].size.width * [[NSScreen mainScreen] backingScaleFactor], ...height...) - 获取所有屏幕:
[NSScreen screens],遍历每个NSScreen *实例分别查frame和backingScaleFactor - 注意:必须在主线程调用,且项目需启用 AppKit(即非纯命令行);C++ 项目需混编 Objective-C++(.mm 文件)
Linux(X11)下用 XRandR 查询输出和缩放
Linux 没统一 API,主流桌面环境依赖 X11 的 XRandR 扩展。分辨率可直接从 XRRGetScreenResources 拿当前活动模式(mode->width/mode->height),但“缩放”不是 X11 原生概念——它由 compositor(如 GNOME 的 mutter、KDE 的 kwin)或客户端自己处理(比如 Qt 用 devicePixelRatio())。
真正影响渲染的是 scale factor,常见来源有两个:gsettings get org.gnome.desktop.interface scaling-factor(GNOME),或环境变量 QT_SCALE_FACTOR / GDK_SCALE。X11 层本身不提供缩放值,硬查只能靠解析这些外部信号。
- 物理分辨率:调
XRRGetScreenResources→ 找output对应的mode→ 读mode->width/mode->height - 逻辑分辨率(缩放后):一般等于物理分辨率 ÷ 整数缩放因子;若启用了分数缩放(如 125%),则需查
gsettings或xdpyinfo | grep "dots per inch"(不准确) - 多屏处理:每个
RROutput可独立设置模式和位置,需遍历XRRGetOutputInfo结果 - 注意:需要链接
-lX11 -lXrandr;Wayland 下XRandR不可用,得走 DBus 或 wl_output 协议(更复杂)
跨平台封装要注意 DPI 感知声明和线程上下文
Windows 要在 manifest 中声明 dpiAware=true/pm 或调 SetProcessDpiAwarenessContext,否则 GetDpiForWindow 总返回 96;macOS 必须在主线程访问 NSScreen;Linux 的 XRandR 调用虽无严格线程限制,但 Display* 句柄通常只在主线程初始化安全。
最易被忽略的一点:缩放值不是全局常量。用户可能在运行时切换显示器缩放(Windows 设置里改,或 macOS 插拔外接屏),而你的代码如果只在启动时查一次,后续就失效了。真要响应变化,Windows 需监听 WM_DPICHANGED,macOS 监听 NSApplicationDidChangeScreenParametersNotification,Linux 则基本靠轮询或 dbus 信号。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











