#ifdef _WIN32 // clang-format off #define WIN32_LEAN_AND_MEAN #include #include #include #include #include #include #include #include #include #include #include #include #include "src/core/util/error.hpp" #include "src/core/util/helpers.hpp" #include "src/core/util/logging.hpp" #include "src/core/util/types.hpp" #include "os.hpp" // clang-format on using RtlGetVersionPtr = NTSTATUS(WINAPI*)(PRTL_OSVERSIONINFOW); namespace { using util::error::DraconisError, util::error::DraconisErrorCode; using namespace util::types; struct ProcessData { DWORD parentPid = 0; String baseExeNameLower; }; // clang-format off constexpr Array, 3> windowsShellMap = {{ { "cmd", "Command Prompt" }, { "powershell", "PowerShell" }, { "pwsh", "PowerShell Core" }, }}; constexpr Array, 3> msysShellMap = {{ { "bash", "Bash" }, { "zsh", "Zsh" }, { "fish", "Fish" }, }}; // clang-format on fn GetRegistryValue(const HKEY& hKey, const String& subKey, const String& valueName) -> String { HKEY key = nullptr; if (RegOpenKeyExA(hKey, subKey.c_str(), 0, KEY_READ, &key) != ERROR_SUCCESS) return ""; DWORD dataSize = 0; DWORD type = 0; if (RegQueryValueExA(key, valueName.c_str(), nullptr, &type, nullptr, &dataSize) != ERROR_SUCCESS) { RegCloseKey(key); return ""; } String value((type == REG_SZ || type == REG_EXPAND_SZ) ? dataSize - 1 : dataSize, '\0'); if (RegQueryValueExA(key, valueName.c_str(), nullptr, nullptr, reinterpret_cast(value.data()), &dataSize) != ERROR_SUCCESS) { RegCloseKey(key); return ""; } RegCloseKey(key); return value; } template fn FindShellInProcessTree(const DWORD startPid, const Array, sz>& shellMap) -> Option { if (startPid == 0) return None; std::unordered_map processMap; const HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnap == INVALID_HANDLE_VALUE) { error_log("FindShellInProcessTree: Failed snapshot, error {}", GetLastError()); return None; } PROCESSENTRY32 pe32; pe32.dwSize = sizeof(PROCESSENTRY32); if (Process32First(hSnap, &pe32)) { do { String fullName = pe32.szExeFile; String baseName; const size_t lastSlash = fullName.find_last_of("\\/"); baseName = (lastSlash == String::npos) ? fullName : fullName.substr(lastSlash + 1); std::transform(baseName.begin(), baseName.end(), baseName.begin(), [](const u8 character) { return std::tolower(character); }); if (baseName.length() > 4 && baseName.ends_with(".exe")) baseName.resize(baseName.length() - 4); processMap[pe32.th32ProcessID] = ProcessData { pe32.th32ParentProcessID, std::move(baseName) }; } while (Process32Next(hSnap, &pe32)); } else { error_log("FindShellInProcessTree: Process32First failed, error {}", GetLastError()); } CloseHandle(hSnap); DWORD currentPid = startPid; i32 depth = 0; constexpr int maxDepth = 32; while (currentPid != 0 && depth < maxDepth) { auto procIt = processMap.find(currentPid); if (procIt == processMap.end()) break; const String& processName = procIt->second.baseExeNameLower; auto mapIter = std::ranges::find_if(shellMap, [&](const auto& pair) { return StringView { processName } == pair.first; }); if (mapIter != std::ranges::end(shellMap)) return String { mapIter->second }; currentPid = procIt->second.parentPid; depth++; } if (depth >= maxDepth) error_log("FindShellInProcessTree: Reached max depth limit ({}) walking parent PIDs from {}", maxDepth, startPid); return None; } fn GetBuildNumber() -> Option { try { using namespace winrt::Windows::System::Profile; const auto versionInfo = AnalyticsInfo::VersionInfo(); const winrt::hstring familyVersion = versionInfo.DeviceFamilyVersion(); if (!familyVersion.empty()) { const u64 versionUl = std::stoull(winrt::to_string(familyVersion)); return (versionUl >> 16) & 0xFFFF; } } catch (const winrt::hresult_error& e) { debug_log("WinRT error getting build number: {}", winrt::to_string(e.message())); } catch (const Exception& e) { debug_log("Standard exception getting build number: {}", e.what()); } return None; } } // namespace namespace os { fn GetMemInfo() -> Result { MEMORYSTATUSEX memInfo; memInfo.dwLength = sizeof(MEMORYSTATUSEX); if (GlobalMemoryStatusEx(&memInfo)) return memInfo.ullTotalPhys; DWORD lastError = GetLastError(); return Err(DraconisError( DraconisErrorCode::PlatformSpecific, std::format("GlobalMemoryStatusEx failed with error code {}", lastError) )); } fn GetNowPlaying() -> Result { using namespace winrt::Windows::Media::Control; using namespace winrt::Windows::Foundation; using Session = GlobalSystemMediaTransportControlsSession; using SessionManager = GlobalSystemMediaTransportControlsSessionManager; using MediaProperties = GlobalSystemMediaTransportControlsSessionMediaProperties; try { const IAsyncOperation sessionManagerOp = SessionManager::RequestAsync(); const SessionManager sessionManager = sessionManagerOp.get(); if (const Session currentSession = sessionManager.GetCurrentSession()) { const MediaProperties mediaProperties = currentSession.TryGetMediaPropertiesAsync().get(); return MediaInfo( winrt::to_string(mediaProperties.Title()), winrt::to_string(mediaProperties.Artist()), None, None ); } return Err(DraconisError(DraconisErrorCode::NotFound, "No media session found")); } catch (const winrt::hresult_error& e) { return Err(DraconisError(e)); } } fn GetOSVersion() -> Result { try { const String regSubKey = R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)"; String productName = GetRegistryValue(HKEY_LOCAL_MACHINE, regSubKey, "ProductName"); const String displayVersion = GetRegistryValue(HKEY_LOCAL_MACHINE, regSubKey, "DisplayVersion"); if (productName.empty()) return Err(DraconisError(DraconisErrorCode::NotFound, "ProductName not found in registry")); if (const Option buildNumberOpt = GetBuildNumber()) { if (const u64 buildNumber = *buildNumberOpt; buildNumber >= 22000) { if (const size_t pos = productName.find("Windows 10"); pos != String::npos) { const bool startBoundary = (pos == 0 || !isalnum(static_cast(productName[pos - 1]))); const bool endBoundary = (pos + 10 == productName.length() || !isalnum(static_cast(productName[pos + 10]))); if (startBoundary && endBoundary) { productName.replace(pos, 10, "Windows 11"); } } } } else { debug_log("Warning: Could not get build number via WinRT; Win11 detection might be inaccurate."); } return displayVersion.empty() ? productName : productName + " " + displayVersion; } catch (const std::exception& e) { return Err(DraconisError(e)); } } fn GetHost() -> Result { return GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SYSTEM\HardwareConfig\Current)", "SystemFamily"); } fn GetKernelVersion() -> Result { if (const HMODULE ntdllHandle = GetModuleHandleW(L"ntdll.dll")) { if (const auto rtlGetVersion = std::bit_cast(GetProcAddress(ntdllHandle, "RtlGetVersion"))) { RTL_OSVERSIONINFOW osInfo = {}; osInfo.dwOSVersionInfoSize = sizeof(osInfo); if (rtlGetVersion(&osInfo) == 0) return std::format( "{}.{}.{}.{}", osInfo.dwMajorVersion, osInfo.dwMinorVersion, osInfo.dwBuildNumber, osInfo.dwPlatformId ); } } return Err(DraconisError(DraconisErrorCode::NotFound, "Could not determine kernel version using RtlGetVersion")); } fn GetWindowManager() -> Option { BOOL compositionEnabled = FALSE; if (SUCCEEDED(DwmIsCompositionEnabled(&compositionEnabled))) { return compositionEnabled ? "DWM" : "Windows Manager (Basic)"; } error_log("GetWindowManager: DwmIsCompositionEnabled failed"); return None; } fn GetDesktopEnvironment() -> Option { const String buildStr = GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "CurrentBuildNumber"); if (buildStr.empty()) { debug_log("Failed to get CurrentBuildNumber from registry"); return None; } try { const i32 build = stoi(buildStr); // Windows 11+ (Fluent) if (build >= 22000) return "Fluent (Windows 11)"; // Windows 10 Fluent Era if (build >= 15063) return "Fluent (Windows 10)"; // Windows 8.1/10 Metro Era if (build >= 9200) { // Windows 8+ // Distinguish between Windows 8 and 10 const String productName = GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "ProductName"); if (productName.find("Windows 10") != String::npos) return "Metro (Windows 10)"; if (build >= 9600) return "Metro (Windows 8.1)"; return "Metro (Windows 8)"; } // Windows 7 Aero if (build >= 7600) return "Aero (Windows 7)"; // Pre-Win7 return "Classic"; } catch (...) { debug_log("Failed to parse CurrentBuildNumber"); return None; } } fn GetShell() -> Option { using util::helpers::GetEnv; if (const Result msystemResult = GetEnv("MSYSTEM"); msystemResult && !msystemResult->empty()) { String shellPath; if (const Result shellResult = GetEnv("SHELL"); shellResult && !shellResult->empty()) { shellPath = *shellResult; } else if (const Result loginShellResult = GetEnv("LOGINSHELL"); loginShellResult && !loginShellResult->empty()) { shellPath = *loginShellResult; } if (!shellPath.empty()) { const usize lastSlash = shellPath.find_last_of("\\/"); String shellExe = (lastSlash != String::npos) ? shellPath.substr(lastSlash + 1) : shellPath; std::ranges::transform(shellExe, shellExe.begin(), [](const u8 character) { return std::tolower(character); }); if (shellExe.ends_with(".exe")) shellExe.resize(shellExe.length() - 4); const auto iter = std::ranges::find_if(msysShellMap, [&](const auto& pair) { return StringView { shellExe } == pair.first; }); if (iter != std::ranges::end(msysShellMap)) return String { iter->second }; } const DWORD currentPid = GetCurrentProcessId(); if (const Option msysShell = FindShellInProcessTree(currentPid, msysShellMap)) return msysShell; return "MSYS2 Environment"; } const DWORD currentPid = GetCurrentProcessId(); if (const Option windowsShell = FindShellInProcessTree(currentPid, windowsShellMap)) return windowsShell; return None; } fn GetDiskUsage() -> Result { ULARGE_INTEGER freeBytes, totalBytes; if (GetDiskFreeSpaceExW(L"C:\\", nullptr, &totalBytes, &freeBytes)) return DiskSpace { .used_bytes = totalBytes.QuadPart - freeBytes.QuadPart, .total_bytes = totalBytes.QuadPart }; return Err(DraconisError(util::error::DraconisErrorCode::NotFound, "Failed to get disk usage")); } fn GetPackageCount() -> Result { try { return std::ranges::distance(winrt::Windows::Management::Deployment::PackageManager().FindPackagesForUser(L"")); } catch (const winrt::hresult_error& e) { return Err(DraconisError(e)); } } } // namespace os #endif