diff --git a/.gitignore b/.gitignore index 9e926f1..f6a4d28 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ config.toml draconis++ Makefile result +/include/ diff --git a/meson-vcpkg.txt b/meson-vcpkg.txt new file mode 100644 index 0000000..e69de29 diff --git a/meson.build b/meson.build index e1bd376..2f41dc5 100644 --- a/meson.build +++ b/meson.build @@ -1,9 +1,7 @@ project( 'draconis++', 'cpp', - version: '0.1.0', - default_options: [ - 'objc_std=c++20', - 'objcpp_std=c++20', + version : '0.1.0', + default_options : [ 'cpp_std=c++20', 'default_library=static', 'warning_level=everything', @@ -11,17 +9,11 @@ project( ] ) -clangtidy = find_program('clang-tidy', required: false) - cpp = meson.get_compiler('cpp') -if host_machine.system() == 'darwin' - add_languages('objcpp') - - objcpp = meson.get_compiler('objcpp') - - add_project_arguments( - objcpp.get_supported_arguments([ +add_project_arguments( + cpp.get_supported_arguments( + [ '-Wno-c++20-compat', '-Wno-c++20-extensions', '-Wno-c++98-compat', @@ -32,79 +24,36 @@ if host_machine.system() == 'darwin' '-Wno-pre-c++20-compat-pedantic', '-Wno-switch-default', '-Wunused-function', - ]), - language: 'objcpp' - ) -endif - -add_project_arguments( - cpp.get_supported_arguments([ - '-Wno-c++20-compat', - '-Wno-c++20-extensions', - '-Wno-c++98-compat', - '-Wno-c++98-compat-pedantic', - '-Wno-disabled-macro-expansion', - '-Wno-missing-prototypes', - '-Wno-padded', - '-Wno-pre-c++20-compat-pedantic', - '-Wno-switch-default', - '-Wunused-function', - ]), - language: 'cpp' + ] + ), + language : 'cpp' ) source_file_names = [ 'src/main.cpp', 'src/config/config.cpp', - 'src/config/weather.cpp' + 'src/config/weather.cpp', + 'src/os/windows.cpp' ] -if host_machine.system() == 'linux' - source_file_names += ['src/os/linux.cpp'] -endif - -if host_machine.system() == 'darwin' - source_file_names += [ - 'src/os/macos.cpp', - 'src/os/macos/bridge.mm', - ] -endif - sources = [] - foreach file : source_file_names sources += files(file) endforeach +windows_sdk_lib_dir = 'C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/x64' +link_args = ['-L' + windows_sdk_lib_dir, '-lwindowsapp', '-lcurl'] + deps = [] -deps += dependency('fmt') -deps += dependency('libcurl') -deps += dependency('tomlplusplus') -deps += cpp.find_library('yyjson') -deps += cpp.find_library('reflectcpp') - -if host_machine.system() == 'darwin' - deps += dependency('Foundation') - deps += dependency('MediaPlayer') -endif - -if host_machine.system() == 'linux' - deps += cpp.find_library('sdbus-c++') -endif - -objc_args = [] -link_args = [] - -if host_machine.system() == 'darwin' - objc_args += ['-fobjc-arc'] - link_args += ['-framework', 'Foundation', '-framework', 'MediaPlayer'] -endif +deps += dependency('fmt', static : true) +deps += dependency('libcurl', static : true) +deps += dependency('tomlplusplus', static : true) +deps += dependency('yyjson', static : true) executable( 'draconis++', sources, - objc_args: objc_args, - link_args: link_args, - dependencies: deps -) + dependencies : deps, + link_args : link_args +) \ No newline at end of file diff --git a/src/config/weather.cpp b/src/config/weather.cpp index e4da179..d10f6a2 100644 --- a/src/config/weather.cpp +++ b/src/config/weather.cpp @@ -8,7 +8,13 @@ // Function to read cache from file fn ReadCacheFromFile() -> Result<WeatherOutput> { +#ifdef __WIN32__ + const char* tempPath = getenv("TEMP"); + const string path = string(tempPath) + "\\weather_cache.json"; + std::ifstream ifs(path); +#else std::ifstream ifs("/tmp/weather_cache.json"); +#endif if (!ifs.is_open()) return Error("Cache file not found."); @@ -34,7 +40,13 @@ fn ReadCacheFromFile() -> Result<WeatherOutput> { fn WriteCacheToFile(const WeatherOutput& data) -> Result<> { fmt::println("Writing to cache file..."); +#ifdef __WIN32__ + const char* tempPath = getenv("TEMP"); + const string path = string(tempPath) + "\\weather_cache.json"; + std::ofstream ofs(path); +#else std::ofstream ofs("/tmp/weather_cache.json"); +#endif if (!ofs.is_open()) return Error("Failed to open cache file for writing."); diff --git a/src/main.cpp b/src/main.cpp index 72b1c49..075c60a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,12 +60,26 @@ fn main() -> i32 { future<string> dateFuture = std::async(std::launch::async, GetDate); future<u64> memInfoFuture = std::async(std::launch::async, GetMemInfo); - const WeatherOutput json = weatherFuture.get(); - const i64 temp = std::lround(json.main.temp); - const string townName = json.name; + const auto + [clouds, + timezone, + visibility, + main, + coords, + rain, + snow, + base, + townName, + weather, + sys, + cod, + dt, + id, + wind] = weatherFuture.get(); + const i64 temp = std::lround(main.temp); const bool nowPlayingEnabled = nowPlayingEnabledFuture.get(); - const char* version = osVersionFuture.get(); + const string version = osVersionFuture.get(); const string date = dateFuture.get(); const string name = config.general.get().name.get(); const u64 mem = memInfoFuture.get(); @@ -79,6 +93,5 @@ fn main() -> i32 { if (nowPlayingEnabled) fmt::println("{}", GetNowPlaying()); - delete[] version; delete &config; } diff --git a/src/os/linux.cpp b/src/os/linux.cpp index 48e90d2..e21dfb2 100644 --- a/src/os/linux.cpp +++ b/src/os/linux.cpp @@ -39,12 +39,12 @@ fn MeminfoParse(std::ifstream input) -> u64 { fn GetMemInfo() -> u64 { return MeminfoParse(std::ifstream("/proc/meminfo")) * 1024; } -fn GetOSVersion() -> const char* { +fn GetOSVersion() -> string { std::ifstream file("/etc/os-release"); if (!file.is_open()) { std::cerr << "Failed to open /etc/os-release" << std::endl; - return nullptr; + return ""; // Return empty string indicating failure } string line; @@ -58,15 +58,11 @@ fn GetOSVersion() -> const char* { if (!prettyName.empty() && prettyName.front() == '"' && prettyName.back() == '"') prettyName = prettyName.substr(1, prettyName.size() - 2); - // Allocate memory for the C-string and copy the content - char* cstr = new char[prettyName.size() + 1]; - std::strcpy(cstr, prettyName.c_str()); - - return cstr; + return prettyName; } } - return nullptr; + return ""; // Return empty string if PRETTY_NAME= line not found } fn GetMprisPlayers(sdbus::IConnection& connection) -> std::vector<string> { diff --git a/src/os/os.h b/src/os/os.h index 2d96984..076dfc1 100644 --- a/src/os/os.h +++ b/src/os/os.h @@ -16,4 +16,4 @@ fn GetNowPlaying() -> string; /** * @brief Get the OS version. */ -fn GetOSVersion() -> const char*; +fn GetOSVersion() -> string; diff --git a/src/os/windows.cpp b/src/os/windows.cpp new file mode 100644 index 0000000..6419cb3 --- /dev/null +++ b/src/os/windows.cpp @@ -0,0 +1,107 @@ +#include <windows.h> +#include <winrt/Windows.Foundation.h> // ReSharper disable once CppUnusedIncludeDirective +#include <winrt/Windows.Media.Control.h> +#include <winrt/base.h> +#include <winrt/impl/Windows.Media.Control.2.h> + +#include "os.h" + +fn GetMemInfo()->u64 { + u64 mem = 0; + GetPhysicallyInstalledSystemMemory(&mem); + return mem * 1024; +} + +fn GetNowPlaying()->string { + using namespace winrt::Windows::Media::Control; + using namespace winrt::Windows::Foundation; + + using MediaProperties = GlobalSystemMediaTransportControlsSessionMediaProperties; + using Session = GlobalSystemMediaTransportControlsSession; + using SessionManager = GlobalSystemMediaTransportControlsSessionManager; + + try { + // Request the session manager asynchronously + const IAsyncOperation<SessionManager> sessionManagerOp = SessionManager::RequestAsync(); + const SessionManager sessionManager = sessionManagerOp.get(); + + if (const Session currentSession = sessionManager.GetCurrentSession()) { + // Try to get the media properties asynchronously + const IAsyncOperation<MediaProperties> mediaPropertiesOp = + currentSession.TryGetMediaPropertiesAsync(); + const MediaProperties mediaProperties = mediaPropertiesOp.get(); + + // Convert the hstring title to string + return to_string(mediaProperties.Title()); + } + + // If we reach this point, there is no current session + return "No current media session."; + } catch (...) { return "Failed to get media properties."; } +} + +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; + if (RegQueryValueExA(key, valueName.c_str(), nullptr, nullptr, nullptr, &dataSize) != ERROR_SUCCESS) { + RegCloseKey(key); + return ""; + } + + string value(dataSize, '\0'); + if (RegQueryValueExA( + key, + valueName.c_str(), + nullptr, + nullptr, + reinterpret_cast<LPBYTE>(value.data()), // NOLINT(*-reinterpret-cast) + &dataSize + ) != ERROR_SUCCESS) { + RegCloseKey(key); + return ""; + } + + RegCloseKey(key); + // Remove null terminator if present + if (!value.empty() && value.back() == '\0') + value.pop_back(); + + return value; +} + +fn GetOSVersion()->string { + string productName = GetRegistryValue( + HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "ProductName" + ); + + const string displayVersion = GetRegistryValue( + HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "DisplayVersion" + ); + + const string releaseId = GetRegistryValue( + HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "ReleaseId" + ); + + // 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()) { + string result = productName; + + if (!displayVersion.empty()) + result += " " + displayVersion; + else if (!releaseId.empty()) + result += " " + releaseId; + + return result; + } + + return ""; +}