summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main.cpp2
-rw-r--r--src/shared/util.cpp223
-rw-r--r--src/shared/util.h27
3 files changed, 251 insertions, 1 deletions
diff --git a/src/main.cpp b/src/main.cpp
index ef698dd4..09da9408 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -529,7 +529,7 @@ void checkNahimic(const env::Environment& e)
for (auto&& m : e.loadedModules()) {
const QFileInfo file(m.path());
- if (file.fileName().compare("NahimicOSD.dll", Qt::CaseInsensitive)) {
+ if (file.fileName().compare("NahimicOSD.dll", Qt::CaseInsensitive) == 0) {
log::warn(
"NahimicOSD.dll is loaded. Nahimic is known to cause issues with "
"Mod Organizer, such as freezing or blank windows. Consider "
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index eacd1f88..8c4a3f17 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -39,6 +39,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <Wbemidl.h>
#include <wscapi.h>
#include <netfw.h>
+#include <shellscalingapi.h>
#pragma comment(lib, "Wbemuuid.lib")
@@ -864,6 +865,196 @@ private:
};
+class DisplayEnumerator
+{
+public:
+ DisplayEnumerator()
+ : m_GetDpiForMonitor(nullptr)
+ {
+ m_shcore.reset(LoadLibraryW(L"Shcore.dll"));
+
+ 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();
+ }
+
+ std::vector<Metrics::Display>&& displays() &&
+ {
+ return std::move(m_displays);
+ }
+
+ const std::vector<Metrics::Display>& displays() const &
+ {
+ return m_displays;
+ }
+
+private:
+ using GetDpiForMonitorFunction =
+ HRESULT WINAPI (HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*);
+
+ std::unique_ptr<HINSTANCE, LibraryFreer> m_shcore;
+ GetDpiForMonitorFunction* m_GetDpiForMonitor;
+ std::vector<Metrics::Display> m_displays;
+
+ void getDisplayDevices()
+ {
+ // 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.push_back(createDisplay(device));
+ }
+ }
+
+ Metrics::Display createDisplay(const DISPLAY_DEVICEW& device)
+ {
+ Metrics::Display d;
+
+ 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);
+
+ return d;
+ }
+
+ void getDisplaySettings(const wchar_t* monitorName, Metrics::Display& d)
+ {
+ DEVMODEW dm = {};
+ dm.dmSize = sizeof(dm);
+
+ if (!EnumDisplaySettingsW(monitorName, ENUM_CURRENT_SETTINGS, &dm)) {
+ log::error("EnumDisplaySettings() failed for '{}'", d.monitor);
+ return;
+ }
+
+ // all these fields should be available
+
+ if (dm.dmFields & DM_DISPLAYFREQUENCY) {
+ d.refreshRate = dm.dmDisplayFrequency;
+ }
+
+ if (dm.dmFields & DM_PELSWIDTH) {
+ d.resX = dm.dmPelsWidth;
+ }
+
+ if (dm.dmFields & DM_PELSHEIGHT) {
+ d.resY = dm.dmPelsHeight;
+ }
+ }
+
+ void getDpi(Metrics::Display& d)
+ {
+ if (!m_GetDpiForMonitor) {
+ // this happens on windows 7, get the desktop dpi instead
+ getDesktopDpi(d);
+ return;
+ }
+
+ // 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);
+
+ if (FAILED(r)) {
+ log::error(
+ "GetDpiForMonitor() failed for '{}', {}",
+ d.monitor, formatSystemMessageQ(r));
+
+ return;
+ }
+
+ // dpiX and dpiY are always identical, as per the documentation
+ d.dpi = dpiX;
+ }
+
+ void getDesktopDpi(Metrics::Display& d)
+ {
+ // desktop dc
+ HDC dc = GetDC(0);
+
+ if (!dc) {
+ const auto e = GetLastError();
+ log::error("can't get desktop DC, {}", formatSystemMessageQ(e));
+ return;
+ }
+
+ d.dpi = GetDeviceCaps(dc, LOGPIXELSX);
+
+ ReleaseDC(0, dc);
+ }
+
+ HMONITOR findMonitor(const QString& name)
+ {
+ // passed to the enumeration callback
+ struct Data
+ {
+ DisplayEnumerator* self;
+ QString name;
+ HMONITOR hm;
+ };
+
+ Data data = {this, name, 0};
+
+ // for each monitor
+ EnumDisplayMonitors(0, nullptr, [](HMONITOR hm, HDC, RECT*, LPARAM lp) {
+ auto& data = *reinterpret_cast<Data*>(lp);
+
+ MONITORINFOEX mi = {};
+ mi.cbSize = sizeof(mi);
+
+ // monitor info will include the name
+ if (!GetMonitorInfoW(hm, &mi)) {
+ const auto e = GetLastError();
+ log::error(
+ "GetMonitorInfo() failed for '{}', {}",
+ data.name, formatSystemMessageQ(e));
+
+ // error for this monitor, but continue
+ return TRUE;
+ }
+
+ if (QString::fromWCharArray(mi.szDevice) == data.name) {
+ // found, stop
+ data.hm = hm;
+ return FALSE;
+ }
+
+ // not found, continue to the next monitor
+ return TRUE;
+ }, reinterpret_cast<LPARAM>(&data));
+
+ return data.hm;
+ }
+};
+
+
Environment::Environment()
{
m_modules = getLoadedModules();
@@ -885,6 +1076,11 @@ const std::vector<SecurityProduct>& Environment::securityProducts() const
return m_security;
}
+const Metrics& Environment::metrics() const
+{
+ return m_metrics;
+}
+
void Environment::dump() const
{
log::debug("windows: {}", windowsInfo().toString());
@@ -902,6 +1098,11 @@ void Environment::dump() const
for (const auto& m : loadedModules()) {
log::debug(" . {}", m.toString());
}
+
+ log::debug("displays:");
+ for (const auto& d : m_metrics.displays()) {
+ log::debug(" . {}", d.toString());
+ }
}
std::vector<Module> Environment::getLoadedModules() const
@@ -1137,6 +1338,28 @@ std::optional<SecurityProduct> Environment::getWindowsFirewall() const
}
+Metrics::Metrics()
+{
+ m_displays = DisplayEnumerator().displays();
+}
+
+const std::vector<Metrics::Display>& Metrics::displays() const
+{
+ return m_displays;
+}
+
+QString Metrics::Display::toString() const
+{
+ return QString("%1*%2 %3hz dpi=%4 on %5%6")
+ .arg(resX)
+ .arg(resY)
+ .arg(refreshRate)
+ .arg(dpi)
+ .arg(adapter)
+ .arg(primary ? " (primary)" : "");
+}
+
+
Module::Module(QString path, std::size_t fileSize)
: m_path(std::move(path)), m_fileSize(fileSize)
{
diff --git a/src/shared/util.h b/src/shared/util.h
index 267bb780..fc2028db 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -401,6 +401,28 @@ private:
};
+class Metrics
+{
+public:
+ struct Display
+ {
+ int resX=0, resY=0, dpi=0;
+ bool primary=false;
+ int refreshRate = 0;
+ QString monitor, adapter;
+
+ QString toString() const;
+ };
+
+ Metrics();
+
+ const std::vector<Display>& displays() const;
+
+private:
+ std::vector<Display> m_displays;
+};
+
+
// represents the process's environment
//
class Environment
@@ -420,6 +442,10 @@ public:
//
const std::vector<SecurityProduct>& securityProducts() const;
+ // information about displays
+ //
+ const Metrics& metrics() const;
+
// logs the environment
//
void dump() const;
@@ -428,6 +454,7 @@ private:
std::vector<Module> m_modules;
WindowsInfo m_windows;
std::vector<SecurityProduct> m_security;
+ Metrics m_metrics;
std::vector<Module> getLoadedModules() const;
std::vector<SecurityProduct> getSecurityProducts() const;