meow
This commit is contained in:
parent
427b7b48a5
commit
2ac6fbfdec
5 changed files with 294 additions and 194 deletions
52
meson.build
52
meson.build
|
@ -38,15 +38,14 @@ common_cpp_flags = {
|
||||||
'-std=c++26',
|
'-std=c++26',
|
||||||
],
|
],
|
||||||
'msvc': [
|
'msvc': [
|
||||||
'-DNOMINMAX',
|
'-DNOMINMAX', '/Zc:__cplusplus',
|
||||||
'/Zc:__cplusplus',
|
|
||||||
'/std:c++latest',
|
'/std:c++latest',
|
||||||
],
|
],
|
||||||
'unix_extra': [
|
'unix_extra': [
|
||||||
'-march=native',
|
'-march=native',
|
||||||
'-nostdlib++',
|
'-nostdlib++',
|
||||||
],
|
],
|
||||||
'windows_extra': '-DCURL_STATICLIB'
|
'windows_extra': '-DCURL_STATICLIB',
|
||||||
}
|
}
|
||||||
|
|
||||||
# Configure Objective-C++ for macOS
|
# Configure Objective-C++ for macOS
|
||||||
|
@ -81,17 +80,13 @@ add_project_arguments(common_cpp_args, language: 'cpp')
|
||||||
# ------- #
|
# ------- #
|
||||||
# Files #
|
# Files #
|
||||||
# ------- #
|
# ------- #
|
||||||
base_sources = files(
|
base_sources = files('src/config/config.cpp', 'src/config/weather.cpp', 'src/main.cpp')
|
||||||
'src/main.cpp',
|
|
||||||
'src/config/config.cpp',
|
|
||||||
'src/config/weather.cpp'
|
|
||||||
)
|
|
||||||
|
|
||||||
platform_sources = {
|
platform_sources = {
|
||||||
'linux': ['src/os/linux.cpp', 'src/os/linux/issetugid_stub.cpp'],
|
'linux': ['src/os/linux.cpp', 'src/os/linux/issetugid_stub.cpp'],
|
||||||
'freebsd': ['src/os/freebsd.cpp'],
|
'freebsd': ['src/os/freebsd.cpp'],
|
||||||
'darwin': ['src/os/macos.cpp', 'src/os/macos/bridge.mm'],
|
'darwin': ['src/os/macos.cpp', 'src/os/macos/bridge.mm'],
|
||||||
'windows': ['src/os/windows.cpp']
|
'windows': ['src/os/windows.cpp'],
|
||||||
}
|
}
|
||||||
|
|
||||||
sources = base_sources + files(platform_sources.get(host_system, []))
|
sources = base_sources + files(platform_sources.get(host_system, []))
|
||||||
|
@ -103,6 +98,7 @@ common_deps = [
|
||||||
dependency('fmt', include_type: 'system', static: true),
|
dependency('fmt', include_type: 'system', static: true),
|
||||||
dependency('libcurl', include_type: 'system', static: true),
|
dependency('libcurl', include_type: 'system', static: true),
|
||||||
dependency('tomlplusplus', include_type: 'system', static: true),
|
dependency('tomlplusplus', include_type: 'system', static: true),
|
||||||
|
dependency('nlohmann_json', include_type: 'system', static: true),
|
||||||
dependency('openssl', include_type: 'system', static: true, required: false),
|
dependency('openssl', include_type: 'system', static: true, required: false),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -111,8 +107,12 @@ platform_deps = []
|
||||||
|
|
||||||
if host_system == 'darwin'
|
if host_system == 'darwin'
|
||||||
platform_deps += [
|
platform_deps += [
|
||||||
dependency('appleframeworks', modules: ['foundation', 'mediaplayer', 'systemconfiguration'], static: true),
|
dependency(
|
||||||
dependency('iconv')
|
'appleframeworks',
|
||||||
|
modules: ['foundation', 'mediaplayer', 'systemconfiguration'],
|
||||||
|
static: true,
|
||||||
|
),
|
||||||
|
dependency('iconv'),
|
||||||
]
|
]
|
||||||
elif host_system == 'windows'
|
elif host_system == 'windows'
|
||||||
platform_deps += [
|
platform_deps += [
|
||||||
|
@ -135,15 +135,21 @@ endif
|
||||||
# FTXUI configuration
|
# FTXUI configuration
|
||||||
cmake = import('cmake')
|
cmake = import('cmake')
|
||||||
ftxui_components = ['ftxui::screen', 'ftxui::dom', 'ftxui::component']
|
ftxui_components = ['ftxui::screen', 'ftxui::dom', 'ftxui::component']
|
||||||
ftxui_dep = dependency('ftxui', modules: ftxui_components, include_type: 'system', static: true, required: false)
|
ftxui_dep = dependency(
|
||||||
|
'ftxui',
|
||||||
|
modules: ftxui_components,
|
||||||
|
include_type: 'system',
|
||||||
|
static: true,
|
||||||
|
required: false,
|
||||||
|
)
|
||||||
|
|
||||||
if not ftxui_dep.found()
|
if not ftxui_dep.found()
|
||||||
ftxui_dep = declare_dependency(
|
ftxui_dep = declare_dependency(
|
||||||
dependencies: [
|
dependencies: [
|
||||||
dependency('ftxui-dom', fallback: ['ftxui', 'dom_dep']),
|
dependency('ftxui-dom', fallback: ['ftxui', 'dom_dep']),
|
||||||
dependency('ftxui-screen', fallback: ['ftxui', 'screen_dep']),
|
dependency('ftxui-screen', fallback: ['ftxui', 'screen_dep']),
|
||||||
dependency('ftxui-component', fallback: ['ftxui', 'component_dep'])
|
dependency('ftxui-component', fallback: ['ftxui', 'component_dep']),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -151,12 +157,14 @@ endif
|
||||||
reflectcpp_dep = dependency('reflectcpp', include_type: 'system', required: false, static: true)
|
reflectcpp_dep = dependency('reflectcpp', include_type: 'system', required: false, static: true)
|
||||||
if not reflectcpp_dep.found()
|
if not reflectcpp_dep.found()
|
||||||
cmake_opts = cmake.subproject_options()
|
cmake_opts = cmake.subproject_options()
|
||||||
cxx_flags = cpp.get_id() == 'msvc' ? '/w' : '-Wno-everything -std=c++26 -fvisibility=hidden' # Added visibility flag
|
cxx_flags = cpp.get_id() == 'msvc' ? '/w' : '-Wno-everything -std=c++26 -fvisibility=hidden'
|
||||||
|
|
||||||
cmake_opts.add_cmake_defines({
|
cmake_opts.add_cmake_defines(
|
||||||
'CMAKE_CXX_FLAGS': cxx_flags,
|
{
|
||||||
'CMAKE_VISIBILITY_INLINES_HIDDEN': 'ON' # Add this line
|
'CMAKE_CXX_FLAGS': cxx_flags,
|
||||||
})
|
'CMAKE_VISIBILITY_INLINES_HIDDEN': 'ON',
|
||||||
|
},
|
||||||
|
)
|
||||||
cmake_opts.append_compile_args('cpp', cxx_flags)
|
cmake_opts.append_compile_args('cpp', cxx_flags)
|
||||||
|
|
||||||
reflectcpp_proj = cmake.subproject('reflectcpp', options: cmake_opts)
|
reflectcpp_proj = cmake.subproject('reflectcpp', options: cmake_opts)
|
||||||
|
@ -174,7 +182,7 @@ objc_args = []
|
||||||
|
|
||||||
if host_system == 'darwin'
|
if host_system == 'darwin'
|
||||||
objc_args += ['-fobjc-arc']
|
objc_args += ['-fobjc-arc']
|
||||||
elif host_system == 'linux'
|
elif cpp.get_id() == 'clang'
|
||||||
link_args += ['-static-libgcc', '-static-libstdc++', '-static']
|
link_args += ['-static-libgcc', '-static-libstdc++', '-static']
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
@ -34,14 +34,21 @@ namespace {
|
||||||
|
|
||||||
fn Config::getInstance() -> Config {
|
fn Config::getInstance() -> Config {
|
||||||
fs::path configPath = GetConfigPath();
|
fs::path configPath = GetConfigPath();
|
||||||
|
|
||||||
|
// purely visual but whatever
|
||||||
|
#ifdef _WIN32
|
||||||
|
configPath /= "draconis++\\config.toml";
|
||||||
|
#else
|
||||||
configPath /= "draconis++/config.toml";
|
configPath /= "draconis++/config.toml";
|
||||||
|
#endif
|
||||||
|
|
||||||
const Result<Config> result = rfl::toml::load<Config>(configPath.string());
|
const Result<Config> result = rfl::toml::load<Config>(configPath.string());
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
ERROR_LOG("Failed to load config file: {}", result.error().what());
|
DEBUG_LOG("Failed to load config file: {}", result.error().what());
|
||||||
|
|
||||||
exit(1);
|
// Use default values
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.value();
|
return result.value();
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <rfl.hpp>
|
#include <rfl.hpp>
|
||||||
#include <rfl/Field.hpp>
|
#include <rfl/Field.hpp>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
#include "../util/macros.h"
|
#include "../util/macros.h"
|
||||||
#include "../util/types.h"
|
#include "../util/types.h"
|
||||||
|
@ -10,7 +11,26 @@
|
||||||
using Location = std::variant<string, Coords>;
|
using Location = std::variant<string, Coords>;
|
||||||
|
|
||||||
struct General {
|
struct General {
|
||||||
rfl::Field<"name", string> name = "user";
|
// TODO: implement for the other OSes idiot
|
||||||
|
string name =
|
||||||
|
#ifdef _WIN32
|
||||||
|
[]() -> string {
|
||||||
|
std::array<char, 256> username;
|
||||||
|
DWORD size = sizeof(username);
|
||||||
|
|
||||||
|
if (GetUserNameA(username.data(), &size))
|
||||||
|
return { username.data() };
|
||||||
|
|
||||||
|
return "Unknown";
|
||||||
|
}()
|
||||||
|
#elif defined(__linux__)
|
||||||
|
"Linux"
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
"MacOS"
|
||||||
|
#else
|
||||||
|
"Unknown"
|
||||||
|
#endif
|
||||||
|
;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NowPlaying {
|
struct NowPlaying {
|
||||||
|
@ -29,7 +49,7 @@ struct Weather {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Config {
|
struct Config {
|
||||||
rfl::Field<"general", General> general = General { .name = "user" };
|
rfl::Field<"general", General> general = General();
|
||||||
rfl::Field<"now_playing", NowPlaying> now_playing = NowPlaying();
|
rfl::Field<"now_playing", NowPlaying> now_playing = NowPlaying();
|
||||||
rfl::Field<"weather", Weather> weather = Weather();
|
rfl::Field<"weather", Weather> weather = Weather();
|
||||||
|
|
||||||
|
|
34
src/main.cpp
34
src/main.cpp
|
@ -96,28 +96,26 @@ namespace {
|
||||||
data.window_manager = GetWindowManager();
|
data.window_manager = GetWindowManager();
|
||||||
|
|
||||||
// Parallel execution for disk/shell only
|
// Parallel execution for disk/shell only
|
||||||
// auto diskShell = std::async(std::launch::async, [] {
|
auto diskShell = std::async(std::launch::async, [] {
|
||||||
// auto [used, total] = GetDiskUsage();
|
auto [used, total] = GetDiskUsage();
|
||||||
// return std::make_tuple(used, total, GetShell());
|
return std::make_tuple(used, total, GetShell());
|
||||||
// });
|
});
|
||||||
|
|
||||||
// Conditional tasks
|
// Conditional tasks
|
||||||
std::future<WeatherOutput> weather;
|
std::future<WeatherOutput> weather;
|
||||||
std::future<std::expected<string, NowPlayingError>> nowPlaying;
|
std::future<std::expected<string, NowPlayingError>> nowPlaying;
|
||||||
|
|
||||||
if (config.weather.get().enabled) {
|
if (config.weather.get().enabled)
|
||||||
weather = std::async(std::launch::async, [&config] { return config.weather.get().getWeatherInfo(); });
|
weather = std::async(std::launch::async, [&config] { return config.weather.get().getWeatherInfo(); });
|
||||||
}
|
|
||||||
|
|
||||||
if (config.now_playing.get().enabled) {
|
if (config.now_playing.get().enabled)
|
||||||
nowPlaying = std::async(std::launch::async, GetNowPlaying);
|
nowPlaying = std::async(std::launch::async, GetNowPlaying);
|
||||||
}
|
|
||||||
|
|
||||||
// Get remaining results
|
// Get remaining results
|
||||||
// auto [used, total, shell] = diskShell.get();
|
auto [used, total, shell] = diskShell.get();
|
||||||
// data.disk_used = used;
|
data.disk_used = used;
|
||||||
// data.disk_total = total;
|
data.disk_total = total;
|
||||||
// data.shell = shell;
|
data.shell = shell;
|
||||||
|
|
||||||
if (weather.valid())
|
if (weather.valid())
|
||||||
data.weather_info = weather.get();
|
data.weather_info = weather.get();
|
||||||
|
@ -142,7 +140,7 @@ namespace {
|
||||||
|
|
||||||
fn SystemInfoBox(const Config& config, const SystemData& data) -> Element {
|
fn SystemInfoBox(const Config& config, const SystemData& data) -> Element {
|
||||||
// Fetch data
|
// Fetch data
|
||||||
const string& name = config.general.get().name.get();
|
const string& name = config.general.get().name;
|
||||||
const Weather weather = config.weather.get();
|
const Weather weather = config.weather.get();
|
||||||
const bool nowPlayingEnabled = config.now_playing.get().enabled;
|
const bool nowPlayingEnabled = config.now_playing.get().enabled;
|
||||||
|
|
||||||
|
@ -239,12 +237,11 @@ namespace {
|
||||||
ERROR_LOG("Failed to get memory info: {}", data.mem_info.error());
|
ERROR_LOG("Failed to get memory info: {}", data.mem_info.error());
|
||||||
|
|
||||||
// Add Disk usage row
|
// Add Disk usage row
|
||||||
// content.push_back(
|
content.push_back(
|
||||||
// createRow(" ", "Disk", fmt::format("{}/{}", BytesToGiB { data.disk_used }, BytesToGiB { data.disk_total
|
createRow(" ", "Disk", fmt::format("{}/{}", BytesToGiB { data.disk_used }, BytesToGiB { data.disk_total }))
|
||||||
// }))
|
);
|
||||||
// );
|
|
||||||
|
|
||||||
// content.push_back(createRow(" ", "Shell", data.shell));
|
content.push_back(createRow(" ", "Shell", data.shell));
|
||||||
|
|
||||||
content.push_back(separator() | color(borderColor));
|
content.push_back(separator() | color(borderColor));
|
||||||
|
|
||||||
|
@ -308,7 +305,6 @@ fn main() -> i32 {
|
||||||
const Config& config = Config::getInstance();
|
const Config& config = Config::getInstance();
|
||||||
const SystemData data = SystemData::fetchSystemData(config);
|
const SystemData data = SystemData::fetchSystemData(config);
|
||||||
|
|
||||||
// Add vertical box with forced newline
|
|
||||||
Element document = vbox({ hbox({ SystemInfoBox(config, data), filler() }), text("") });
|
Element document = vbox({ hbox({ SystemInfoBox(config, data), filler() }), text("") });
|
||||||
|
|
||||||
Screen screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
|
Screen screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
|
||||||
|
|
|
@ -19,22 +19,70 @@
|
||||||
|
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
|
|
||||||
|
using std::string_view;
|
||||||
using RtlGetVersionPtr = NTSTATUS(WINAPI*)(PRTL_OSVERSIONINFOW);
|
using RtlGetVersionPtr = NTSTATUS(WINAPI*)(PRTL_OSVERSIONINFOW);
|
||||||
|
|
||||||
// NOLINTBEGIN(*-pro-type-cstyle-cast,*-no-int-to-ptr)
|
// NOLINTBEGIN(*-pro-type-cstyle-cast,*-no-int-to-ptr)
|
||||||
namespace {
|
namespace {
|
||||||
|
class ProcessSnapshot {
|
||||||
|
public:
|
||||||
|
ProcessSnapshot() : h_snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)) {}
|
||||||
|
|
||||||
|
ProcessSnapshot(ProcessSnapshot&&) = delete;
|
||||||
|
ProcessSnapshot(const ProcessSnapshot&) = delete;
|
||||||
|
fn operator=(ProcessSnapshot&&)->ProcessSnapshot& = delete;
|
||||||
|
fn operator=(const ProcessSnapshot&)->ProcessSnapshot& = delete;
|
||||||
|
|
||||||
|
~ProcessSnapshot() {
|
||||||
|
if (h_snapshot != INVALID_HANDLE_VALUE)
|
||||||
|
CloseHandle(h_snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] fn isValid() const -> bool { return h_snapshot != INVALID_HANDLE_VALUE; }
|
||||||
|
|
||||||
|
[[nodiscard]] fn getProcesses() const -> std::vector<std::pair<DWORD, string>> {
|
||||||
|
std::vector<std::pair<DWORD, string>> processes;
|
||||||
|
|
||||||
|
if (!isValid())
|
||||||
|
return processes;
|
||||||
|
|
||||||
|
PROCESSENTRY32 pe32;
|
||||||
|
pe32.dwSize = sizeof(PROCESSENTRY32);
|
||||||
|
|
||||||
|
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(static_cast<const char*>(pe32.szExeFile)));
|
||||||
|
|
||||||
|
// Add remaining processes
|
||||||
|
while (Process32Next(h_snapshot, &pe32))
|
||||||
|
processes.emplace_back(pe32.th32ProcessID, string(static_cast<const char*>(pe32.szExeFile)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return processes;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE h_snapshot;
|
||||||
|
};
|
||||||
|
|
||||||
fn GetRegistryValue(const HKEY& hKey, const string& subKey, const string& valueName) -> string {
|
fn GetRegistryValue(const HKEY& hKey, const string& subKey, const string& valueName) -> string {
|
||||||
HKEY key = nullptr;
|
HKEY key = nullptr;
|
||||||
if (RegOpenKeyExA(hKey, subKey.c_str(), 0, KEY_READ, &key) != ERROR_SUCCESS)
|
if (RegOpenKeyExA(hKey, subKey.c_str(), 0, KEY_READ, &key) != ERROR_SUCCESS)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
DWORD dataSize = 0;
|
DWORD dataSize = 0;
|
||||||
if (RegQueryValueExA(key, valueName.c_str(), nullptr, nullptr, nullptr, &dataSize) != ERROR_SUCCESS) {
|
DWORD type = 0;
|
||||||
|
if (RegQueryValueExA(key, valueName.c_str(), nullptr, &type, nullptr, &dataSize) != ERROR_SUCCESS) {
|
||||||
RegCloseKey(key);
|
RegCloseKey(key);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
string value(dataSize, '\0');
|
// 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<LPBYTE>(value.data()), &dataSize) !=
|
if (RegQueryValueExA(key, valueName.c_str(), nullptr, nullptr, std::bit_cast<LPBYTE>(value.data()), &dataSize) !=
|
||||||
ERROR_SUCCESS) {
|
ERROR_SUCCESS) {
|
||||||
RegCloseKey(key);
|
RegCloseKey(key);
|
||||||
|
@ -42,92 +90,60 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
RegCloseKey(key);
|
RegCloseKey(key);
|
||||||
|
|
||||||
// Remove null terminator if present
|
|
||||||
if (!value.empty() && value.back() == '\0')
|
|
||||||
value.pop_back();
|
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add these function implementations
|
fn GetProcessInfo() -> std::vector<std::pair<DWORD, string>> {
|
||||||
fn GetRunningProcesses() -> std::vector<string> {
|
ProcessSnapshot snapshot;
|
||||||
std::vector<string> processes;
|
return snapshot.isValid() ? snapshot.getProcesses() : std::vector<std::pair<DWORD, string>> {};
|
||||||
// ReSharper disable once CppLocalVariableMayBeConst
|
|
||||||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
|
||||||
|
|
||||||
if (hSnapshot == INVALID_HANDLE_VALUE) // NOLINT(*-no-int-to-ptr)
|
|
||||||
return processes;
|
|
||||||
|
|
||||||
PROCESSENTRY32 pe32;
|
|
||||||
pe32.dwSize = sizeof(PROCESSENTRY32);
|
|
||||||
|
|
||||||
if (!Process32First(hSnapshot, &pe32)) {
|
|
||||||
CloseHandle(hSnapshot);
|
|
||||||
return processes;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (Process32Next(hSnapshot, &pe32)) processes.emplace_back(pe32.szExeFile);
|
|
||||||
|
|
||||||
CloseHandle(hSnapshot);
|
|
||||||
return processes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn IsProcessRunning(const std::vector<string>& processes, const string& name) -> bool {
|
fn IsProcessRunning(const std::vector<string>& processes, const string& name) -> bool {
|
||||||
return std::ranges::any_of(processes, [&name](const string& proc) {
|
return std::ranges::any_of(processes, [&name](const string& proc) -> bool {
|
||||||
return _stricmp(proc.c_str(), name.c_str()) == 0;
|
return _stricmp(proc.c_str(), name.c_str()) == 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetParentProcessId(DWORD pid) -> DWORD {
|
fn GetParentProcessId(DWORD pid) -> DWORD {
|
||||||
// ReSharper disable once CppLocalVariableMayBeConst
|
ProcessSnapshot snapshot;
|
||||||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
if (!snapshot.isValid())
|
||||||
if (hSnapshot == INVALID_HANDLE_VALUE)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
PROCESSENTRY32 pe32;
|
PROCESSENTRY32 pe32;
|
||||||
pe32.dwSize = sizeof(PROCESSENTRY32);
|
pe32.dwSize = sizeof(PROCESSENTRY32);
|
||||||
DWORD parentPid = 0;
|
|
||||||
|
|
||||||
if (Process32First(hSnapshot, &pe32)) {
|
if (!Process32First(snapshot.h_snapshot, &pe32))
|
||||||
while (true) {
|
return 0;
|
||||||
if (pe32.th32ProcessID == pid) {
|
|
||||||
parentPid = pe32.th32ParentProcessID;
|
if (pe32.th32ProcessID == pid)
|
||||||
break;
|
return pe32.th32ParentProcessID;
|
||||||
}
|
|
||||||
if (!Process32Next(hSnapshot, &pe32)) {
|
while (Process32Next(snapshot.h_snapshot, &pe32))
|
||||||
break;
|
if (pe32.th32ProcessID == pid)
|
||||||
}
|
return pe32.th32ParentProcessID;
|
||||||
}
|
|
||||||
}
|
return 0;
|
||||||
CloseHandle(hSnapshot);
|
|
||||||
return parentPid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetProcessName(const DWORD pid) -> string {
|
fn GetProcessName(const DWORD pid) -> string {
|
||||||
// ReSharper disable once CppLocalVariableMayBeConst
|
ProcessSnapshot snapshot;
|
||||||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
if (!snapshot.isValid())
|
||||||
if (hSnapshot == INVALID_HANDLE_VALUE)
|
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
PROCESSENTRY32 pe32;
|
PROCESSENTRY32 pe32;
|
||||||
pe32.dwSize = sizeof(PROCESSENTRY32);
|
pe32.dwSize = sizeof(PROCESSENTRY32);
|
||||||
string processName;
|
|
||||||
|
|
||||||
if (Process32First(hSnapshot, &pe32)) {
|
if (!Process32First(snapshot.h_snapshot, &pe32))
|
||||||
while (true) {
|
return "";
|
||||||
if (pe32.th32ProcessID == pid) {
|
|
||||||
// ReSharper disable once CppRedundantCastExpression
|
|
||||||
processName = string(static_cast<const char*>(pe32.szExeFile));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Process32Next(hSnapshot, &pe32))
|
if (pe32.th32ProcessID == pid)
|
||||||
break;
|
return { static_cast<const char*>(pe32.szExeFile) };
|
||||||
}
|
|
||||||
}
|
while (Process32Next(snapshot.h_snapshot, &pe32))
|
||||||
CloseHandle(hSnapshot);
|
if (pe32.th32ProcessID == pid)
|
||||||
return processName;
|
return { static_cast<const char*>(pe32.szExeFile) };
|
||||||
|
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,29 +184,75 @@ fn GetNowPlaying() -> expected<string, NowPlayingError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetOSVersion() -> expected<string, string> {
|
fn GetOSVersion() -> expected<string, string> {
|
||||||
string productName =
|
// First try using the native Windows API
|
||||||
GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "ProductName");
|
OSVERSIONINFOEXW osvi = { sizeof(OSVERSIONINFOEXW), 0, 0, 0, 0, { 0 }, 0, 0, 0, 0, 0 };
|
||||||
|
NTSTATUS status = 0;
|
||||||
|
|
||||||
const string displayVersion =
|
// Get RtlGetVersion function from ntdll.dll (not affected by application manifest)
|
||||||
GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "DisplayVersion");
|
if (HMODULE ntdllHandle = GetModuleHandleW(L"ntdll.dll"))
|
||||||
|
if (const auto rtlGetVersion = std::bit_cast<RtlGetVersionPtr>(GetProcAddress(ntdllHandle, "RtlGetVersion")))
|
||||||
|
status = rtlGetVersion(std::bit_cast<PRTL_OSVERSIONINFOW>(&osvi));
|
||||||
|
|
||||||
const string releaseId =
|
string productName;
|
||||||
GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "ReleaseId");
|
string edition;
|
||||||
|
|
||||||
// Check for Windows 11
|
if (status == 0) { // STATUS_SUCCESS
|
||||||
if (const i32 buildNumber = stoi(
|
// We need to get the edition information which isn't available from version API
|
||||||
GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "CurrentBuildNumber")
|
// Use GetProductInfo which is available since Vista
|
||||||
);
|
DWORD productType = 0;
|
||||||
buildNumber >= 22000 && productName.find("Windows 10") != string::npos)
|
if (GetProductInfo(
|
||||||
productName.replace(productName.find("Windows 10"), 10, "Windows 11");
|
osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor, &productType
|
||||||
|
)) {
|
||||||
|
if (osvi.dwMajorVersion == 10) {
|
||||||
|
if (osvi.dwBuildNumber >= 22000) {
|
||||||
|
productName = "Windows 11";
|
||||||
|
} else {
|
||||||
|
productName = "Windows 10";
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
// Fallback to registry method if the API approach fails
|
||||||
|
productName =
|
||||||
|
GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "ProductName");
|
||||||
|
|
||||||
|
// Check for Windows 11
|
||||||
|
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()) {
|
if (!productName.empty()) {
|
||||||
string result = productName;
|
string result = productName + edition;
|
||||||
|
|
||||||
|
const string displayVersion =
|
||||||
|
GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "DisplayVersion");
|
||||||
|
|
||||||
if (!displayVersion.empty())
|
if (!displayVersion.empty())
|
||||||
result += " " + displayVersion;
|
result += " " + displayVersion;
|
||||||
else if (!releaseId.empty())
|
|
||||||
result += " " + releaseId;
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -201,55 +263,54 @@ fn GetOSVersion() -> expected<string, string> {
|
||||||
fn GetHost() -> string {
|
fn GetHost() -> string {
|
||||||
string hostName = GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SYSTEM\HardwareConfig\Current)", "SystemFamily");
|
string hostName = GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SYSTEM\HardwareConfig\Current)", "SystemFamily");
|
||||||
|
|
||||||
if (hostName.empty())
|
|
||||||
hostName = GetRegistryValue(
|
|
||||||
HKEY_LOCAL_MACHINE, R"(SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName)", "ComputerName"
|
|
||||||
);
|
|
||||||
|
|
||||||
return hostName;
|
return hostName;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetKernelVersion() -> string {
|
fn GetKernelVersion() -> string {
|
||||||
std::stringstream versionStream;
|
|
||||||
|
|
||||||
// ReSharper disable once CppLocalVariableMayBeConst
|
// ReSharper disable once CppLocalVariableMayBeConst
|
||||||
if (HMODULE ntdllHandle = GetModuleHandleW(L"ntdll.dll")) {
|
if (HMODULE ntdllHandle = GetModuleHandleW(L"ntdll.dll")) {
|
||||||
if (const auto rtlGetVersion = std::bit_cast<RtlGetVersionPtr>(GetProcAddress(ntdllHandle, "RtlGetVersion"))) {
|
if (const auto rtlGetVersion = std::bit_cast<RtlGetVersionPtr>(GetProcAddress(ntdllHandle, "RtlGetVersion"))) {
|
||||||
RTL_OSVERSIONINFOW osInfo = {};
|
RTL_OSVERSIONINFOW osInfo = {};
|
||||||
|
|
||||||
osInfo.dwOSVersionInfoSize = sizeof(osInfo);
|
osInfo.dwOSVersionInfoSize = sizeof(osInfo);
|
||||||
|
|
||||||
if (rtlGetVersion(&osInfo) == 0)
|
if (rtlGetVersion(&osInfo) == 0) {
|
||||||
versionStream << osInfo.dwMajorVersion << "." << osInfo.dwMinorVersion << "." << osInfo.dwBuildNumber << "."
|
return std::format(
|
||||||
<< osInfo.dwPlatformId;
|
"{}.{}.{}.{}", osInfo.dwMajorVersion, osInfo.dwMinorVersion, osInfo.dwBuildNumber, osInfo.dwPlatformId
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return versionStream.str();
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetWindowManager() -> string {
|
fn GetWindowManager() -> string {
|
||||||
const std::vector<string> processes = GetRunningProcesses();
|
// Get process information once and reuse it
|
||||||
string windowManager;
|
const auto processInfo = GetProcessInfo();
|
||||||
|
std::vector<string> processNames;
|
||||||
|
|
||||||
// Check for third-party WMs
|
processNames.reserve(processInfo.size());
|
||||||
if (IsProcessRunning(processes, "glazewm.exe"))
|
for (const auto& [pid, name] : processInfo) processNames.push_back(name);
|
||||||
windowManager = "GlazeWM";
|
|
||||||
else if (IsProcessRunning(processes, "fancywm.exe"))
|
|
||||||
windowManager = "FancyWM";
|
|
||||||
else if (IsProcessRunning(processes, "komorebi.exe") || IsProcessRunning(processes, "komorebic.exe"))
|
|
||||||
windowManager = "Komorebi";
|
|
||||||
|
|
||||||
// Fallback to DWM detection
|
// Check for third-party WMs using a map for cleaner code
|
||||||
if (windowManager.empty()) {
|
const std::unordered_map<string, string> wmProcesses = {
|
||||||
BOOL compositionEnabled = FALSE;
|
{ "glazewm.exe", "GlazeWM" },
|
||||||
if (SUCCEEDED(DwmIsCompositionEnabled(&compositionEnabled)))
|
{ "fancywm.exe", "FancyWM" },
|
||||||
windowManager = compositionEnabled ? "DWM" : "Windows Manager (Basic)";
|
{ "komorebi.exe", "Komorebi" },
|
||||||
else
|
{ "komorebic.exe", "Komorebi" }
|
||||||
windowManager = "Windows Manager";
|
};
|
||||||
|
|
||||||
|
for (const auto& [processName, wmName] : wmProcesses) {
|
||||||
|
if (IsProcessRunning(processNames, processName))
|
||||||
|
return wmName;
|
||||||
}
|
}
|
||||||
|
|
||||||
return windowManager;
|
// Fallback to DWM detection
|
||||||
|
BOOL compositionEnabled = FALSE;
|
||||||
|
if (SUCCEEDED(DwmIsCompositionEnabled(&compositionEnabled)))
|
||||||
|
return compositionEnabled ? "DWM" : "Windows Manager (Basic)";
|
||||||
|
|
||||||
|
return "Windows Manager";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetDesktopEnvironment() -> optional<string> {
|
fn GetDesktopEnvironment() -> optional<string> {
|
||||||
|
@ -257,8 +318,6 @@ fn GetDesktopEnvironment() -> optional<string> {
|
||||||
const string buildStr =
|
const string buildStr =
|
||||||
GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "CurrentBuildNumber");
|
GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "CurrentBuildNumber");
|
||||||
|
|
||||||
DEBUG_LOG("buildStr: {}", buildStr);
|
|
||||||
|
|
||||||
if (buildStr.empty()) {
|
if (buildStr.empty()) {
|
||||||
DEBUG_LOG("Failed to get CurrentBuildNumber from registry");
|
DEBUG_LOG("Failed to get CurrentBuildNumber from registry");
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -303,17 +362,29 @@ fn GetDesktopEnvironment() -> optional<string> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetShell() -> string {
|
fn GetShell() -> string {
|
||||||
|
// Define known shells map once for reuse
|
||||||
|
const std::unordered_map<string, string> 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" }
|
||||||
|
};
|
||||||
|
|
||||||
// Detect MSYS2/MinGW shells
|
// Detect MSYS2/MinGW shells
|
||||||
char* msystemEnv = nullptr;
|
char* msystemEnv = nullptr;
|
||||||
if (_dupenv_s(&msystemEnv, nullptr, "MSYSTEM") == 0 && msystemEnv != nullptr) {
|
if (_dupenv_s(&msystemEnv, nullptr, "MSYSTEM") == 0 && msystemEnv != nullptr) {
|
||||||
const std::unique_ptr<char, decltype(&free)> msystemEnvGuard(msystemEnv, free);
|
const std::unique_ptr<char, decltype(&free)> msystemEnvGuard(msystemEnv, free);
|
||||||
char* shell = nullptr;
|
|
||||||
size_t shellLen = 0;
|
// Get shell from environment variables
|
||||||
|
char* shell = nullptr;
|
||||||
|
size_t shellLen = 0;
|
||||||
_dupenv_s(&shell, &shellLen, "SHELL");
|
_dupenv_s(&shell, &shellLen, "SHELL");
|
||||||
const std::unique_ptr<char, decltype(&free)> shellGuard(shell, free);
|
const std::unique_ptr<char, decltype(&free)> shellGuard(shell, free);
|
||||||
string shellExe;
|
string shellExe;
|
||||||
|
|
||||||
// First try SHELL, then LOGINSHELL
|
// If SHELL is empty, try LOGINSHELL
|
||||||
if (!shell || strlen(shell) == 0) {
|
if (!shell || strlen(shell) == 0) {
|
||||||
char* loginShell = nullptr;
|
char* loginShell = nullptr;
|
||||||
size_t loginShellLen = 0;
|
size_t loginShellLen = 0;
|
||||||
|
@ -327,50 +398,49 @@ fn GetShell() -> string {
|
||||||
const size_t lastSlash = shellPath.find_last_of("\\/");
|
const size_t lastSlash = shellPath.find_last_of("\\/");
|
||||||
shellExe = (lastSlash != string::npos) ? shellPath.substr(lastSlash + 1) : shellPath;
|
shellExe = (lastSlash != string::npos) ? shellPath.substr(lastSlash + 1) : shellPath;
|
||||||
std::ranges::transform(shellExe, shellExe.begin(), ::tolower);
|
std::ranges::transform(shellExe, shellExe.begin(), ::tolower);
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to process ancestry if both env vars are missing
|
// Use a map for shell name lookup instead of multiple if statements
|
||||||
if (shellExe.empty()) {
|
const std::unordered_map<string_view, string> shellNames = {
|
||||||
DWORD pid = GetCurrentProcessId();
|
{ "bash", "Bash" },
|
||||||
|
{ "zsh", "Zsh" },
|
||||||
|
{ "fish", "Fish" }
|
||||||
|
};
|
||||||
|
|
||||||
while (pid != 0) {
|
for (const auto& [pattern, name] : shellNames) {
|
||||||
string processName = GetProcessName(pid);
|
if (shellExe.find(pattern) != string::npos)
|
||||||
std::ranges::transform(processName, processName.begin(), [](const u8 character) {
|
|
||||||
return static_cast<char>(std::tolower(character));
|
|
||||||
});
|
|
||||||
|
|
||||||
if (processName == "bash.exe" || processName == "zsh.exe" || processName == "fish.exe" ||
|
|
||||||
processName == "mintty.exe") {
|
|
||||||
string name = processName.substr(0, processName.find(".exe"));
|
|
||||||
if (!name.empty())
|
|
||||||
name[0] = static_cast<char>(std::toupper(static_cast<unsigned char>(name[0]))); // Capitalize first letter
|
|
||||||
return name;
|
return name;
|
||||||
}
|
|
||||||
pid = GetParentProcessId(pid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "MSYS2";
|
return shellExe.empty() ? "MSYS2" : "MSYS2/" + shellExe;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shellExe.find("bash") != string::npos)
|
// Fallback to process ancestry with cached process info
|
||||||
return "Bash";
|
const auto processInfo = GetProcessInfo();
|
||||||
if (shellExe.find("zsh") != string::npos)
|
DWORD pid = GetCurrentProcessId();
|
||||||
return "Zsh";
|
|
||||||
if (shellExe.find("fish") != string::npos)
|
while (pid != 0) {
|
||||||
return "Fish";
|
string processName = GetProcessName(pid);
|
||||||
return shellExe.empty() ? "MSYS2" : "MSYS2/" + shellExe;
|
std::ranges::transform(processName, processName.begin(), ::tolower);
|
||||||
|
|
||||||
|
const std::unordered_map<string, string> 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";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect Windows shells
|
// Detect Windows shells
|
||||||
const std::unordered_map<string, string> 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" }
|
|
||||||
};
|
|
||||||
|
|
||||||
DWORD pid = GetCurrentProcessId();
|
DWORD pid = GetCurrentProcessId();
|
||||||
while (pid != 0) {
|
while (pid != 0) {
|
||||||
string processName = GetProcessName(pid);
|
string processName = GetProcessName(pid);
|
||||||
|
@ -394,5 +464,4 @@ fn GetDiskUsage() -> std::pair<u64, u64> {
|
||||||
return { 0, 0 };
|
return { 0, 0 };
|
||||||
}
|
}
|
||||||
// NOLINTEND(*-pro-type-cstyle-cast,*-no-int-to-ptr)
|
// NOLINTEND(*-pro-type-cstyle-cast,*-no-int-to-ptr)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Reference in a new issue