summaryrefslogtreecommitdiff
path: root/src/envmetrics.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/envmetrics.cpp')
-rw-r--r--src/envmetrics.cpp334
1 files changed, 182 insertions, 152 deletions
diff --git a/src/envmetrics.cpp b/src/envmetrics.cpp
index a6988909..784e4baf 100644
--- a/src/envmetrics.cpp
+++ b/src/envmetrics.cpp
@@ -10,215 +10,245 @@ namespace env
using namespace MOBase;
-class DisplayEnumerator
+// fallback for windows 7
+//
+int getDesktopDpi()
{
-public:
- DisplayEnumerator()
- : m_GetDpiForMonitor(nullptr)
- {
- m_shcore.reset(LoadLibraryW(L"Shcore.dll"));
+ // desktop DC
+ DesktopDCPtr dc(GetDC(0));
- if (m_shcore) {
- // windows 8.1+ only
- m_GetDpiForMonitor = reinterpret_cast<GetDpiForMonitorFunction*>(
- GetProcAddress(m_shcore.get(), "GetDpiForMonitor"));
- }
-
- // gets all monitors and the device they're running on
- getDisplayDevices();
+ if (!dc) {
+ const auto e = GetLastError();
+ log::error("can't get desktop DC, {}", formatSystemMessageQ(e));
+ return 0;
}
- std::vector<Metrics::Display>&& displays() &&
- {
- return std::move(m_displays);
- }
+ return GetDeviceCaps(dc.get(), LOGPIXELSX);
+}
- const std::vector<Metrics::Display>& displays() const &
+// finds a monitor by device name; there's no real good way to do that except
+// by enumerating all the monitors and checking their name
+//
+HMONITOR findMonitor(const QString& name)
+{
+ // passed to the enumeration callback
+ struct Data
{
- return m_displays;
- }
+ QString name;
+ HMONITOR hm;
+ };
-private:
- using GetDpiForMonitorFunction =
- HRESULT WINAPI (HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*);
+ Data data = {name, 0};
- std::unique_ptr<HINSTANCE, LibraryFreer> m_shcore;
- GetDpiForMonitorFunction* m_GetDpiForMonitor;
- std::vector<Metrics::Display> m_displays;
+ // callback
+ auto callback = [](HMONITOR hm, HDC, RECT*, LPARAM lp) {
+ auto& data = *reinterpret_cast<Data*>(lp);
- void getDisplayDevices()
- {
- // don't bother if it goes over 100
- for (int i=0; i<100; ++i) {
- DISPLAY_DEVICEW device = {};
- device.cb = sizeof(device);
+ MONITORINFOEX mi = {};
+ mi.cbSize = sizeof(mi);
- if (!EnumDisplayDevicesW(nullptr, i, &device, 0)) {
- // no more
- break;
- }
+ // monitor info will include the name
+ if (!GetMonitorInfoW(hm, &mi)) {
+ const auto e = GetLastError();
+ log::error(
+ "GetMonitorInfo() failed for '{}', {}",
+ data.name, formatSystemMessageQ(e));
- // EnumDisplayDevices() seems to be returning a lot of devices that are
- // not actually monitors, but those don't have the
- // DISPLAY_DEVICE_ATTACHED_TO_DESKTOP bit set
- if ((device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == 0) {
- continue;
- }
+ // error for this monitor, but continue
+ return TRUE;
+ }
- m_displays.push_back(createDisplay(device));
+ if (QString::fromWCharArray(mi.szDevice) == data.name) {
+ // found, stop
+ data.hm = hm;
+ return FALSE;
}
- }
- Metrics::Display createDisplay(const DISPLAY_DEVICEW& device)
- {
- Metrics::Display d;
+ // not found, continue to the next monitor
+ return TRUE;
+ };
- d.adapter = QString::fromWCharArray(device.DeviceString);
- d.monitor = QString::fromWCharArray(device.DeviceName);
- d.primary = (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE);
- getDisplaySettings(device.DeviceName, d);
- getDpi(d);
+ // for each monitor
+ EnumDisplayMonitors(0, nullptr, callback, reinterpret_cast<LPARAM>(&data));
- return d;
- }
+ return data.hm;
+}
- void getDisplaySettings(const wchar_t* monitorName, Metrics::Display& d)
- {
- DEVMODEW dm = {};
- dm.dmSize = sizeof(dm);
+// returns the dpi for the given monitor; for systems that do not support
+// per-monitor dpi (such as windows 7), this is the desktop dpi
+//
+int getDpi(const QString& monitorDevice)
+{
+ using GetDpiForMonitorFunction =
+ HRESULT WINAPI (HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*);
- if (!EnumDisplaySettingsW(monitorName, ENUM_CURRENT_SETTINGS, &dm)) {
- log::error("EnumDisplaySettings() failed for '{}'", d.monitor);
- return;
- }
+ static LibraryPtr shcore;
+ static GetDpiForMonitorFunction* GetDpiForMonitor = nullptr;
+ static bool checked = false;
- // all these fields should be available
+ if (!checked) {
+ // try to find GetDpiForMonitor() from shcored.dll
- if (dm.dmFields & DM_DISPLAYFREQUENCY) {
- d.refreshRate = dm.dmDisplayFrequency;
- }
+ shcore.reset(LoadLibraryW(L"Shcore.dll"));
- if (dm.dmFields & DM_PELSWIDTH) {
- d.resX = dm.dmPelsWidth;
+ if (shcore) {
+ // windows 8.1+ only
+ GetDpiForMonitor = reinterpret_cast<GetDpiForMonitorFunction*>(
+ GetProcAddress(shcore.get(), "GetDpiForMonitor"));
}
- if (dm.dmFields & DM_PELSHEIGHT) {
- d.resY = dm.dmPelsHeight;
- }
+ checked = true;
}
- void getDpi(Metrics::Display& d)
- {
- if (!m_GetDpiForMonitor) {
- // this happens on windows 7, get the desktop dpi instead
- getDesktopDpi(d);
- return;
- }
+ if (!GetDpiForMonitor) {
+ // get the desktop dpi instead
+ return getDesktopDpi();
+ }
- // there's no way to get an HMONITOR from a device name, so all monitors
- // will have to be enumerated and their name checked
- HMONITOR hm = findMonitor(d.monitor);
- if (!hm) {
- log::error("can't get dpi for monitor '{}', not found", d.monitor);
- return;
- }
- UINT dpiX=0, dpiY=0;
- const auto r = m_GetDpiForMonitor(hm, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
+ // there's no way to get an HMONITOR from a device name, so all monitors
+ // will have to be enumerated and their name checked
+ HMONITOR hm = findMonitor(monitorDevice);
+ if (!hm) {
+ log::error("can't get dpi for monitor '{}', not found", monitorDevice);
+ return 0;
+ }
- if (FAILED(r)) {
- log::error(
- "GetDpiForMonitor() failed for '{}', {}",
- d.monitor, formatSystemMessageQ(r));
+ UINT dpiX=0, dpiY=0;
+ const auto r = GetDpiForMonitor(hm, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
- return;
- }
+ if (FAILED(r)) {
+ log::error(
+ "GetDpiForMonitor() failed for '{}', {}",
+ monitorDevice, formatSystemMessageQ(r));
- // dpiX and dpiY are always identical, as per the documentation
- d.dpi = dpiX;
+ return 0;
}
- void getDesktopDpi(Metrics::Display& d)
- {
- // desktop dc
- HDC dc = GetDC(0);
+ // dpiX and dpiY are always identical, as per the documentation
+ return dpiX;
+}
- if (!dc) {
- const auto e = GetLastError();
- log::error("can't get desktop DC, {}", formatSystemMessageQ(e));
- return;
- }
- d.dpi = GetDeviceCaps(dc, LOGPIXELSX);
+Display::Display(QString adapter, QString monitorDevice, bool primary) :
+ m_adapter(std::move(adapter)),
+ m_monitorDevice(std::move(monitorDevice)),
+ m_primary(primary),
+ m_resX(0), m_resY(0), m_dpi(0), m_refreshRate(0)
+{
+ getSettings();
+ m_dpi = getDpi(m_monitorDevice);
+}
- ReleaseDC(0, dc);
- }
+const QString& Display::adapter() const
+{
+ return m_adapter;
+}
- HMONITOR findMonitor(const QString& name)
- {
- // passed to the enumeration callback
- struct Data
- {
- DisplayEnumerator* self;
- QString name;
- HMONITOR hm;
- };
+const QString& Display::monitorDevice() const
+{
+ return m_monitorDevice;
+}
- Data data = {this, name, 0};
+bool Display::primary()
+{
+ return m_primary;
+}
- // for each monitor
- EnumDisplayMonitors(0, nullptr, [](HMONITOR hm, HDC, RECT*, LPARAM lp) {
- auto& data = *reinterpret_cast<Data*>(lp);
+int Display::resX() const
+{
+ return m_resX;
+}
- MONITORINFOEX mi = {};
- mi.cbSize = sizeof(mi);
+int Display::resY() const
+{
+ return m_resY;
+}
- // monitor info will include the name
- if (!GetMonitorInfoW(hm, &mi)) {
- const auto e = GetLastError();
- log::error(
- "GetMonitorInfo() failed for '{}', {}",
- data.name, formatSystemMessageQ(e));
+int Display::dpi()
+{
+ return m_dpi;
+}
- // error for this monitor, but continue
- return TRUE;
- }
+int Display::refreshRate() const
+{
+ return m_refreshRate;
+}
- if (QString::fromWCharArray(mi.szDevice) == data.name) {
- // found, stop
- data.hm = hm;
- return FALSE;
- }
+QString Display::toString() const
+{
+ return QString("%1*%2 %3hz dpi=%4 on %5%6")
+ .arg(m_resX)
+ .arg(m_resY)
+ .arg(m_refreshRate)
+ .arg(m_dpi)
+ .arg(m_adapter)
+ .arg(m_primary ? " (primary)" : "");
+}
- // not found, continue to the next monitor
- return TRUE;
- }, reinterpret_cast<LPARAM>(&data));
+void Display::getSettings()
+{
+ DEVMODEW dm = {};
+ dm.dmSize = sizeof(dm);
+
+ const auto wsDevice = m_monitorDevice.toStdWString();
+
+ if (!EnumDisplaySettingsW(wsDevice.c_str(), ENUM_CURRENT_SETTINGS, &dm)) {
+ log::error("EnumDisplaySettings() failed for '{}'", m_monitorDevice);
+ return;
+ }
+
+ // all these fields should be available
- return data.hm;
+ if (dm.dmFields & DM_DISPLAYFREQUENCY) {
+ m_refreshRate = dm.dmDisplayFrequency;
}
-};
+
+ if (dm.dmFields & DM_PELSWIDTH) {
+ m_resX = dm.dmPelsWidth;
+ }
+
+ if (dm.dmFields & DM_PELSHEIGHT) {
+ m_resY = dm.dmPelsHeight;
+ }
+}
Metrics::Metrics()
{
- m_displays = DisplayEnumerator().displays();
+ getDisplays();
}
-const std::vector<Metrics::Display>& Metrics::displays() const
+const std::vector<Display>& Metrics::displays() const
{
return m_displays;
}
-QString Metrics::Display::toString() const
+void Metrics::getDisplays()
{
- return QString("%1*%2 %3hz dpi=%4 on %5%6")
- .arg(resX)
- .arg(resY)
- .arg(refreshRate)
- .arg(dpi)
- .arg(adapter)
- .arg(primary ? " (primary)" : "");
+ // don't bother if it goes over 100
+ for (int i=0; i<100; ++i) {
+ DISPLAY_DEVICEW device = {};
+ device.cb = sizeof(device);
+
+ if (!EnumDisplayDevicesW(nullptr, i, &device, 0)) {
+ // no more
+ break;
+ }
+
+ // EnumDisplayDevices() seems to be returning a lot of devices that are
+ // not actually monitors, but those don't have the
+ // DISPLAY_DEVICE_ATTACHED_TO_DESKTOP bit set
+ if ((device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == 0) {
+ continue;
+ }
+
+ m_displays.emplace_back(
+ QString::fromWCharArray(device.DeviceString),
+ QString::fromWCharArray(device.DeviceName),
+ (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE));
+ }
}
} // namespace