From 2219182539abe33a231c1355c96789db8f811014 Mon Sep 17 00:00:00 2001 From: Mars Date: Wed, 23 Apr 2025 21:47:25 -0400 Subject: [PATCH] um i think this owrks --- .clang-format | 3 + meson.build | 73 +++++---- src/config/config.cpp | 3 +- src/main.cpp | 31 ++-- src/os/linux.cpp | 14 +- src/os/os.h | 84 +++++----- src/os/windows.cpp | 357 +++++++++++++++++++----------------------- src/util/macros.h | 48 +++--- src/util/types.h | 27 ++-- 9 files changed, 306 insertions(+), 334 deletions(-) diff --git a/.clang-format b/.clang-format index 40e31ca..7385b38 100644 --- a/.clang-format +++ b/.clang-format @@ -1,8 +1,11 @@ AlignAfterOpenBracket: BlockIndent AlignArrayOfStructures: Right AlignConsecutiveAssignments: true +AlignConsecutiveShortCaseStatements: + Enabled: true AlignConsecutiveDeclarations: true AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: true AllowShortEnumsOnASingleLine: true AllowShortFunctionsOnASingleLine: All AllowShortLoopsOnASingleLine: true diff --git a/meson.build b/meson.build index 89eefed..51ab1f4 100644 --- a/meson.build +++ b/meson.build @@ -4,12 +4,12 @@ project( 'draconis++', 'cpp', - version: '0.1.0', - default_options: [ + version : '0.1.0', + default_options : [ 'default_library=static', - 'warning_level=everything', 'buildtype=debugoptimized', 'b_vscrt=mt', + 'warning_level=3', ], ) @@ -32,29 +32,34 @@ common_warning_flags = [ ] common_cpp_flags = { - 'common': [ + 'common' : [ '-fno-strict-enums', '-fvisibility=hidden', '-fvisibility-inlines-hidden', '-std=c++26', ], - 'msvc': [ + 'msvc' : [ '-DNOMINMAX', '/MT', '/Zc:__cplusplus', '/Zc:preprocessor', + '/external:W0', + '/external:anglebrackets', '/std:c++latest', + '/w44668', + '/w44710', + '/w44820', ], - 'unix_extra': [ + 'unix_extra' : [ '-march=native', '-nostdlib++', ], - 'windows_extra': '-DCURL_STATICLIB', + 'windows_extra' : '-DCURL_STATICLIB', } # Configure Objective-C++ for macOS if host_system == 'darwin' - add_languages('objcpp', native: false) + add_languages('objcpp', native : false) objcpp = meson.get_compiler('objcpp') objcpp_flags = common_warning_flags + [ '-Wno-switch-default', @@ -62,7 +67,7 @@ if host_system == 'darwin' '-fvisibility=hidden', '-fvisibility-inlines-hidden', ] - add_project_arguments(objcpp.get_supported_arguments(objcpp_flags), language: 'objcpp') + add_project_arguments(objcpp.get_supported_arguments(objcpp_flags), language : 'objcpp') endif # Apply C++ compiler arguments @@ -79,7 +84,7 @@ else common_cpp_args = cpp.get_supported_arguments(common_cpp_args) endif -add_project_arguments(common_cpp_args, language: 'cpp') +add_project_arguments(common_cpp_args, language : 'cpp') # ------- # # Files # @@ -87,9 +92,9 @@ add_project_arguments(common_cpp_args, language: 'cpp') base_sources = files('src/config/config.cpp', 'src/config/weather.cpp', 'src/main.cpp') platform_sources = { - 'linux': ['src/os/linux.cpp', 'src/os/linux/issetugid_stub.cpp', 'src/os/linux/display_guards.cpp'], - 'darwin': ['src/os/macos.cpp', 'src/os/macos/bridge.mm'], - 'windows': ['src/os/windows.cpp'], + 'linux' : ['src/os/linux.cpp', 'src/os/linux/issetugid_stub.cpp', 'src/os/linux/display_guards.cpp'], + 'darwin' : ['src/os/macos.cpp', 'src/os/macos/bridge.mm'], + 'windows' : ['src/os/windows.cpp'], } sources = base_sources + files(platform_sources.get(host_system, [])) @@ -98,9 +103,9 @@ sources = base_sources + files(platform_sources.get(host_system, [])) # Dependencies Config # # --------------------- # common_deps = [ - dependency('libcurl', include_type: 'system', static: true), - dependency('tomlplusplus', include_type: 'system', static: true), - dependency('openssl', include_type: 'system', static: true, required: false), + dependency('libcurl', include_type : 'system', static : true), + dependency('tomlplusplus', include_type : 'system', static : true), + dependency('openssl', include_type : 'system', static : true, required : false), ] # Platform-specific dependencies @@ -110,8 +115,8 @@ if host_system == 'darwin' platform_deps += [ dependency( 'appleframeworks', - modules: ['foundation', 'mediaplayer', 'systemconfiguration'], - static: true, + modules : ['foundation', 'mediaplayer', 'systemconfiguration'], + static : true, ), dependency('iconv'), ] @@ -128,8 +133,8 @@ elif host_system == 'linux' dependency('xau'), dependency('xdmcp'), dependency('wayland-client'), - dependency('sigc++-3.0', include_type: 'system'), - dependency('dbus-cxx', include_type: 'system'), + dependency('sigc++-3.0', include_type : 'system'), + dependency('dbus-cxx', include_type : 'system'), ] endif @@ -137,30 +142,30 @@ endif ftxui_components = ['ftxui::screen', 'ftxui::dom', 'ftxui::component'] ftxui_dep = dependency( 'ftxui', - modules: ftxui_components, - include_type: 'system', - static: true, - required: false, + modules : ftxui_components, + include_type : 'system', + static : true, + required : false, ) if not ftxui_dep.found() ftxui_dep = declare_dependency( - dependencies: [ - dependency('ftxui-dom', fallback: ['ftxui', 'dom_dep']), - dependency('ftxui-screen', fallback: ['ftxui', 'screen_dep']), - dependency('ftxui-component', fallback: ['ftxui', 'component_dep']), + dependencies : [ + dependency('ftxui-dom', fallback : ['ftxui', 'dom_dep']), + dependency('ftxui-screen', fallback : ['ftxui', 'screen_dep']), + dependency('ftxui-component', fallback : ['ftxui', 'component_dep']), ], ) endif -glaze_dep = dependency('glaze', include_type: 'system', required: false) +glaze_dep = dependency('glaze', include_type : 'system', required : false) if not glaze_dep.found() cmake = import('cmake') glaze_proj = cmake.subproject('glaze') - glaze_dep = glaze_proj.dependency('glaze_glaze', include_type: 'system') + glaze_dep = glaze_proj.dependency('glaze_glaze', include_type : 'system') endif # Combine all dependencies @@ -184,8 +189,8 @@ endif executable( 'draconis++', sources, - objc_args: objc_args, - link_args: link_args, - dependencies: deps, - install: true, + objc_args : objc_args, + link_args : link_args, + dependencies : deps, + install : true, ) diff --git a/src/config/config.cpp b/src/config/config.cpp index 9af3ac7..f4d9ad5 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include "config.h" @@ -149,6 +148,8 @@ fn Config::getInstance() -> Config { } const toml::parse_result config = toml::parse_file(configPath.string()); + + DEBUG_LOG("Config loaded from {}", configPath.string()); return fromToml(config); } catch (const std::exception& e) { DEBUG_LOG("Config loading failed: {}, using defaults", e.what()); diff --git a/src/main.cpp b/src/main.cpp index 8404d5f..592e3eb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -146,19 +146,19 @@ namespace { // Single-threaded execution for core system info (faster on Windows) data.date = GetDate(); - data.host = GetHost(); - data.kernel_version = GetKernelVersion(); - data.os_version = GetOSVersion(); - data.mem_info = GetMemInfo(); + data.host = os::GetHost(); + data.kernel_version = os::GetKernelVersion(); + data.os_version = os::GetOSVersion(); + data.mem_info = os::GetMemInfo(); // Desktop environment info - data.desktop_environment = GetDesktopEnvironment(); - data.window_manager = GetWindowManager(); + data.desktop_environment = os::GetDesktopEnvironment(); + data.window_manager = os::GetWindowManager(); // Parallel execution for disk/shell only auto diskShell = std::async(std::launch::async, [] { - auto [used, total] = GetDiskUsage(); - return std::make_tuple(used, total, GetShell()); + auto [used, total] = os::GetDiskUsage(); + return std::make_tuple(used, total, os::GetShell()); }); // Conditional tasks @@ -169,7 +169,7 @@ namespace { weather = std::async(std::launch::async, [&config] { return config.weather.getWeatherInfo(); }); if (config.now_playing.enabled) - nowPlaying = std::async(std::launch::async, GetNowPlaying); + nowPlaying = std::async(std::launch::async, os::GetNowPlaying); // Get remaining results auto [used, total, shell] = diskShell.get(); @@ -330,17 +330,8 @@ namespace { if (std::holds_alternative(error)) switch (std::get(error)) { - case NowPlayingCode::NoPlayers: - DEBUG_LOG("No players found"); - break; - case NowPlayingCode::NoActivePlayer: - DEBUG_LOG("No active player found"); - break; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wcovered-switch-default" - default: - std::unreachable(); -#pragma clang diagnostic pop + case NowPlayingCode::NoPlayers: DEBUG_LOG("No players found"); break; + case NowPlayingCode::NoActivePlayer: DEBUG_LOG("No active player found"); break; } #ifdef _WIN32 diff --git a/src/os/linux.cpp b/src/os/linux.cpp index 6e23854..c0e15e7 100644 --- a/src/os/linux.cpp +++ b/src/os/linux.cpp @@ -219,13 +219,13 @@ namespace { fn DetectFromProcesses() -> Option { // clang-format off const Array, 7> processChecks = {{ - { "plasmashell", "KDE" }, - { "gnome-shell", "GNOME" }, - { "xfce4-session", "XFCE" }, - { "mate-session", "MATE" }, - { "cinnamon-session", "Cinnamon" }, - { "budgie-wm", "Budgie" }, - { "lxqt-session", "LXQt" }, + { "plasmashell", "KDE" }, + { "gnome-shell", "GNOME" }, + { "xfce4-session", "XFCE" }, + { "mate-session", "MATE" }, + { "cinnamon-session", "Cinnamon" }, + { "budgie-wm", "Budgie" }, + { "lxqt-session", "LXQt" }, }}; // clang-format on diff --git a/src/os/os.h b/src/os/os.h index 9fd2340..4709b29 100644 --- a/src/os/os.h +++ b/src/os/os.h @@ -3,53 +3,55 @@ #include "../util/macros.h" #include "../util/types.h" -/** - * @brief Get the amount of installed RAM in bytes. - */ -fn GetMemInfo() -> Result; +namespace os { + /** + * @brief Get the amount of installed RAM in bytes. + */ + fn GetMemInfo() -> Result; -/** - * @brief Get the currently playing song metadata. - */ -fn GetNowPlaying() -> Result; + /** + * @brief Get the currently playing song metadata. + */ + fn GetNowPlaying() -> Result; -/** - * @brief Get the OS version. - */ -fn GetOSVersion() -> Result; + /** + * @brief Get the OS version. + */ + fn GetOSVersion() -> Result; -/** - * @brief Get the current desktop environment. - */ -fn GetDesktopEnvironment() -> Option; + /** + * @brief Get the current desktop environment. + */ + fn GetDesktopEnvironment() -> Option; -/** - * @brief Get the current window manager. - */ -fn GetWindowManager() -> String; + /** + * @brief Get the current window manager. + */ + fn GetWindowManager() -> String; -/** - * @brief Get the current shell. - */ -fn GetShell() -> String; + /** + * @brief Get the current shell. + */ + fn GetShell() -> String; -/** - * @brief Get the product family - */ -fn GetHost() -> String; + /** + * @brief Get the product family + */ + fn GetHost() -> String; -/** - * @brief Get the kernel version. - */ -fn GetKernelVersion() -> String; + /** + * @brief Get the kernel version. + */ + fn GetKernelVersion() -> String; -/** - * @brief Get the number of installed packages. - */ -fn GetPackageCount() -> u64; + /** + * @brief Get the number of installed packages. + */ + fn GetPackageCount() -> u64; -/** - * @brief Get the current disk usage. - * @return std::pair Used space/total space - */ -fn GetDiskUsage() -> Pair; + /** + * @brief Get the current disk usage. + * @return std::pair Used space/total space + */ + fn GetDiskUsage() -> Pair; +} \ No newline at end of file diff --git a/src/os/windows.cpp b/src/os/windows.cpp index 51e7a6c..e20349a 100644 --- a/src/os/windows.cpp +++ b/src/os/windows.cpp @@ -9,20 +9,57 @@ // clang-format on #include -#include +#include #include #include #include #include +#include #include #include #include "os.h" -using RtlGetVersionPtr = NTSTATUS(WINAPI*)(PRTL_OSVERSIONINFOW); - -// NOLINTBEGIN(*-pro-type-cstyle-cast,*-no-int-to-ptr,*-pro-type-reinterpret-cast) namespace { + struct OSVersion { + u16 major; + u16 minor; + u16 build; + u16 revision; + + static fn parseDeviceFamilyVersion(const winrt::hstring& versionString) -> OSVersion { + try { + const u64 versionUl = std::stoull(winrt::to_string(versionString)); + return { + .major = static_cast((versionUl >> 48) & 0xFFFF), + .minor = static_cast((versionUl >> 32) & 0xFFFF), + .build = static_cast((versionUl >> 16) & 0xFFFF), + .revision = static_cast(versionUl & 0xFFFF), + }; + } catch (const std::invalid_argument& e) { + ERROR_LOG("Invalid argument: {}", e.what()); + } catch (const std::out_of_range& e) { + ERROR_LOG("Value out of range: {}", e.what()); + } catch (const winrt::hresult_error& e) { ERROR_LOG("Windows error: {}", winrt::to_string(e.message())); } + + return { .major = 0, .minor = 0, .build = 0, .revision = 0 }; + } + }; + + // 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 + class ProcessSnapshot { public: ProcessSnapshot() : h_snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)) {} @@ -39,8 +76,8 @@ namespace { [[nodiscard]] fn isValid() const -> bool { return h_snapshot != INVALID_HANDLE_VALUE; } - [[nodiscard]] fn getProcesses() const -> std::vector> { - std::vector> processes; + [[nodiscard]] fn getProcesses() const -> Vec> { + Vec> processes; if (!isValid()) return processes; @@ -51,12 +88,9 @@ namespace { if (!Process32First(h_snapshot, &pe32)) return processes; - // Get first process if (Process32First(h_snapshot, &pe32)) { - // Add first process to vector processes.emplace_back(pe32.th32ProcessID, String(reinterpret_cast(pe32.szExeFile))); - // Add remaining processes while (Process32Next(h_snapshot, &pe32)) processes.emplace_back(pe32.th32ProcessID, String(reinterpret_cast(pe32.szExeFile))); } @@ -79,10 +113,9 @@ namespace { return ""; } - // For string values, allocate one less byte to avoid the null terminator String value((type == REG_SZ || type == REG_EXPAND_SZ) ? dataSize - 1 : dataSize, '\0'); - if (RegQueryValueExA(key, valueName.c_str(), nullptr, nullptr, std::bit_cast(value.data()), &dataSize) != + if (RegQueryValueExA(key, valueName.c_str(), nullptr, nullptr, reinterpret_cast(value.data()), &dataSize) != ERROR_SUCCESS) { RegCloseKey(key); return ""; @@ -92,12 +125,12 @@ namespace { return value; } - fn GetProcessInfo() -> std::vector> { + fn GetProcessInfo() -> Vec> { const ProcessSnapshot snapshot; return snapshot.isValid() ? snapshot.getProcesses() : std::vector> {}; } - fn IsProcessRunning(const std::vector& processes, const String& name) -> bool { + fn IsProcessRunning(const Vec& processes, const String& name) -> bool { return std::ranges::any_of(processes, [&name](const String& proc) -> bool { return _stricmp(proc.c_str(), name.c_str()) == 0; }); @@ -143,139 +176,130 @@ namespace { return ""; } + + template + fn FindShellInProcessTree(const DWORD startPid, const Array, sz>& shellMap) + -> std::optional { + DWORD pid = startPid; + while (pid != 0) { + String processName = GetProcessName(pid); + + if (processName.empty()) { + pid = GetParentProcessId(pid); + continue; + } + + std::ranges::transform(processName, processName.begin(), [](const u8 character) { + return static_cast(std::tolower(static_cast(character))); + }); + + if (processName.length() > 4 && processName.substr(processName.length() - 4) == ".exe") + processName.resize(processName.length() - 4); + + auto iter = std::ranges::find_if(shellMap, [&](const auto& pair) { + return std::string_view { processName } == pair.first; + }); + + if (iter != std::ranges::end(shellMap)) + return String { iter->second }; + + pid = GetParentProcessId(pid); + } + + return std::nullopt; + } + + 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; + } } -fn GetMemInfo() -> Result { +fn os::GetMemInfo() -> Result { try { - using namespace winrt::Windows::System::Diagnostics; - const SystemDiagnosticInfo diag = SystemDiagnosticInfo::GetForCurrentSystem(); - return diag.MemoryUsage().GetReport().TotalPhysicalSizeInBytes(); + return winrt::Windows::System::Diagnostics::SystemDiagnosticInfo::GetForCurrentSystem() + .MemoryUsage() + .GetReport() + .TotalPhysicalSizeInBytes(); } catch (const winrt::hresult_error& e) { return Err(std::format("Failed to get memory info: {}", to_string(e.message()))); } } -fn GetNowPlaying() -> Result { +fn os::GetNowPlaying() -> Result { using namespace winrt::Windows::Media::Control; using namespace winrt::Windows::Foundation; - using MediaProperties = GlobalSystemMediaTransportControlsSessionMediaProperties; - using Session = GlobalSystemMediaTransportControlsSession; - using SessionManager = GlobalSystemMediaTransportControlsSessionManager; + using Session = GlobalSystemMediaTransportControlsSession; + using SessionManager = GlobalSystemMediaTransportControlsSessionManager; try { - // Request the session manager asynchronously const IAsyncOperation sessionManagerOp = SessionManager::RequestAsync(); const SessionManager sessionManager = sessionManagerOp.get(); - if (const Session currentSession = sessionManager.GetCurrentSession()) { - // Try to get the media properties asynchronously - const MediaProperties mediaProperties = currentSession.TryGetMediaPropertiesAsync().get(); + if (const Session currentSession = sessionManager.GetCurrentSession()) + return winrt::to_string(currentSession.TryGetMediaPropertiesAsync().get().Title()); - // Convert the hstring title to string - return to_string(mediaProperties.Title()); - } - - // If we reach this point, there is no current session return Err(NowPlayingCode::NoActivePlayer); } catch (const winrt::hresult_error& e) { return Err(e); } } -fn GetOSVersion() -> Result { - constexpr OSVERSIONINFOEXW osvi = { sizeof(OSVERSIONINFOEXW), 0, 0, 0, 0, { 0 }, 0, 0, 0, 0, 0 }; - NTSTATUS status = 0; +fn os::GetOSVersion() -> Result { + try { + const String regSubKey = R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)"; - if (const HMODULE ntdllHandle = GetModuleHandleW(L"ntdll.dll")) - if (const auto rtlGetVersion = std::bit_cast(GetProcAddress(ntdllHandle, "RtlGetVersion"))) - status = rtlGetVersion(std::bit_cast(&osvi)); + String productName = GetRegistryValue(HKEY_LOCAL_MACHINE, regSubKey, "ProductName"); + const String displayVersion = GetRegistryValue(HKEY_LOCAL_MACHINE, regSubKey, "DisplayVersion"); - String productName; - String edition; + if (productName.empty()) + return Err("Failed to read ProductName"); - if (status == 0) { - DWORD productType = 0; - if (GetProductInfo( - osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor, &productType - )) { - if (osvi.dwMajorVersion == 10) { - if (osvi.dwBuildNumber >= 22000) - productName = "Windows 11"; - else - productName = "Windows 10"; + if (const Option buildNumber = GetBuildNumber()) { + if (*buildNumber >= 22000) + if (const usize pos = productName.find("Windows 10"); + pos != String::npos && (pos == 0 || !isalnum(static_cast(productName[pos - 1]))) && + (pos + 10 == productName.length() || !isalnum(static_cast(productName[pos + 10])))) + productName.replace(pos, 10, "Windows 11"); + } else + DEBUG_LOG("Warning: Could not get build number via WinRT; Win11 patch relies on registry ProductName only."); - switch (productType) { - case PRODUCT_PROFESSIONAL: - edition = " Pro"; - break; - case PRODUCT_ENTERPRISE: - edition = " Enterprise"; - break; - case PRODUCT_EDUCATION: - edition = " Education"; - break; - case PRODUCT_HOME_BASIC: - case PRODUCT_HOME_PREMIUM: - edition = " Home"; - break; - case PRODUCT_CLOUDEDITION: - edition = " Cloud"; - break; - default: - break; - } - } - } - } else { - productName = - GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "ProductName"); - - if (const i32 buildNumber = stoi( - GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "CurrentBuildNumber") - ); - buildNumber >= 22000 && productName.find("Windows 10") != String::npos) - productName.replace(productName.find("Windows 10"), 10, "Windows 11"); - } - - if (!productName.empty()) { - String result = productName + edition; - - const String displayVersion = - GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "DisplayVersion"); - - if (!displayVersion.empty()) - result += " " + displayVersion; - - return result; - } - - return "Windows"; + return displayVersion.empty() ? productName : productName + " " + displayVersion; + } catch (const Exception& e) { return Err(std::format("Exception occurred getting OS version: {}", e.what())); } } -fn GetHost() -> String { - String hostName = GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SYSTEM\HardwareConfig\Current)", "SystemFamily"); - - return hostName; +fn os::GetHost() -> String { + return GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SYSTEM\HardwareConfig\Current)", "SystemFamily"); } -fn GetKernelVersion() -> String { - // ReSharper disable once CppLocalVariableMayBeConst - if (HMODULE ntdllHandle = GetModuleHandleW(L"ntdll.dll")) { - if (const auto rtlGetVersion = std::bit_cast(GetProcAddress(ntdllHandle, "RtlGetVersion"))) { - RTL_OSVERSIONINFOW osInfo = {}; - osInfo.dwOSVersionInfoSize = sizeof(osInfo); +fn os::GetKernelVersion() -> String { + try { + using namespace winrt::Windows::System::Profile; - if (rtlGetVersion(&osInfo) == 0) { - return std::format( - "{}.{}.{}.{}", osInfo.dwMajorVersion, osInfo.dwMinorVersion, osInfo.dwBuildNumber, osInfo.dwPlatformId - ); - } - } - } + const AnalyticsVersionInfo versionInfo = AnalyticsInfo::VersionInfo(); + + if (const winrt::hstring familyVersion = versionInfo.DeviceFamilyVersion(); !familyVersion.empty()) + if (auto [major, minor, build, revision] = OSVersion::parseDeviceFamilyVersion(familyVersion); build > 0) + return std::format("{}.{}.{}.{}", major, minor, build, revision); + } catch (const winrt::hresult_error& e) { + ERROR_LOG("WinRT error: {}", winrt::to_string(e.message())); + } catch (const Exception& e) { ERROR_LOG("Failed to get kernel version: {}", e.what()); } return ""; } -fn GetWindowManager() -> String { +fn os::GetWindowManager() -> String { const auto processInfo = GetProcessInfo(); std::vector processNames; @@ -300,7 +324,7 @@ fn GetWindowManager() -> String { return "Windows Manager"; } -fn GetDesktopEnvironment() -> Option { +fn os::GetDesktopEnvironment() -> Option { const String buildStr = GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "CurrentBuildNumber"); @@ -347,97 +371,45 @@ fn GetDesktopEnvironment() -> Option { } } -fn GetShell() -> String { - // TODO: update this to use GetEnv +fn os::GetShell() -> String { + const DWORD currentPid = GetCurrentProcessId(); - const std::unordered_map knownShells = { - { "cmd.exe", "Command Prompt" }, - { "powershell.exe", "PowerShell" }, - { "pwsh.exe", "PowerShell Core" }, - { "windowsterminal.exe", "Windows Terminal" }, - { "mintty.exe", "Mintty" }, - { "bash.exe", "Windows Subsystem for Linux" } - }; + if (const Result msystemResult = GetEnv("MSYSTEM")) { + String shellPath; + if (const Result shellResult = GetEnv("SHELL"); !shellResult->empty()) + shellPath = *shellResult; + else if (const Result loginShellResult = GetEnv("LOGINSHELL"); !loginShellResult->empty()) + shellPath = *loginShellResult; - char* msystemEnv = nullptr; - if (_dupenv_s(&msystemEnv, nullptr, "MSYSTEM") == 0 && msystemEnv != nullptr) { - const std::unique_ptr msystemEnvGuard(msystemEnv, free); + if (!shellPath.empty()) { + const usize lastSlash = shellPath.find_last_of("\\/"); + String shellExe = (lastSlash != String::npos) ? shellPath.substr(lastSlash + 1) : shellPath; - char* shell = nullptr; - size_t shellLen = 0; - _dupenv_s(&shell, &shellLen, "SHELL"); - const std::unique_ptr shellGuard(shell, free); + std::ranges::transform(shellExe, shellExe.begin(), [](const u8 c) { return std::tolower(c); }); - if (!shell || strlen(shell) == 0) { - char* loginShell = nullptr; - size_t loginShellLen = 0; - _dupenv_s(&loginShell, &loginShellLen, "LOGINSHELL"); - const std::unique_ptr loginShellGuard(loginShell, free); - shell = loginShell; + 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 }; } - if (shell) { - String shellExe; - const String shellPath = shell; - const size_t lastSlash = shellPath.find_last_of("\\/"); - shellExe = (lastSlash != String::npos) ? shellPath.substr(lastSlash + 1) : shellPath; - std::ranges::transform(shellExe, shellExe.begin(), ::tolower); + if (const Option msysShell = FindShellInProcessTree(currentPid, msysShellMap)) + return *msysShell; - // Use a map for shell name lookup instead of multiple if statements - const std::unordered_map shellNames = { - { "bash", "Bash" }, - { "zsh", "Zsh" }, - { "fish", "Fish" } - }; - - for (const auto& [pattern, name] : shellNames) { - if (shellExe.find(pattern) != String::npos) - return name; - } - - return shellExe.empty() ? "MSYS2" : "MSYS2/" + shellExe; - } - - const auto processInfo = GetProcessInfo(); - DWORD pid = GetCurrentProcessId(); - - while (pid != 0) { - String processName = GetProcessName(pid); - std::ranges::transform(processName, processName.begin(), ::tolower); - - const std::unordered_map msysShells = { - { "bash.exe", "Bash" }, - { "zsh.exe", "Zsh" }, - { "fish.exe", "Fish" }, - { "mintty.exe", "Mintty" } - }; - - for (const auto& [msysShellExe, shellName] : msysShells) { - if (processName == msysShellExe) - return shellName; - } - - pid = GetParentProcessId(pid); - } - - return "MSYS2"; + return "MSYS2 Environment"; } - DWORD pid = GetCurrentProcessId(); - while (pid != 0) { - String processName = GetProcessName(pid); - std::ranges::transform(processName, processName.begin(), ::tolower); + if (const Option windowsShell = FindShellInProcessTree(currentPid, windowsShellMap)) + return *windowsShell; - if (auto shellIterator = knownShells.find(processName); shellIterator != knownShells.end()) - return shellIterator->second; - - pid = GetParentProcessId(pid); - } - - return "Windows Console"; + return "Unknown Shell"; } -fn GetDiskUsage() -> std::pair { +fn os::GetDiskUsage() -> Pair { ULARGE_INTEGER freeBytes, totalBytes; if (GetDiskFreeSpaceExW(L"C:\\", nullptr, &totalBytes, &freeBytes)) @@ -445,6 +417,5 @@ fn GetDiskUsage() -> std::pair { return { 0, 0 }; } -// NOLINTEND(*-pro-type-cstyle-cast,*-no-int-to-ptr,*-pro-type-reinterpret-cast) #endif diff --git a/src/util/macros.h b/src/util/macros.h index 6b673ad..0c3c23a 100644 --- a/src/util/macros.h +++ b/src/util/macros.h @@ -17,9 +17,10 @@ #ifdef None #undef None -#define None std::nullopt #endif +#define None std::nullopt + namespace term { enum class Emphasis : u8 { none = 0, bold = 1, italic = 2 }; @@ -49,16 +50,14 @@ namespace term { struct FgColor { Color col; - constexpr explicit FgColor(Color color) : col(color) {} + constexpr explicit FgColor(const Color color) : col(color) {} [[nodiscard]] fn ansiCode() const -> String { return std::format("\033[{}m", static_cast(col)); } }; - constexpr fn Fg(Color color) -> FgColor { return FgColor(color); } - struct Style { Emphasis emph = Emphasis::none; - FgColor fg_col = FgColor(static_cast(-1)); // Invalid color + FgColor fg_col = FgColor(static_cast(-1)); [[nodiscard]] fn ansiCode() const -> String { String result; @@ -77,8 +76,13 @@ namespace term { } }; - constexpr fn operator|(Emphasis emph, FgColor fgColor)->Style { return { .emph = emph, .fg_col = fgColor }; } - constexpr fn operator|(FgColor fgColor, Emphasis emph)->Style { return { .emph = emph, .fg_col = fgColor }; } + constexpr fn operator|(const Emphasis emph, const FgColor fgColor)->Style { + return { .emph = emph, .fg_col = fgColor }; + } + + constexpr fn operator|(const FgColor fgColor, const Emphasis emph)->Style { + return { .emph = emph, .fg_col = fgColor }; + } constexpr CStr reset = "\033[0m"; @@ -122,19 +126,11 @@ fn LogImpl(const LogLevel level, const std::source_location& loc, std::format_st const auto [color, levelStr] = [&] { switch (level) { - case LogLevel::DEBUG: - return std::make_pair(log_colors::debug, "DEBUG"); - case LogLevel::INFO: - return std::make_pair(log_colors::info, "INFO "); - case LogLevel::WARN: - return std::make_pair(log_colors::warn, "WARN "); - case LogLevel::ERROR: - return std::make_pair(log_colors::error, "ERROR"); -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wcovered-switch-default" - default: - std::unreachable(); -#pragma clang diagnostic pop + case LogLevel::DEBUG: return std::make_pair(log_colors::debug, "DEBUG"); + case LogLevel::INFO: return std::make_pair(log_colors::info, "INFO "); + case LogLevel::WARN: return std::make_pair(log_colors::warn, "WARN "); + case LogLevel::ERROR: return std::make_pair(log_colors::error, "ERROR"); + default: std::unreachable(); } }(); @@ -142,20 +138,22 @@ fn LogImpl(const LogLevel level, const std::source_location& loc, std::format_st using namespace term; - Print(Fg(log_colors::timestamp), "[{:%H:%M:%S}] ", now); - Print(Emphasis::bold | Fg(color), "{} ", levelStr); + Print(FgColor(log_colors::timestamp), "[{:%H:%M:%S}] ", now); + Print(Emphasis::bold | FgColor(color), "{} ", levelStr); Print(fmt, std::forward(args)...); #ifndef NDEBUG - Print(Fg(log_colors::file_info), "\n{:>14} ", "╰──"); - Print(Emphasis::italic | Fg(log_colors::file_info), "{}:{}", filename, loc.line()); + Print(FgColor(log_colors::file_info), "\n{:>14} ", "╰──"); + Print(Emphasis::italic | FgColor(log_colors::file_info), "{}:{}", filename, loc.line()); #endif Print("\n"); } +#ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-macros" +#endif #ifdef NDEBUG #define DEBUG_LOG(...) static_cast(0) #else @@ -164,4 +162,6 @@ fn LogImpl(const LogLevel level, const std::source_location& loc, std::format_st #define INFO_LOG(...) LogImpl(LogLevel::INFO, std::source_location::current(), __VA_ARGS__) #define WARN_LOG(...) LogImpl(LogLevel::WARN, std::source_location::current(), __VA_ARGS__) #define ERROR_LOG(...) LogImpl(LogLevel::ERROR, std::source_location::current(), __VA_ARGS__) +#ifdef __clang__ #pragma clang diagnostic pop +#endif diff --git a/src/util/types.h b/src/util/types.h index 08b8200..71d5800 100644 --- a/src/util/types.h +++ b/src/util/types.h @@ -1,15 +1,12 @@ #pragma once #include -#include -#include #include #include #include #include #include #include -#include #include #include @@ -183,8 +180,8 @@ using Option = std::optional; * @typedef Array * @brief Represents a fixed-size array. */ -template -using Array = std::array; +template +using Array = std::array; /** * @typedef Vec @@ -264,18 +261,20 @@ enum class EnvError : u8 { NotFound, AccessError }; inline auto GetEnv(const String& name) -> Result { #ifdef _WIN32 - char* rawPtr = nullptr; - size_t bufferSize = 0; + char* rawPtr = nullptr; + usize bufferSize = 0; - if (_dupenv_s(&rawPtr, &bufferSize, name.c_str()) != 0) - return std::unexpected(EnvError::AccessError); + const i32 err = _dupenv_s(&rawPtr, &bufferSize, name.c_str()); - if (!rawPtr) - return std::unexpected(EnvError::NotFound); + const UniquePointer ptrManager(rawPtr, free); - const String result(rawPtr); - free(rawPtr); - return result; + if (err != 0) + return Err(EnvError::AccessError); + + if (!ptrManager) + return Err(EnvError::NotFound); + + return ptrManager.get(); #else CStr value = std::getenv(name.c_str());