diff options
| author | isanae <14251494+isanae@users.noreply.github.com> | 2019-07-18 23:52:11 -0400 |
|---|---|---|
| committer | isanae <14251494+isanae@users.noreply.github.com> | 2019-07-22 07:33:38 -0400 |
| commit | d8760ed8ad688c7e69d2a5be89a8574f4bf44f74 (patch) | |
| tree | 20e4410c0c38addb87cc511aa1c242b34c728368 /src/envmetrics.cpp | |
| parent | b2a1e1391fdd6bdee1c5e8d337b273447c70a506 (diff) | |
refactored Metrics and Display, no change in functionality
Diffstat (limited to 'src/envmetrics.cpp')
| -rw-r--r-- | src/envmetrics.cpp | 334 |
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 |
