From 2edfae46cc600079cb705e78f885df1fac269ce2 Mon Sep 17 00:00:00 2001 From: SulfurNitride Date: Tue, 28 Apr 2026 20:20:17 -0500 Subject: Strip Windows-only blocks from env*.cpp/.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These files were ported from upstream MO2 with the Windows code wrapped in #ifdef _WIN32 and Linux equivalents in the #else branch. The Windows branches never compile on this fork — drop them so the source matches what's actually built. - envwindows.cpp/.h: kept Linux uname()/os-release implementation; the WindowsInfo::Version/Release fields drop their DWORD/uint32_t fork. - envshell.cpp: deleted entirely (Linux body was empty — header declares no-op stubs inline). - envshell.h: drop Win32 ShellMenu declarations, keep Linux stubs. - envmetrics.cpp: kept QScreen-based Linux Display/Metrics implementation. - envshortcut.cpp: kept Linux .desktop shortcut writer + PE icon extractor, drop Win32 IShellLink path. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/src/envmetrics.cpp | 493 +++++++++----------------------------- src/src/envshell.cpp | 610 ------------------------------------------------ src/src/envshell.h | 157 ++++--------- src/src/envshortcut.cpp | 359 +--------------------------- src/src/envwindows.cpp | 442 +++++++++++------------------------ src/src/envwindows.h | 19 -- 6 files changed, 295 insertions(+), 1785 deletions(-) delete mode 100644 src/src/envshell.cpp diff --git a/src/src/envmetrics.cpp b/src/src/envmetrics.cpp index 828848d..5eb623c 100644 --- a/src/src/envmetrics.cpp +++ b/src/src/envmetrics.cpp @@ -1,379 +1,114 @@ -#include "envmetrics.h" - -#ifdef _WIN32 - -#include "env.h" -#include -#include -#include -#include -#include - -namespace env -{ - -using namespace MOBase; - -// fallback for windows 7 -// -int getDesktopDpi() -{ - // desktop DC - DesktopDCPtr dc(GetDC(0)); - - if (!dc) { - const auto e = GetLastError(); - log::error("can't get desktop DC, {}", formatSystemMessage(e)); - return 0; - } - - return GetDeviceCaps(dc.get(), LOGPIXELSX); -} - -// 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 - { - QString name; - HMONITOR hm; - }; - - Data data = {name, 0}; - - // callback - auto callback = [](HMONITOR hm, HDC, RECT*, LPARAM lp) { - auto& data = *reinterpret_cast(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, - formatSystemMessage(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; - }; - - // for each monitor - EnumDisplayMonitors(0, nullptr, callback, reinterpret_cast(&data)); - - return data.hm; -} - -// 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*); - - static LibraryPtr shcore; - static GetDpiForMonitorFunction* GetDpiForMonitor = nullptr; - static bool checked = false; - - if (!checked) { - // try to find GetDpiForMonitor() from shcored.dll - - shcore.reset(LoadLibraryW(L"Shcore.dll")); - - if (shcore) { - // windows 8.1+ only - GetDpiForMonitor = reinterpret_cast( - GetProcAddress(shcore.get(), "GetDpiForMonitor")); - } - - checked = true; - } - - 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(monitorDevice); - if (!hm) { - log::error("can't get dpi for monitor '{}', not found", monitorDevice); - return 0; - } - - UINT dpiX = 0, dpiY = 0; - const auto r = GetDpiForMonitor(hm, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); - - if (FAILED(r)) { - log::error("GetDpiForMonitor() failed for '{}', {}", monitorDevice, - formatSystemMessage(r)); - - return 0; - } - - // dpiX and dpiY are always identical, as per the documentation - return dpiX; -} - -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); -} - -const QString& Display::adapter() const -{ - return m_adapter; -} - -const QString& Display::monitorDevice() const -{ - return m_monitorDevice; -} - -bool Display::primary() -{ - return m_primary; -} - -int Display::resX() const -{ - return m_resX; -} - -int Display::resY() const -{ - return m_resY; -} - -int Display::dpi() -{ - return m_dpi; -} - -int Display::refreshRate() const -{ - return m_refreshRate; -} - -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)" : ""); -} - -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 - - 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() -{ - getDisplays(); -} - -const std::vector& Metrics::displays() const -{ - return m_displays; -} - -QRect Metrics::desktopGeometry() const -{ - QRect r; - - for (auto* s : QGuiApplication::screens()) { - r = r.united(s->geometry()); - } - - return r; -} - -void Metrics::getDisplays() -{ - // 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 env - -#else // Linux - -#include -#include -#include - -namespace env -{ - -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(); -} - -const QString& Display::adapter() const -{ - return m_adapter; -} - -const QString& Display::monitorDevice() const -{ - return m_monitorDevice; -} - -bool Display::primary() -{ - return m_primary; -} - -int Display::resX() const -{ - return m_resX; -} - -int Display::resY() const -{ - return m_resY; -} - -int Display::dpi() -{ - return m_dpi; -} - -int Display::refreshRate() const -{ - return m_refreshRate; -} - -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)" : ""); -} - -void Display::getSettings() -{ - // Use QScreen to get display settings on Linux - const auto screens = QGuiApplication::screens(); - for (auto* screen : screens) { - if (screen->name() == m_monitorDevice) { - const auto geo = screen->geometry(); - m_resX = geo.width(); - m_resY = geo.height(); - m_refreshRate = qRound(screen->refreshRate()); - m_dpi = qRound(screen->logicalDotsPerInch()); - return; - } - } - - // Fallback: if monitor name didn't match, use primary screen - if (auto* primary = QGuiApplication::primaryScreen()) { - const auto geo = primary->geometry(); - m_resX = geo.width(); - m_resY = geo.height(); - m_refreshRate = qRound(primary->refreshRate()); - m_dpi = qRound(primary->logicalDotsPerInch()); - } -} - -Metrics::Metrics() -{ - getDisplays(); -} - -const std::vector& Metrics::displays() const -{ - return m_displays; -} - -QRect Metrics::desktopGeometry() const -{ - if (auto* primary = QGuiApplication::primaryScreen()) { - return primary->virtualGeometry(); - } - return QRect(); -} - -void Metrics::getDisplays() -{ - const auto screens = QGuiApplication::screens(); - for (auto* screen : screens) { - const bool isPrimary = (screen == QGuiApplication::primaryScreen()); - m_displays.emplace_back(screen->manufacturer() + " " + screen->model(), - screen->name(), isPrimary); - } -} - -} // namespace env - -#endif // _WIN32 +#include "envmetrics.h" + +#include +#include +#include + +namespace env +{ + +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(); +} + +const QString& Display::adapter() const +{ + return m_adapter; +} + +const QString& Display::monitorDevice() const +{ + return m_monitorDevice; +} + +bool Display::primary() +{ + return m_primary; +} + +int Display::resX() const +{ + return m_resX; +} + +int Display::resY() const +{ + return m_resY; +} + +int Display::dpi() +{ + return m_dpi; +} + +int Display::refreshRate() const +{ + return m_refreshRate; +} + +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)" : ""); +} + +void Display::getSettings() +{ + const auto screens = QGuiApplication::screens(); + for (auto* screen : screens) { + if (screen->name() == m_monitorDevice) { + const auto geo = screen->geometry(); + m_resX = geo.width(); + m_resY = geo.height(); + m_refreshRate = qRound(screen->refreshRate()); + m_dpi = qRound(screen->logicalDotsPerInch()); + return; + } + } + + if (auto* primary = QGuiApplication::primaryScreen()) { + const auto geo = primary->geometry(); + m_resX = geo.width(); + m_resY = geo.height(); + m_refreshRate = qRound(primary->refreshRate()); + m_dpi = qRound(primary->logicalDotsPerInch()); + } +} + +Metrics::Metrics() +{ + getDisplays(); +} + +const std::vector& Metrics::displays() const +{ + return m_displays; +} + +QRect Metrics::desktopGeometry() const +{ + if (auto* primary = QGuiApplication::primaryScreen()) { + return primary->virtualGeometry(); + } + return QRect(); +} + +void Metrics::getDisplays() +{ + const auto screens = QGuiApplication::screens(); + for (auto* screen : screens) { + const bool isPrimary = (screen == QGuiApplication::primaryScreen()); + m_displays.emplace_back(screen->manufacturer() + " " + screen->model(), + screen->name(), isPrimary); + } +} + +} // namespace env diff --git a/src/src/envshell.cpp b/src/src/envshell.cpp deleted file mode 100644 index f8e1ec9..0000000 --- a/src/src/envshell.cpp +++ /dev/null @@ -1,610 +0,0 @@ -#ifdef _WIN32 - -#include - -#include -#include -#include - -#include "envshell.h" - -namespace env -{ - -using namespace MOBase; - -const int QCM_FIRST = 1; -const int QCM_LAST = 0x7ff; - -class MenuFailed : public std::runtime_error -{ -public: - MenuFailed(HRESULT r, const std::string& what) - : runtime_error( - std::format("{}, {}", what, - QString::fromStdWString(formatSystemMessage(r)).toStdString())) - {} -}; - -class DummyMenu -{ -public: - DummyMenu(QString s) : m_what(s) {} - - const QString& what() const { return m_what; } - -private: - QString m_what; -}; - -struct IdlsFreer -{ - const std::vector& v; - - IdlsFreer(const std::vector& v) : v(v) {} - - ~IdlsFreer() - { - for (auto&& idl : v) { - ::CoTaskMemFree(const_cast(idl)); - } - } -}; - -class WndProcFilter : public QAbstractNativeEventFilter -{ -public: - using function_type = - std::function; - - WndProcFilter(function_type f) : m_f(std::move(f)) {} - - bool nativeEventFilter(const QByteArray& eventType, void* message, - qintptr* result) override - { - MSG* msg = (MSG*)message; - if (!msg) { - return false; - } - - LRESULT lr = 0; - - const bool r = m_f(msg->hwnd, msg->message, msg->wParam, msg->lParam, &lr); - - if (result) { - *result = lr; - } - - return r; - } - -private: - function_type m_f; -}; - -HWND getHWND(QMainWindow* mw) -{ - if (mw) { - return (HWND)mw->winId(); - } else { - return 0; - } -} - -// adapted from -// https://devblogs.microsoft.com/oldnewthing/20040928-00/?p=37723 -// -HRESULT IContextMenu_GetCommandString(IContextMenu* pcm, UINT_PTR idCmd, UINT uFlags, - UINT* pwReserved, LPWSTR pszName, UINT cchMax) -{ - // Callers are expected to be using Unicode. - if (!(uFlags & GCS_UNICODE)) { - return E_INVALIDARG; - } - - if (cchMax <= 1) { - return E_FAIL; - } - - cchMax--; - - pszName[0] = L'\0'; - - HRESULT hr = pcm->GetCommandString(idCmd, uFlags, pwReserved, (LPSTR)pszName, cchMax); - - if (SUCCEEDED(hr) && pszName[0] == L'\0') { - hr = E_NOTIMPL; - } - - if (FAILED(hr)) { - LPSTR pszAnsi = (LPSTR)LocalAlloc(LMEM_FIXED, (cchMax + 1) * sizeof(CHAR)); - - if (pszAnsi) { - pszAnsi[0] = '\0'; - - hr = pcm->GetCommandString(idCmd, uFlags & ~GCS_UNICODE, pwReserved, pszAnsi, - cchMax); - - if (SUCCEEDED(hr) && pszAnsi[0] == '\0') { - hr = E_NOTIMPL; - } - - if (SUCCEEDED(hr)) { - if (MultiByteToWideChar(CP_ACP, 0, pszAnsi, -1, pszName, cchMax) == 0) { - hr = E_FAIL; - } - } - - LocalFree(pszAnsi); - - } else { - hr = E_OUTOFMEMORY; - } - } - - return hr; -} - -ShellMenu::ShellMenu(QMainWindow* mw) : m_mw(mw) {} - -void ShellMenu::addFile(QFileInfo fi) -{ - m_files.emplace_back(std::move(fi)); -} - -int ShellMenu::fileCount() const -{ - return static_cast(m_files.size()); -} - -void ShellMenu::exec(const QPoint& pos) -{ - HMENU menu = getMenu(); - if (!menu) { - return; - } - - try { - const auto hwnd = getHWND(m_mw); - - auto filter = std::make_unique( - [&](HWND h, UINT m, WPARAM wp, LPARAM lp, LRESULT* out) { - return wndProc(h, m, wp, lp, out); - }); - - QCoreApplication::instance()->installNativeEventFilter(filter.get()); - - const int cmd = - TrackPopupMenuEx(menu, TPM_RETURNCMD, pos.x(), pos.y(), hwnd, nullptr); - - if (m_mw) { - if (auto* sb = m_mw->statusBar()) { - sb->clearMessage(); - } - } - - if (cmd <= 0) { - return; - } - - invoke(pos, cmd - QCM_FIRST); - } catch (MenuFailed& e) { - if (m_files.size() == 1) { - log::error("can't exec shell menu for '{}': {}", - QDir::toNativeSeparators(m_files[0].absoluteFilePath()), e.what()); - } else { - log::error("can't exec shell menu for {} files: {}", m_files.size(), e.what()); - } - } -} - -HMENU ShellMenu::getMenu() -{ - if (!m_menu) { - create(); - } - - return m_menu.get(); -} - -bool ShellMenu::wndProc(HWND h, UINT m, WPARAM wp, LPARAM lp, LRESULT* out) -{ - if (m == WM_MENUSELECT) { - HANDLE_WM_MENUSELECT(h, wp, lp, onMenuSelect); - return true; - } - - if (m_cm3) { - const auto r = m_cm3->HandleMenuMsg2(m, wp, lp, out); - - if (SUCCEEDED(r)) { - return true; - } - } - - if (m_cm2) { - const auto r = m_cm2->HandleMenuMsg(m, wp, lp); - - if (SUCCEEDED(r)) { - if (out) { - *out = 0; - } - - return true; - } - } - - return false; -} - -void ShellMenu::onMenuSelect(HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, - UINT flags) -{ - if (m_cm && item >= QCM_FIRST && item <= QCM_LAST) { - WCHAR szBuf[MAX_PATH]; - - const auto r = IContextMenu_GetCommandString(m_cm.get(), item - QCM_FIRST, - GCS_HELPTEXTW, NULL, szBuf, MAX_PATH); - - if (FAILED(r)) { - lstrcpynW(szBuf, L"No help available.", MAX_PATH); - } - - if (m_mw) { - if (auto* sb = m_mw->statusBar()) { - sb->showMessage(QString::fromWCharArray(szBuf)); - } - } - } -} - -void ShellMenu::create() -{ - if (m_files.empty()) { - log::warn("showShellMenu(): no files given"); - return; - } - - try { - auto idls = createIdls(m_files); - - if (idls.empty()) { - log::error("no idls, can't create context menu"); - return; - } - - IdlsFreer freer(idls); - - auto array = createItemArray(idls); - - createContextMenu(array.get()); - createPopupMenu(m_cm.get()); - } catch (DummyMenu& dm) { - m_menu = createDummyMenu(dm.what()); - } catch (MenuFailed& e) { - if (m_files.size() == 1) { - log::error("can't create shell menu for '{}': {}", - QDir::toNativeSeparators(m_files[0].absoluteFilePath()), e.what()); - } else { - log::error("can't create shell menu for {} files: {}", m_files.size(), e.what()); - } - - m_menu = createDummyMenu(QObject::tr("No menu available")); - } -} - -HMenuPtr ShellMenu::createDummyMenu(const QString& what) -{ - try { - HMENU menu = CreatePopupMenu(); - if (!menu) { - const auto e = GetLastError(); - throw MenuFailed(e, "CreatePopupMenu failed"); - } - - if (!AppendMenuW(menu, MF_STRING | MF_DISABLED, 0, what.toStdWString().c_str())) { - const auto e = GetLastError(); - throw MenuFailed(e, "AppendMenuW failed"); - } - - return HMenuPtr(menu); - } catch (MenuFailed& e) { - log::error("{}", what); - log::error("additionally, creating the dummy menu failed: {}", e.what()); - - return {}; - } -} - -std::vector ShellMenu::createIdls(const std::vector& files) -{ - std::vector idls; - std::optional parent; - - for (auto&& f : files) { - const auto path = QDir::toNativeSeparators(f.absoluteFilePath()).toStdWString(); - - if (!parent) { - parent = f.absoluteDir(); - } else { - if (*parent != f.absoluteDir()) { - throw DummyMenu(QObject::tr("Selected files must be in the same directory")); - } - } - - auto item = createShellItem(path); - auto pidlist = getPersistIDList(item.get()); - auto absIdl = getIDList(pidlist.get()); - - idls.push_back(absIdl.release()); - } - - return idls; -} - -COMPtr ShellMenu::createItemArray(std::vector& idls) -{ - IShellItemArray* array = nullptr; - auto r = SHCreateShellItemArrayFromIDLists(static_cast(idls.size()), &idls[0], - &array); - - if (FAILED(r)) { - throw MenuFailed(r, "SHCreateShellItemArrayFromIDLists failed"); - } - - return COMPtr(array); -} - -void ShellMenu::createContextMenu(IShellItemArray* array) -{ - IContextMenu* cm = nullptr; - - auto r = - array->BindToHandler(nullptr, BHID_SFUIObject, IID_IContextMenu, (void**)&cm); - - if (FAILED(r)) { - throw MenuFailed(r, "BindToHandler failed"); - } - - m_cm.reset(cm); - - { - IContextMenu2* cm2 = nullptr; - if (SUCCEEDED(m_cm->QueryInterface(IID_IContextMenu2, (void**)&cm2))) { - m_cm2.reset(cm2); - } - } - - { - IContextMenu3* cm3 = nullptr; - if (SUCCEEDED(m_cm->QueryInterface(IID_IContextMenu3, (void**)&cm3))) { - m_cm3.reset(cm3); - } - } -} - -void ShellMenu::createPopupMenu(IContextMenu* cm) -{ - HMENU hmenu = CreatePopupMenu(); - if (!hmenu) { - const auto e = GetLastError(); - throw MenuFailed(e, "CreatePopupMenu failed"); - } - - const auto r = cm->QueryContextMenu(hmenu, 0, QCM_FIRST, QCM_LAST, CMF_EXTENDEDVERBS); - - if (FAILED(r)) { - throw MenuFailed(r, "QueryContextMenu failed"); - } - - m_menu.reset(hmenu); -} - -COMPtr ShellMenu::createShellItem(const std::wstring& path) -{ - IShellItem* item = nullptr; - - auto r = - SHCreateItemFromParsingName(path.c_str(), nullptr, IID_IShellItem, (void**)&item); - - if (FAILED(r)) { - throw MenuFailed(r, "SHCreateItemFromParsingName failed"); - } - - return COMPtr(item); -} - -COMPtr ShellMenu::getPersistIDList(IShellItem* item) -{ - IPersistIDList* idl = nullptr; - auto r = item->QueryInterface(IID_IPersistIDList, (void**)&idl); - - if (FAILED(r)) { - throw MenuFailed(r, "QueryInterface IID_IPersistIDList failed"); - } - - return COMPtr(idl); -} - -CoTaskMemPtr ShellMenu::getIDList(IPersistIDList* pidlist) -{ - LPITEMIDLIST absIdl = nullptr; - auto r = pidlist->GetIDList(&absIdl); - - if (FAILED(r)) { - throw MenuFailed(r, "GetIDList failed"); - } - - return CoTaskMemPtr(absIdl); -} - -void ShellMenu::invoke(const QPoint& p, int cmd) -{ - const auto hwnd = getHWND(m_mw); - - CMINVOKECOMMANDINFOEX info = {}; - - info.cbSize = sizeof(info); - info.fMask = CMIC_MASK_UNICODE | CMIC_MASK_PTINVOKE; - info.hwnd = hwnd; - info.lpVerb = MAKEINTRESOURCEA(cmd); - info.lpVerbW = MAKEINTRESOURCEW(cmd); - info.nShow = SW_SHOWNORMAL; - info.ptInvoke = {p.x(), p.y()}; - - const auto m = QApplication::queryKeyboardModifiers(); - - if (m & Qt::ShiftModifier) { - info.fMask |= CMIC_MASK_SHIFT_DOWN; - } - - if (m & Qt::ControlModifier) { - info.fMask |= CMIC_MASK_CONTROL_DOWN; - } - - const auto r = m_cm->InvokeCommand((CMINVOKECOMMANDINFO*)&info); - - if (FAILED(r)) { - throw MenuFailed(r, std::format("InvokeCommand failed, verb={}", cmd)); - } -} - -ShellMenuCollection::ShellMenuCollection(QMainWindow* mw) : m_mw(mw), m_active(nullptr) -{} - -void ShellMenuCollection::addDetails(QString s) -{ - m_details.emplace_back(std::move(s)); -} - -void ShellMenuCollection::add(QString name, ShellMenu m) -{ - m_menus.push_back({name, std::move(m)}); -} - -void ShellMenuCollection::exec(const QPoint& pos) -{ - HMENU menu = ::CreatePopupMenu(); - if (!menu) { - const auto e = GetLastError(); - - log::error("CreatePopupMenu for merged menus failed, {}", formatSystemMessage(e)); - - return; - } - - if (!m_details.empty()) { - for (auto&& d : m_details) { - const auto s = d.toStdWString(); - const auto r = AppendMenuW(menu, MF_STRING | MF_DISABLED, 0, s.c_str()); - - if (!r) { - const auto e = GetLastError(); - log::error("AppendMenuW failed for details '{}', {}", d, - formatSystemMessage(e)); - } - } - - const auto r = AppendMenuW(menu, MF_SEPARATOR, 0, nullptr); - if (!r) { - const auto e = GetLastError(); - log::error("AppendMenuW failed for separator, {}", formatSystemMessage(e)); - } - } - - for (auto&& m : m_menus) { - auto hmenu = m.menu.getMenu(); - if (!hmenu) { - continue; - } - - const auto r = - AppendMenuW(menu, MF_POPUP | MF_STRING, reinterpret_cast(hmenu), - m.name.toStdWString().c_str()); - - if (!r) { - const auto e = GetLastError(); - - log::error("AppendMenuW failed for merged menu {}, {}", m.name, - formatSystemMessage(e)); - - continue; - } - } - - auto hwnd = getHWND(m_mw); - - auto filter = std::make_unique( - [&](HWND h, UINT m, WPARAM wp, LPARAM lp, LRESULT* out) { - return wndProc(h, m, wp, lp, out); - }); - - QCoreApplication::instance()->installNativeEventFilter(filter.get()); - - const int cmd = - TrackPopupMenuEx(menu, TPM_RETURNCMD, pos.x(), pos.y(), hwnd, nullptr); - - if (m_mw) { - if (auto* sb = m_mw->statusBar()) { - sb->clearMessage(); - } - } - - if (cmd <= 0) { - return; - } - - if (!m_active) { - log::debug("SMC: command {} selected without active submenu", cmd); - return; - } - - const auto realCmd = cmd - QCM_FIRST; - - log::debug("SMC: invoking {} on {}", realCmd, m_active->name); - m_active->menu.invoke(pos, realCmd); -} - -bool ShellMenuCollection::wndProc(HWND h, UINT m, WPARAM wp, LPARAM lp, LRESULT* out) -{ - if (m == WM_MENUSELECT) { - auto* oldActive = m_active; - m_active = nullptr; - - HANDLE_WM_MENUSELECT(h, wp, lp, onMenuSelect); - - if (!m_active && oldActive) { - m_active = oldActive; - } else if (m_active && m_active == oldActive) { - return true; - } else if (m_active && m_active != oldActive) { - log::debug("SMC: switching to {}", m_active->name); - } - } - - if (!m_active) { - return false; - } - - return m_active->menu.wndProc(h, m, wp, lp, out); -} - -void ShellMenuCollection::onMenuSelect(HWND hwnd, HMENU hmenu, int item, - HMENU hmenuPopup, UINT flags) -{ - for (auto&& m : m_menus) { - if (m.menu.getMenu() == hmenuPopup) { - m_active = &m; - break; - } - } -} - -} // namespace env - -#else // Linux - -// The header (envshell.h) provides stub class declarations for Linux. -// No additional implementation is needed since the stubs are inline in the header. - -#endif // _WIN32 diff --git a/src/src/envshell.h b/src/src/envshell.h index a390f3a..c672949 100644 --- a/src/src/envshell.h +++ b/src/src/envshell.h @@ -1,115 +1,42 @@ -#ifndef ENV_SHELL_H -#define ENV_SHELL_H - -#include "env.h" -#include -#include - -namespace env -{ - -#ifdef _WIN32 - -class ShellMenu -{ -public: - ShellMenu(QMainWindow* mw); - - // noncopyable - ShellMenu(const ShellMenu&) = delete; - ShellMenu& operator=(const ShellMenu&) = delete; - ShellMenu(ShellMenu&&) = default; - ShellMenu& operator=(ShellMenu&&) = default; - - void addFile(QFileInfo fi); - int fileCount() const; - - void exec(const QPoint& pos); - HMENU getMenu(); - bool wndProc(HWND hwnd, UINT m, WPARAM wp, LPARAM lp, LRESULT* out); - void invoke(const QPoint& p, int cmd); - -private: - QMainWindow* m_mw; - std::vector m_files; - COMPtr m_cm; - COMPtr m_cm2; - COMPtr m_cm3; - HMenuPtr m_menu; - - void create(); - - std::vector createIdls(const std::vector& files); - COMPtr createItemArray(std::vector& idls); - - void createContextMenu(IShellItemArray* array); - void createPopupMenu(IContextMenu* cm); - - COMPtr createShellItem(const std::wstring& path); - COMPtr getPersistIDList(IShellItem* item); - CoTaskMemPtr getIDList(IPersistIDList* pidlist); - HMenuPtr createDummyMenu(const QString& what); - - void onMenuSelect(HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags); -}; - -class ShellMenuCollection -{ -public: - ShellMenuCollection(QMainWindow* mw); - - void addDetails(QString s); - void add(QString name, ShellMenu m); - - void exec(const QPoint& pos); - -private: - struct MenuInfo - { - QString name; - ShellMenu menu; - }; - - QMainWindow* m_mw; - std::vector m_details; - std::vector m_menus; - MenuInfo* m_active; - - bool wndProc(HWND hwnd, UINT m, WPARAM wp, LPARAM lp, LRESULT* out); - - void onMenuSelect(HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags); -}; - -#else // Linux - -// Stub implementations for Linux - shell context menus are Windows-only -class ShellMenu -{ -public: - ShellMenu(QMainWindow*) {} - - ShellMenu(const ShellMenu&) = delete; - ShellMenu& operator=(const ShellMenu&) = delete; - ShellMenu(ShellMenu&&) = default; - ShellMenu& operator=(ShellMenu&&) = default; - - void addFile(QFileInfo) {} - int fileCount() const { return 0; } - void exec(const QPoint&) {} -}; - -class ShellMenuCollection -{ -public: - ShellMenuCollection(QMainWindow*) {} - - void addDetails(QString) {} - void add(QString, ShellMenu) {} - void exec(const QPoint&) {} -}; - -#endif // _WIN32 - -} // namespace env - -#endif // ENV_SHELL_H +#ifndef ENV_SHELL_H +#define ENV_SHELL_H + +#include "env.h" +#include +#include +#include +#include + +namespace env +{ + +// Shell context menus are Windows-only — these stubs let the rest of the +// codebase compile and call into them as no-ops on Linux. +class ShellMenu +{ +public: + ShellMenu(QMainWindow*) {} + + ShellMenu(const ShellMenu&) = delete; + ShellMenu& operator=(const ShellMenu&) = delete; + ShellMenu(ShellMenu&&) = default; + ShellMenu& operator=(ShellMenu&&) = default; + + void addFile(QFileInfo) {} + int fileCount() const { return 0; } + void exec(const QPoint&) {} +}; + +class ShellMenuCollection +{ +public: + ShellMenuCollection(QMainWindow*) {} + + void addDetails(QString) {} + void add(QString, ShellMenu) {} + void exec(const QPoint&) {} +}; + +} // namespace env + +#endif // ENV_SHELL_H diff --git a/src/src/envshortcut.cpp b/src/src/envshortcut.cpp index 5b6ff8e..1a36cf4 100644 --- a/src/src/envshortcut.cpp +++ b/src/src/envshortcut.cpp @@ -1,358 +1,5 @@ -#include "envshortcut.h" - -#ifdef _WIN32 - -#include "env.h" -#include "executableslist.h" -#include "filesystemutilities.h" -#include "instancemanager.h" -#include -#include - -namespace env -{ - -using namespace MOBase; - -class ShellLinkException -{ -public: - ShellLinkException(QString s) : m_what(std::move(s)) {} - - const QString& what() const { return m_what; } - -private: - QString m_what; -}; - -// just a wrapper around IShellLink operations that throws ShellLinkException -// on errors -// -class ShellLinkWrapper -{ -public: - ShellLinkWrapper() - { - m_link = createShellLink(); - m_file = createPersistFile(); - } - - void setPath(const QString& s) - { - if (s.isEmpty()) { - throw ShellLinkException("path cannot be empty"); - } - - const auto r = m_link->SetPath(s.toStdWString().c_str()); - throwOnFail(r, QString("failed to set target path '%1'").arg(s)); - } - - void setArguments(const QString& s) - { - const auto r = m_link->SetArguments(s.toStdWString().c_str()); - throwOnFail(r, QString("failed to set arguments '%1'").arg(s)); - } - - void setDescription(const QString& s) - { - if (s.isEmpty()) { - return; - } - - const auto r = m_link->SetDescription(s.toStdWString().c_str()); - throwOnFail(r, QString("failed to set description '%1'").arg(s)); - } - - void setIcon(const QString& file, int i) - { - if (file.isEmpty()) { - return; - } - - const auto r = m_link->SetIconLocation(file.toStdWString().c_str(), i); - throwOnFail(r, QString("failed to set icon '%1' @ %2").arg(file).arg(i)); - } - - void setWorkingDirectory(const QString& s) - { - if (s.isEmpty()) { - return; - } - - const auto r = m_link->SetWorkingDirectory(s.toStdWString().c_str()); - throwOnFail(r, QString("failed to set working directory '%1'").arg(s)); - } - - void save(const QString& path) - { - const auto r = m_file->Save(path.toStdWString().c_str(), TRUE); - throwOnFail(r, QString("failed to save link '%1'").arg(path)); - } - -private: - COMPtr m_link; - COMPtr m_file; - - void throwOnFail(HRESULT r, const QString& s) - { - if (FAILED(r)) { - throw ShellLinkException(QString("%1, %2").arg(s).arg(formatSystemMessage(r))); - } - } - - COMPtr createShellLink() - { - void* link = nullptr; - - const auto r = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, - IID_IShellLink, &link); - - throwOnFail(r, "failed to create IShellLink instance"); - - if (!link) { - throw ShellLinkException("creating IShellLink worked, pointer is null"); - } - - return COMPtr(static_cast(link)); - } - - COMPtr createPersistFile() - { - void* file = nullptr; - - const auto r = m_link->QueryInterface(IID_IPersistFile, &file); - throwOnFail(r, "failed to get IPersistFile interface"); - - if (!file) { - throw ShellLinkException("querying IPersistFile worked, pointer is null"); - } - - return COMPtr(static_cast(file)); - } -}; - -Shortcut::Shortcut() : m_iconIndex(0) {} - -Shortcut::Shortcut(const Executable& exe) : Shortcut() -{ - const auto i = *InstanceManager::singleton().currentInstance(); - - m_name = MOBase::sanitizeFileName(exe.title()); - m_target = QFileInfo(qApp->applicationFilePath()).absoluteFilePath(); - - m_arguments = QString("\"moshortcut://%1:%2\"") - .arg(i.isPortable() ? "" : i.displayName()) - .arg(exe.title()); - - m_description = QString("Run %1 with ModOrganizer").arg(exe.title()); - - if (exe.usesOwnIcon()) { - m_icon = exe.binaryInfo().absoluteFilePath(); - } - - m_workingDirectory = qApp->applicationDirPath(); -} - -Shortcut& Shortcut::name(const QString& s) -{ - m_name = s; - return *this; -} - -Shortcut& Shortcut::target(const QString& s) -{ - m_target = s; - return *this; -} - -Shortcut& Shortcut::arguments(const QString& s) -{ - m_arguments = s; - return *this; -} - -Shortcut& Shortcut::description(const QString& s) -{ - m_description = s; - return *this; -} - -Shortcut& Shortcut::icon(const QString& s, int index) -{ - m_icon = s; - m_iconIndex = index; - return *this; -} - -Shortcut& Shortcut::workingDirectory(const QString& s) -{ - m_workingDirectory = s; - return *this; -} - -bool Shortcut::exists(Locations loc) const -{ - const auto path = shortcutPath(loc); - if (path.isEmpty()) { - return false; - } - - return QFileInfo(path).exists(); -} - -bool Shortcut::toggle(Locations loc) -{ - if (exists(loc)) { - return remove(loc); - } else { - return add(loc); - } -} - -bool Shortcut::add(Locations loc) -{ - log::debug("adding shortcut to {}:\n" - " . name: '{}'\n" - " . target: '{}'\n" - " . arguments: '{}'\n" - " . description: '{}'\n" - " . icon: '{}' @ {}\n" - " . working directory: '{}'", - toString(loc), m_name, m_target, m_arguments, m_description, m_icon, - m_iconIndex, m_workingDirectory); - - if (m_target.isEmpty()) { - log::error("shortcut: target is empty"); - return false; - } - - const auto path = shortcutPath(loc); - if (path.isEmpty()) { - return false; - } - - log::debug("shorcut file will be saved at '{}'", path); - - try { - ShellLinkWrapper link; - - link.setPath(m_target); - link.setArguments(m_arguments); - link.setDescription(m_description); - link.setIcon(m_icon, m_iconIndex); - link.setWorkingDirectory(m_workingDirectory); - - link.save(path); - - return true; - } catch (ShellLinkException& e) { - log::error("{}\nshortcut file was not saved", e.what()); - } - - return false; -} - -bool Shortcut::remove(Locations loc) -{ - log::debug("removing shortcut for '{}' from {}", m_name, toString(loc)); - - const auto path = shortcutPath(loc); - if (path.isEmpty()) { - return false; - } - - log::debug("path to shortcut file is '{}'", path); - - if (!QFile::exists(path)) { - log::error("can't remove shortcut '{}', file not found", path); - return false; - } - - if (!MOBase::shellDelete({path})) { - const auto e = ::GetLastError(); - - log::error("failed to remove shortcut '{}', {}", path, formatSystemMessage(e)); - - return false; - } - - return true; -} - -QString Shortcut::shortcutPath(Locations loc) const -{ - const auto dir = shortcutDirectory(loc); - if (dir.isEmpty()) { - return {}; - } - - const auto file = shortcutFilename(); - if (file.isEmpty()) { - return {}; - } - - return dir + QDir::separator() + file; -} - -QString Shortcut::shortcutDirectory(Locations loc) const -{ - QString dir; - - try { - switch (loc) { - case Desktop: - dir = MOBase::getDesktopDirectory(); - break; - - case StartMenu: - dir = MOBase::getStartMenuDirectory(); - break; - - case None: - default: - log::error("shortcut: bad location {}", loc); - break; - } - } catch (std::exception&) { - } - - return QDir::toNativeSeparators(dir); -} - -QString Shortcut::shortcutFilename() const -{ - if (m_name.isEmpty()) { - log::error("shortcut name is empty"); - return {}; - } - - return m_name + ".lnk"; -} - -QString toString(Shortcut::Locations loc) -{ - switch (loc) { - case Shortcut::None: - return "none"; - - case Shortcut::Desktop: - return "desktop"; - - case Shortcut::StartMenu: - return "start menu"; - - case Shortcut::ApplicationMenu: - return "application menu"; - - default: - return QString("? (%1)").arg(static_cast(loc)); - } -} - -} // namespace env - -#else // Linux - +#include "envshortcut.h" + #include #include #include @@ -921,5 +568,3 @@ QString toString(Shortcut::Locations loc) } } // namespace env - -#endif // _WIN32 diff --git a/src/src/envwindows.cpp b/src/src/envwindows.cpp index 24f98cf..e235032 100644 --- a/src/src/envwindows.cpp +++ b/src/src/envwindows.cpp @@ -1,305 +1,137 @@ -#include "envwindows.h" -#include "env.h" -#include "envmodule.h" -#include -#include - -#ifndef _WIN32 -#include -#include -#include -#include -#endif - -namespace env -{ - -using namespace MOBase; - -#ifdef _WIN32 - -WindowsInfo::WindowsInfo() -{ - // loading ntdll.dll, the functions will be found with GetProcAddress() - LibraryPtr ntdll(LoadLibraryW(L"ntdll.dll")); - - if (!ntdll) { - log::error("failed to load ntdll.dll while getting version"); - return; - } else { - m_reported = getReportedVersion(ntdll.get()); - m_real = getRealVersion(ntdll.get()); - } - - m_release = getRelease(); - m_elevated = getElevated(); -} - -WindowsInfo::Version WindowsInfo::getReportedVersion(HINSTANCE ntdll) const -{ - using RtlGetVersionType = NTSTATUS(NTAPI)(PRTL_OSVERSIONINFOW); - - auto* RtlGetVersion = - reinterpret_cast(GetProcAddress(ntdll, "RtlGetVersion")); - - if (!RtlGetVersion) { - log::error("RtlGetVersion() not found in ntdll.dll"); - return {}; - } - - OSVERSIONINFOEX vi = {}; - vi.dwOSVersionInfoSize = sizeof(vi); - - RtlGetVersion((RTL_OSVERSIONINFOW*)&vi); - - return {vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber}; -} - -WindowsInfo::Version WindowsInfo::getRealVersion(HINSTANCE ntdll) const -{ - using RtlGetNtVersionNumbersType = void(NTAPI)(DWORD*, DWORD*, DWORD*); - - auto* RtlGetNtVersionNumbers = reinterpret_cast( - GetProcAddress(ntdll, "RtlGetNtVersionNumbers")); - - if (!RtlGetNtVersionNumbers) { - log::error("RtlGetNtVersionNumbers not found in ntdll.dll"); - return {}; - } - - DWORD major = 0, minor = 0, build = 0; - RtlGetNtVersionNumbers(&major, &minor, &build); - - // for whatever reason, the build number has 0xf0000000 set - build = 0x0fffffff & build; - - return {major, minor, build}; -} - -WindowsInfo::Release WindowsInfo::getRelease() const -{ - QSettings settings( - R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion)", - QSettings::NativeFormat); - - Release r; - - r.buildLab = settings.value("BuildLabEx", "").toString(); - if (r.buildLab.isEmpty()) { - r.buildLab = settings.value("BuildLab", "").toString(); - if (r.buildLab.isEmpty()) { - r.buildLab = settings.value("BuildBranch", "").toString(); - } - } - - r.ID = settings.value("DisplayVersion", "").toString(); - if (r.ID.isEmpty()) { - r.ID = settings.value("ReleaseId", "").toString(); - } - - r.UBR = settings.value("UBR", 0).toUInt(); - - return r; -} - -std::optional WindowsInfo::getElevated() const -{ - HandlePtr token; - - { - HANDLE rawToken = 0; - - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &rawToken)) { - const auto e = GetLastError(); - - log::error("while trying to check if process is elevated, " - "OpenProcessToken() failed: {}", - formatSystemMessage(e)); - - return {}; - } - - token.reset(rawToken); - } - - TOKEN_ELEVATION e = {}; - DWORD size = sizeof(TOKEN_ELEVATION); - - if (!GetTokenInformation(token.get(), TokenElevation, &e, sizeof(e), &size)) { - const auto e = GetLastError(); - - log::error("while trying to check if process is elevated, " - "GetTokenInformation() failed: {}", - formatSystemMessage(e)); - - return {}; - } - - return (e.TokenIsElevated != 0); -} - -#else // Linux - -WindowsInfo::WindowsInfo() -{ - m_reported = getKernelVersion(); - m_real = m_reported; - m_release = getRelease(); - m_elevated = getElevated(); -} - -WindowsInfo::Version WindowsInfo::getKernelVersion() const -{ - struct utsname uts; - if (uname(&uts) != 0) { - log::error("uname() failed"); - return {}; - } - - Version v; - - // Parse kernel version string like "6.18.9-2-cachyos" - QString kver = QString::fromUtf8(uts.release); - QStringList parts = kver.split('.'); - - if (parts.size() >= 1) v.major = parts[0].toUInt(); - if (parts.size() >= 2) v.minor = parts[1].toUInt(); - if (parts.size() >= 3) { - // Build may contain suffix like "9-2-cachyos", take numeric prefix - QString buildStr = parts[2]; - int dashPos = buildStr.indexOf('-'); - if (dashPos >= 0) { - buildStr = buildStr.left(dashPos); - } - v.build = buildStr.toUInt(); - } - - return v; -} - -WindowsInfo::Release WindowsInfo::getRelease() const -{ - Release r; - - // Parse /etc/os-release - std::ifstream osRelease("/etc/os-release"); - if (osRelease.is_open()) { - std::string line; - while (std::getline(osRelease, line)) { - // Lines are KEY=VALUE or KEY="VALUE" - auto eqPos = line.find('='); - if (eqPos == std::string::npos) continue; - - std::string key = line.substr(0, eqPos); - std::string val = line.substr(eqPos + 1); - - // Strip quotes - if (val.size() >= 2 && val.front() == '"' && val.back() == '"') { - val = val.substr(1, val.size() - 2); - } - - if (key == "PRETTY_NAME") { - r.buildLab = QString::fromStdString(val); - } else if (key == "VERSION_ID") { - r.ID = QString::fromStdString(val); - } - } - } - - return r; -} - -std::optional WindowsInfo::getElevated() const -{ - return (geteuid() == 0); -} - -#endif // _WIN32 - -bool WindowsInfo::compatibilityMode() const -{ -#ifdef _WIN32 - if (m_real == Version()) { - // don't know the real version, can't guess compatibility mode - return false; - } - - return (m_real != m_reported); -#else - // compatibility mode doesn't apply on Linux - return false; -#endif -} - -const WindowsInfo::Version& WindowsInfo::reportedVersion() const -{ - return m_reported; -} - -const WindowsInfo::Version& WindowsInfo::realVersion() const -{ - return m_real; -} - -const WindowsInfo::Release& WindowsInfo::release() const -{ - return m_release; -} - -std::optional WindowsInfo::isElevated() const -{ - return m_elevated; -} - -QString WindowsInfo::toString() const -{ - QStringList sl; - - const QString reported = m_reported.toString(); - const QString real = m_real.toString(); - - // version - sl.push_back("version " + reported); - - // real version if different - if (compatibilityMode()) { - sl.push_back("real version " + real); - } - -#ifdef _WIN32 - // build.UBR, such as 17763.557 - if (m_release.UBR != 0) { - DWORD build = 0; - - if (compatibilityMode()) { - build = m_real.build; - } else { - build = m_reported.build; - } - - sl.push_back(QString("%1.%2").arg(build).arg(m_release.UBR)); - } -#endif - - // release ID - if (!m_release.ID.isEmpty()) { - sl.push_back("release " + m_release.ID); - } - - // buildlab string / distro name - if (!m_release.buildLab.isEmpty()) { - sl.push_back(m_release.buildLab); - } - - // elevated - QString elevated = "?"; - if (m_elevated.has_value()) { - elevated = (*m_elevated ? "yes" : "no"); - } - - sl.push_back("elevated: " + elevated); - - return sl.join(", "); -} - -} // namespace env +#include "envwindows.h" +#include "env.h" +#include "envmodule.h" +#include +#include + +#include +#include +#include +#include + +namespace env +{ + +using namespace MOBase; + +WindowsInfo::WindowsInfo() +{ + m_reported = getKernelVersion(); + m_real = m_reported; + m_release = getRelease(); + m_elevated = getElevated(); +} + +WindowsInfo::Version WindowsInfo::getKernelVersion() const +{ + struct utsname uts; + if (uname(&uts) != 0) { + log::error("uname() failed"); + return {}; + } + + Version v; + + // Parse kernel version like "6.18.9-2-cachyos". + QString kver = QString::fromUtf8(uts.release); + QStringList parts = kver.split('.'); + + if (parts.size() >= 1) + v.major = parts[0].toUInt(); + if (parts.size() >= 2) + v.minor = parts[1].toUInt(); + if (parts.size() >= 3) { + QString buildStr = parts[2]; + int dashPos = buildStr.indexOf('-'); + if (dashPos >= 0) { + buildStr = buildStr.left(dashPos); + } + v.build = buildStr.toUInt(); + } + + return v; +} + +WindowsInfo::Release WindowsInfo::getRelease() const +{ + Release r; + + std::ifstream osRelease("/etc/os-release"); + if (osRelease.is_open()) { + std::string line; + while (std::getline(osRelease, line)) { + auto eqPos = line.find('='); + if (eqPos == std::string::npos) + continue; + + std::string key = line.substr(0, eqPos); + std::string val = line.substr(eqPos + 1); + + if (val.size() >= 2 && val.front() == '"' && val.back() == '"') { + val = val.substr(1, val.size() - 2); + } + + if (key == "PRETTY_NAME") { + r.buildLab = QString::fromStdString(val); + } else if (key == "VERSION_ID") { + r.ID = QString::fromStdString(val); + } + } + } + + return r; +} + +std::optional WindowsInfo::getElevated() const +{ + return (geteuid() == 0); +} + +bool WindowsInfo::compatibilityMode() const +{ + return false; +} + +const WindowsInfo::Version& WindowsInfo::reportedVersion() const +{ + return m_reported; +} + +const WindowsInfo::Version& WindowsInfo::realVersion() const +{ + return m_real; +} + +const WindowsInfo::Release& WindowsInfo::release() const +{ + return m_release; +} + +std::optional WindowsInfo::isElevated() const +{ + return m_elevated; +} + +QString WindowsInfo::toString() const +{ + QStringList sl; + + sl.push_back("version " + m_reported.toString()); + + if (!m_release.ID.isEmpty()) { + sl.push_back("release " + m_release.ID); + } + if (!m_release.buildLab.isEmpty()) { + sl.push_back(m_release.buildLab); + } + + QString elevated = "?"; + if (m_elevated.has_value()) { + elevated = (*m_elevated ? "yes" : "no"); + } + sl.push_back("elevated: " + elevated); + + return sl.join(", "); +} + +} // namespace env diff --git a/src/src/envwindows.h b/src/src/envwindows.h index 938bf3f..70d8d2a 100644 --- a/src/src/envwindows.h +++ b/src/src/envwindows.h @@ -14,11 +14,7 @@ class WindowsInfo public: struct Version { -#ifdef _WIN32 - DWORD major = 0, minor = 0, build = 0; -#else uint32_t major = 0, minor = 0, build = 0; -#endif QString toString() const { @@ -43,11 +39,7 @@ public: QString ID; // some sub-build number, may be empty -#ifdef _WIN32 - DWORD UBR; -#else uint32_t UBR; -#endif Release() : UBR(0) {} }; @@ -83,19 +75,8 @@ private: Release m_release; std::optional m_elevated; -#ifdef _WIN32 - // uses RtlGetVersion() to get the version number as reported by Windows - // - Version getReportedVersion(HINSTANCE ntdll) const; - - // uses RtlGetNtVersionNumbers() to get the real version number - // - Version getRealVersion(HINSTANCE ntdll) const; -#else // uses uname() to get the kernel version - // Version getKernelVersion() const; -#endif // gets various information from the registry (Windows) or /etc/os-release (Linux) // -- cgit v1.3.1