From 203d56e06b7fb2987c129d976c503d177f7fff77 Mon Sep 17 00:00:00 2001 From: Mars Date: Mon, 21 Apr 2025 01:42:24 -0400 Subject: [PATCH] some more updates and stuff --- flake.lock | 12 ++-- flake.nix | 11 ++-- meson.build | 2 +- src/config/config.cpp | 22 +------ src/config/config.h | 22 +++---- src/config/weather.cpp | 34 +++++----- src/config/weather.h | 4 +- src/main.cpp | 141 +++++++++++++++++++++++------------------ src/os/freebsd.cpp | 28 ++++---- src/os/linux.cpp | 5 +- src/os/macos.cpp | 32 +++++----- src/os/macos/bridge.h | 7 +- src/os/macos/bridge.mm | 53 +++++++++------- src/os/os.h | 16 ++--- src/util/macros.h | 71 ++++++++++----------- src/util/types.h | 13 ++-- subprojects/fmt.wrap | 13 ---- 17 files changed, 231 insertions(+), 255 deletions(-) delete mode 100644 subprojects/fmt.wrap diff --git a/flake.lock b/flake.lock index 33ac666..5f462e4 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1744536153, - "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", + "lastModified": 1744868846, + "narHash": "sha256-5RJTdUHDmj12Qsv7XOhuospjAjATNiTMElplWnJE9Hs=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11", + "rev": "ebe4301cbd8f81c4f8d3244b3632338bbeb6d49c", "type": "github" }, "original": { @@ -59,11 +59,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1743748085, - "narHash": "sha256-uhjnlaVTWo5iD3LXics1rp9gaKgDRQj6660+gbUU3cE=", + "lastModified": 1744961264, + "narHash": "sha256-aRmUh0AMwcbdjJHnytg1e5h5ECcaWtIFQa6d9gI85AI=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "815e4121d6a5d504c0f96e5be2dd7f871e4fd99d", + "rev": "8d404a69efe76146368885110f29a2ca3700bee6", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 8d77be1..0e2eb79 100644 --- a/flake.nix +++ b/flake.nix @@ -18,10 +18,7 @@ system: let pkgs = import nixpkgs {inherit system;}; - llvmPackages = with pkgs; - if hostPlatform.isLinux - then llvmPackages_20 - else llvmPackages_19; + llvmPackages = pkgs.llvmPackages_20; stdenv = with pkgs; ( @@ -38,18 +35,20 @@ ++ (with pkgsStatic; [ curl ftxui - libiconv - sqlitecpp tomlplusplus ]) + ++ darwinPkgs ++ linuxPkgs; + darwinPkgs = nixpkgs.lib.optionals stdenv.isDarwin (with pkgs.pkgsStatic; [libiconv]); + linuxPkgs = nixpkgs.lib.optionals stdenv.isLinux (with pkgs; [ valgrind ] ++ (with pkgsStatic; [ dbus + sqlitecpp xorg.libX11 wayland ])); diff --git a/meson.build b/meson.build index 63aae2d..f3f3fa8 100644 --- a/meson.build +++ b/meson.build @@ -58,7 +58,7 @@ if host_system == 'darwin' objcpp = meson.get_compiler('objcpp') objcpp_flags = common_warning_flags + [ '-Wno-switch-default', - '-std=c++2b', + '-std=c++23', '-fvisibility=hidden', '-fvisibility-inlines-hidden', ] diff --git a/src/config/config.cpp b/src/config/config.cpp index c2862c6..bc0b304 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -14,24 +14,19 @@ namespace { std::vector possiblePaths; #ifdef _WIN32 - // Windows possible paths in order of preference if (auto result = GetEnv("LOCALAPPDATA"); result) possiblePaths.push_back(fs::path(*result) / "draconis++" / "config.toml"); if (auto result = GetEnv("USERPROFILE"); result) { - // Support for .config style on Windows (some users prefer this) possiblePaths.push_back(fs::path(*result) / ".config" / "draconis++" / "config.toml"); - // Traditional Windows location alternative possiblePaths.push_back(fs::path(*result) / "AppData" / "Local" / "draconis++" / "config.toml"); } if (auto result = GetEnv("APPDATA"); result) possiblePaths.push_back(fs::path(*result) / "draconis++" / "config.toml"); - // Portable app option - config in same directory as executable possiblePaths.push_back(fs::path(".") / "config.toml"); #else - // Unix/Linux paths in order of preference if (auto result = GetEnv("XDG_CONFIG_HOME"); result) possiblePaths.emplace_back(fs::path(*result) / "draconis++" / "config.toml"); @@ -40,18 +35,14 @@ namespace { possiblePaths.emplace_back(fs::path(*result) / ".draconis++" / "config.toml"); } - // System-wide config possiblePaths.emplace_back("/etc/draconis++/config.toml"); #endif - // Check if any of these configs already exist for (const auto& path : possiblePaths) if (std::error_code errc; exists(path, errc) && !errc) return path; - // If no config exists yet, return the default (first in priority) if (!possiblePaths.empty()) { - // Create directory structure for the default path const fs::path defaultDir = possiblePaths[0].parent_path(); if (std::error_code errc; !exists(defaultDir, errc) && !errc) { @@ -63,13 +54,11 @@ namespace { return possiblePaths[0]; } - // Ultimate fallback if somehow we have no paths throw std::runtime_error("Could not determine a valid config path"); } fn CreateDefaultConfig(const fs::path& configPath) -> bool { try { - // Ensure the directory exists std::error_code errc; create_directories(configPath.parent_path(), errc); if (errc) { @@ -77,11 +66,9 @@ namespace { return false; } - // Create a default TOML document toml::table root; - // Get default username for General section - std::string defaultName; + String defaultName; #ifdef _WIN32 std::array username; DWORD size = sizeof(username); @@ -95,15 +82,12 @@ namespace { defaultName = "User"; #endif - // General section toml::table* general = root.insert("general", toml::table {}).first->second.as_table(); general->insert("name", defaultName); - // Now Playing section toml::table* nowPlaying = root.insert("now_playing", toml::table {}).first->second.as_table(); nowPlaying->insert("enabled", false); - // Weather section toml::table* weather = root.insert("weather", toml::table {}).first->second.as_table(); weather->insert("enabled", false); weather->insert("show_town_name", false); @@ -111,7 +95,6 @@ namespace { weather->insert("units", "metric"); weather->insert("location", "London"); - // Write to file (using a stringstream for comments + TOML) std::ofstream file(configPath); if (!file) { ERROR_LOG("Failed to open config file for writing: {}", configPath.string()); @@ -154,18 +137,15 @@ fn Config::getInstance() -> Config { try { const fs::path configPath = GetConfigPath(); - // Check if the config file exists if (!exists(configPath)) { INFO_LOG("Config file not found, creating defaults at {}", configPath.string()); - // Create default config if (!CreateDefaultConfig(configPath)) { WARN_LOG("Failed to create default config, using in-memory defaults"); return {}; } } - // Now we should have a config file to read const toml::parse_result config = toml::parse_file(configPath.string()); return fromToml(config); } catch (const std::exception& e) { diff --git a/src/config/config.h b/src/config/config.h index 6826edb..552abe9 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -3,8 +3,8 @@ #ifdef _WIN32 #include #else -#include // For getpwuid -#include // For getuid +#include +#include #endif #include @@ -12,10 +12,10 @@ #include "src/util/macros.h" #include "weather.h" -using Location = std::variant; +using Location = std::variant; struct General { - string name = []() -> string { + String name = []() -> String { #ifdef _WIN32 std::array username; DWORD size = sizeof(username); @@ -34,7 +34,7 @@ struct General { static fn fromToml(const toml::table& tbl) -> General { General gen; - if (const std::optional name = tbl["name"].value()) + if (const std::optional name = tbl["name"].value()) gen.name = *name; return gen; @@ -55,15 +55,15 @@ struct Weather { bool enabled = false; bool show_town_name = false; Location location; - string api_key; - string units; + String api_key; + String units; static fn fromToml(const toml::table& tbl) -> Weather { Weather weather; weather.enabled = tbl["enabled"].value_or(false); - if (auto apiKey = tbl["api_key"].value()) { - const string& keyVal = apiKey.value(); + if (auto apiKey = tbl["api_key"].value()) { + const String& keyVal = apiKey.value(); if (keyVal.empty()) weather.enabled = false; @@ -77,11 +77,11 @@ struct Weather { return weather; weather.show_town_name = tbl["show_town_name"].value_or(false); - weather.units = tbl["units"].value().value_or("metric"); + weather.units = tbl["units"].value().value_or("metric"); if (const toml::node_view location = tbl["location"]) { if (location.is_string()) { - weather.location = location.value().value(); + weather.location = location.value().value(); } else if (location.is_table()) { const auto* coord = location.as_table(); Coords coords; diff --git a/src/config/weather.cpp b/src/config/weather.cpp index 07124e9..d9d396d 100644 --- a/src/config/weather.cpp +++ b/src/config/weather.cpp @@ -15,7 +15,7 @@ using namespace std::string_literals; namespace { constexpr glz::opts glaze_opts = { .error_on_unknown_keys = false }; - fn GetCachePath() -> std::expected { + fn GetCachePath() -> std::expected { std::error_code errc; fs::path cachePath = fs::temp_directory_path(errc); @@ -26,8 +26,8 @@ namespace { return cachePath; } - fn ReadCacheFromFile() -> std::expected { - std::expected cachePath = GetCachePath(); + fn ReadCacheFromFile() -> std::expected { + std::expected cachePath = GetCachePath(); if (!cachePath) return std::unexpected(cachePath.error()); @@ -40,7 +40,7 @@ namespace { DEBUG_LOG("Reading from cache file..."); try { - const string content((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + const String content((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); WeatherOutput result; if (const glz::error_ctx errc = glz::read(result, content); errc.ec != glz::error_code::none) @@ -51,8 +51,8 @@ namespace { } catch (const std::exception& e) { return std::unexpected("Error reading cache: "s + e.what()); } } - fn WriteCacheToFile(const WeatherOutput& data) -> std::expected { - std::expected cachePath = GetCachePath(); + fn WriteCacheToFile(const WeatherOutput& data) -> std::expected { + std::expected cachePath = GetCachePath(); if (!cachePath) return std::unexpected(cachePath.error()); @@ -67,7 +67,7 @@ namespace { if (!ofs.is_open()) return std::unexpected("Failed to open temp file: " + tempPath.string()); - string jsonStr; + String jsonStr; if (const glz::error_ctx errc = glz::write_json(data, jsonStr); errc.ec != glz::error_code::none) return std::unexpected("JSON serialization error: " + glz::format_error(errc, jsonStr)); @@ -89,16 +89,16 @@ namespace { } catch (const std::exception& e) { return std::unexpected("File operation error: "s + e.what()); } } - fn WriteCallback(void* contents, const size_t size, const size_t nmemb, string* str) -> size_t { + fn WriteCallback(void* contents, const size_t size, const size_t nmemb, String* str) -> size_t { const size_t totalSize = size * nmemb; str->append(static_cast(contents), totalSize); return totalSize; } - fn MakeApiRequest(const string& url) -> std::expected { + fn MakeApiRequest(const String& url) -> std::expected { DEBUG_LOG("Making API request to URL: {}", url); CURL* curl = curl_easy_init(); - string responseBuffer; + String responseBuffer; if (!curl) return std::unexpected("Failed to initialize cURL"); @@ -127,7 +127,7 @@ namespace { fn Weather::getWeatherInfo() const -> WeatherOutput { using namespace std::chrono; - if (std::expected data = ReadCacheFromFile()) { + if (std::expected data = ReadCacheFromFile()) { const WeatherOutput& dataVal = *data; if (const duration cacheAge = system_clock::now() - system_clock::time_point(seconds(dataVal.dt)); @@ -141,24 +141,24 @@ fn Weather::getWeatherInfo() const -> WeatherOutput { DEBUG_LOG("Cache error: {}", data.error()); } - fn handleApiResult = [](const std::expected& result) -> WeatherOutput { + fn handleApiResult = [](const std::expected& result) -> WeatherOutput { if (!result) { ERROR_LOG("API request failed: {}", result.error()); return WeatherOutput {}; } - if (std::expected writeResult = WriteCacheToFile(*result); !writeResult) + if (std::expected writeResult = WriteCacheToFile(*result); !writeResult) ERROR_LOG("Failed to write cache: {}", writeResult.error()); return *result; }; - if (std::holds_alternative(location)) { - const auto& city = std::get(location); + if (std::holds_alternative(location)) { + const auto& city = std::get(location); char* escaped = curl_easy_escape(nullptr, city.c_str(), static_cast(city.length())); DEBUG_LOG("Requesting city: {}", escaped); - const string apiUrl = + const String apiUrl = std::format("https://api.openweathermap.org/data/2.5/weather?q={}&appid={}&units={}", escaped, api_key, units); curl_free(escaped); @@ -168,7 +168,7 @@ fn Weather::getWeatherInfo() const -> WeatherOutput { const auto& [lat, lon] = std::get(location); DEBUG_LOG("Requesting coordinates: lat={:.3f}, lon={:.3f}", lat, lon); - const string apiUrl = std::format( + const String apiUrl = std::format( "https://api.openweathermap.org/data/2.5/weather?lat={:.3f}&lon={:.3f}&appid={}&units={}", lat, lon, api_key, units ); diff --git a/src/config/weather.h b/src/config/weather.h index 459ecbe..d887bd7 100644 --- a/src/config/weather.h +++ b/src/config/weather.h @@ -6,7 +6,7 @@ // NOLINTBEGIN(readability-identifier-naming) struct Condition { - string description; + String description; struct glaze { using T = Condition; @@ -30,7 +30,7 @@ struct Coords { struct WeatherOutput { Main main; - string name; + String name; std::vector weather; usize dt; diff --git a/src/main.cpp b/src/main.cpp index 0025261..44a1a92 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -101,19 +101,19 @@ namespace ui { namespace { template - auto expected_visit(const std::expected& exp, ValueFunc value_func, ErrorFunc error_func) { + fn expected_visit(const std::expected& exp, ValueFunc value_func, ErrorFunc error_func) { if (exp.has_value()) return value_func(*exp); return error_func(exp.error()); } - fn GetDate() -> string { + fn GetDate() -> String { using namespace std::chrono; const year_month_day ymd = year_month_day { floor(system_clock::now()) }; - string month = std::format("{:%B}", ymd); + String month = std::format("{:%B}", ymd); u32 day = static_cast(ymd.day()); @@ -129,18 +129,18 @@ namespace { } struct SystemData { - std::string date; - std::string host; - std::string kernel_version; - std::expected os_version; - std::expected mem_info; - std::optional desktop_environment; - std::string window_manager; - std::optional> now_playing; + String date; + String host; + String kernel_version; + std::expected os_version; + std::expected mem_info; + std::optional desktop_environment; + String window_manager; + std::optional> now_playing; std::optional weather_info; u64 disk_used; u64 disk_total; - std::string shell; + String shell; static fn fetchSystemData(const Config& config) -> SystemData { SystemData data; @@ -164,7 +164,7 @@ namespace { // Conditional tasks std::future weather; - std::future> nowPlaying; + std::future> nowPlaying; if (config.weather.enabled) weather = std::async(std::launch::async, [&config] { return config.weather.getWeatherInfo(); }); @@ -200,7 +200,7 @@ namespace { fn SystemInfoBox(const Config& config, const SystemData& data) -> Element { // Fetch data - const string& name = config.general.name; + const String& name = config.general.name; const Weather weather = config.weather; const bool nowPlayingEnabled = config.now_playing.enabled; @@ -209,23 +209,27 @@ namespace { Elements content; - content.push_back(text(string(userIcon) + "Hello " + name + "! ") | bold | color(Color::Cyan)); + content.push_back(text(String(userIcon) + "Hello " + name + "! ") | bold | color(Color::Cyan)); content.push_back(separator() | color(ui::DEFAULT_THEME.border)); - content.push_back(hbox({ - text(string(paletteIcon)) | color(ui::DEFAULT_THEME.icon), - CreateColorCircles(), - })); + content.push_back(hbox( + { + text(String(paletteIcon)) | color(ui::DEFAULT_THEME.icon), + CreateColorCircles(), + } + )); content.push_back(separator() | color(ui::DEFAULT_THEME.border)); // Helper function for aligned rows fn createRow = [&](const auto& icon, const auto& label, const auto& value) { - return hbox({ - text(string(icon)) | color(ui::DEFAULT_THEME.icon), - text(string(static_cast(label))) | color(ui::DEFAULT_THEME.label), - filler(), - text(string(value)) | color(ui::DEFAULT_THEME.value), - text(" "), - }); + return hbox( + { + text(String(icon)) | color(ui::DEFAULT_THEME.icon), + text(String(static_cast(label))) | color(ui::DEFAULT_THEME.label), + filler(), + text(String(value)) | color(ui::DEFAULT_THEME.value), + text(" "), + } + ); }; // System info rows @@ -236,31 +240,39 @@ namespace { const WeatherOutput& weatherInfo = data.weather_info.value(); if (weather.show_town_name) - content.push_back(hbox({ - text(string(weatherIcon)) | color(ui::DEFAULT_THEME.icon), - text("Weather") | color(ui::DEFAULT_THEME.label), - filler(), + content.push_back(hbox( + { + text(String(weatherIcon)) | color(ui::DEFAULT_THEME.icon), + text("Weather") | color(ui::DEFAULT_THEME.label), + filler(), - hbox({ - text(std::format("{}°F ", std::lround(weatherInfo.main.temp))), - text("in "), - text(weatherInfo.name), - text(" "), - }) | - color(ui::DEFAULT_THEME.value), - })); + hbox( + { + text(std::format("{}°F ", std::lround(weatherInfo.main.temp))), + text("in "), + text(weatherInfo.name), + text(" "), + } + ) | + color(ui::DEFAULT_THEME.value), + } + )); else - content.push_back(hbox({ - text(string(weatherIcon)) | color(ui::DEFAULT_THEME.icon), - text("Weather") | color(ui::DEFAULT_THEME.label), - filler(), + content.push_back(hbox( + { + text(String(weatherIcon)) | color(ui::DEFAULT_THEME.icon), + text("Weather") | color(ui::DEFAULT_THEME.label), + filler(), - hbox({ - text(std::format("{}°F, {}", std::lround(weatherInfo.main.temp), weatherInfo.weather[0].description)), - text(" "), - }) | - color(ui::DEFAULT_THEME.value), - })); + hbox( + { + text(std::format("{}°F, {}", std::lround(weatherInfo.main.temp), weatherInfo.weather[0].description)), + text(" "), + } + ) | + color(ui::DEFAULT_THEME.value), + } + )); } content.push_back(separator() | color(ui::DEFAULT_THEME.border)); @@ -273,14 +285,14 @@ namespace { expected_visit( data.os_version, - [&](const string& version) { content.push_back(createRow(string(osIcon), "OS", version)); }, - [](const string& error) { ERROR_LOG("Failed to get OS version: {}", error); } + [&](const String& version) { content.push_back(createRow(String(osIcon), "OS", version)); }, + [](const String& error) { ERROR_LOG("Failed to get OS version: {}", error); } ); expected_visit( data.mem_info, [&](const u64& mem) { content.push_back(createRow(memoryIcon, "RAM", std::format("{}", BytesToGiB { mem }))); }, - [](const string& error) { ERROR_LOG("Failed to get memory info: {}", error); } + [](const String& error) { ERROR_LOG("Failed to get memory info: {}", error); } ); // Add Disk usage row @@ -300,19 +312,21 @@ namespace { // Now Playing row if (nowPlayingEnabled && data.now_playing.has_value()) { - if (const std::expected& nowPlayingResult = *data.now_playing; + if (const std::expected& nowPlayingResult = *data.now_playing; nowPlayingResult.has_value()) { - const std::string& npText = *nowPlayingResult; + const String& npText = *nowPlayingResult; content.push_back(separator() | color(ui::DEFAULT_THEME.border)); - content.push_back(hbox({ - text(string(musicIcon)) | color(ui::DEFAULT_THEME.icon), - text("Playing") | color(ui::DEFAULT_THEME.label), - text(" "), - filler(), - paragraph(npText) | color(Color::Magenta) | size(WIDTH, LESS_THAN, ui::MAX_PARAGRAPH_LENGTH), - text(" "), - })); + content.push_back(hbox( + { + text(String(musicIcon)) | color(ui::DEFAULT_THEME.icon), + text("Playing") | color(ui::DEFAULT_THEME.label), + text(" "), + filler(), + paragraph(npText) | color(Color::Magenta) | size(WIDTH, LESS_THAN, ui::MAX_PARAGRAPH_LENGTH), + text(" "), + } + )); } else { const NowPlayingError& error = nowPlayingResult.error(); @@ -340,6 +354,11 @@ namespace { if (std::holds_alternative(error)) DEBUG_LOG("WinRT error: {}", to_string(std::get(error).message())); #endif + +#ifdef __APPLE__ + if (std::holds_alternative(error)) + DEBUG_LOG("CoreAudio error: {}", std::get(error)); +#endif } } diff --git a/src/os/freebsd.cpp b/src/os/freebsd.cpp index f5fdb2b..5facc38 100644 --- a/src/os/freebsd.cpp +++ b/src/os/freebsd.cpp @@ -1,6 +1,5 @@ #ifdef __FreeBSD__ -#include #include #include #include @@ -48,8 +47,7 @@ fn GetMprisPlayers(sdbus::IConnection& connection) -> std::vector { const sdbus::ObjectPath dbusObjectPath = sdbus::ObjectPath("/org/freedesktop/DBus"); const char* dbusMethodListNames = "ListNames"; - const std::unique_ptr dbusProxy = - createProxy(connection, dbusInterface, dbusObjectPath); + const std::unique_ptr dbusProxy = createProxy(connection, dbusInterface, dbusObjectPath); std::vector names; @@ -58,8 +56,7 @@ fn GetMprisPlayers(sdbus::IConnection& connection) -> std::vector { std::vector mprisPlayers; for (const std::basic_string& name : names) - if (const char* mprisInterfaceName = "org.mpris.MediaPlayer2"; - name.find(mprisInterfaceName) != std::string::npos) + if (const char* mprisInterfaceName = "org.mpris.MediaPlayer2"; name.find(mprisInterfaceName) != String::npos) mprisPlayers.push_back(name); return mprisPlayers; @@ -74,8 +71,7 @@ fn GetActivePlayer(const std::vector& mprisPlayers) -> string { fn GetNowPlaying() -> string { try { - const char *playerObjectPath = "/org/mpris/MediaPlayer2", - *playerInterfaceName = "org.mpris.MediaPlayer2.Player"; + const char *playerObjectPath = "/org/mpris/MediaPlayer2", *playerInterfaceName = "org.mpris.MediaPlayer2.Player"; std::unique_ptr connection = sdbus::createSessionBusConnection(); @@ -89,24 +85,22 @@ fn GetNowPlaying() -> string { if (activePlayer.empty()) return ""; - auto playerProxy = sdbus::createProxy( - *connection, sdbus::ServiceName(activePlayer), sdbus::ObjectPath(playerObjectPath) - ); + auto playerProxy = + sdbus::createProxy(*connection, sdbus::ServiceName(activePlayer), sdbus::ObjectPath(playerObjectPath)); - sdbus::Variant metadataVariant = - playerProxy->getProperty("Metadata").onInterface(playerInterfaceName); + sdbus::Variant metadataVariant = playerProxy->getProperty("Metadata").onInterface(playerInterfaceName); - if (metadataVariant.containsValueOfType>()) { - const auto& metadata = metadataVariant.get>(); + if (metadataVariant.containsValueOfType>()) { + const auto& metadata = metadataVariant.get>(); auto iter = metadata.find("xesam:title"); - if (iter != metadata.end() && iter->second.containsValueOfType()) - return iter->second.get(); + if (iter != metadata.end() && iter->second.containsValueOfType()) + return iter->second.get(); } } catch (const sdbus::Error& e) { if (e.getName() != "com.github.altdesktop.playerctld.NoActivePlayer") - return fmt::format("Error: {}", e.what()); + return std::format("Error: {}", e.what()); return "No active player"; } diff --git a/src/os/linux.cpp b/src/os/linux.cpp index 6ac1b29..8517210 100644 --- a/src/os/linux.cpp +++ b/src/os/linux.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -24,7 +23,6 @@ #include "os.h" #include "src/util/macros.h" -// Minimal global using declarations needed for function signatures using std::expected; using std::optional; @@ -32,7 +30,6 @@ namespace fs = std::filesystem; using namespace std::literals::string_view_literals; namespace { - // Local using declarations for the anonymous namespace using std::array; using std::bit_cast; using std::getenv; @@ -121,7 +118,7 @@ namespace { XCloseDisplay(display); - return "Unknown (X11)"; // Changed to empty string + return "Unknown (X11)"; } fn TrimHyprlandWrapper(const string& input) -> string { diff --git a/src/os/macos.cpp b/src/os/macos.cpp index 0f54033..7be71e8 100644 --- a/src/os/macos.cpp +++ b/src/os/macos.cpp @@ -1,7 +1,8 @@ #ifdef __APPLE__ #include -#include +#include +#include #include #include @@ -9,42 +10,41 @@ #include "os.h" #include "src/util/types.h" -fn GetMemInfo() -> expected { +fn GetMemInfo() -> expected { u64 mem = 0; usize size = sizeof(mem); if (sysctlbyname("hw.memsize", &mem, &size, nullptr, 0) == -1) - return std::unexpected(string("sysctlbyname failed: ") + strerror(errno)); + return std::unexpected(std::format("sysctlbyname failed: {}", strerror(errno))); return mem; } -fn GetNowPlaying() -> expected { return GetCurrentPlayingInfo(); } +fn GetNowPlaying() -> expected { return GetCurrentPlayingInfo(); } -fn GetOSVersion() -> expected { return GetMacOSVersion(); } +fn GetOSVersion() -> expected { return GetMacOSVersion(); } -fn GetDesktopEnvironment() -> optional { return std::nullopt; } +fn GetDesktopEnvironment() -> optional { return std::nullopt; } -fn GetWindowManager() -> string { return "Yabai"; } +fn GetWindowManager() -> String { return "Yabai"; } -fn GetKernelVersion() -> string { - std::array kernelVersion; +fn GetKernelVersion() -> String { + std::array kernelVersion {}; usize kernelVersionLen = sizeof(kernelVersion); - sysctlbyname("kern.osrelease", kernelVersion.data(), &kernelVersionLen, nullptr, 0); - + sysctlbyname("kern.osrelease", std::span(kernelVersion).data(), &kernelVersionLen, nullptr, 0); return kernelVersion.data(); } -fn GetHost() -> string { - std::array hwModel; +fn GetHost() -> String { + std::array hwModel {}; size_t hwModelLen = sizeof(hwModel); sysctlbyname("hw.model", hwModel.data(), &hwModelLen, nullptr, 0); // taken from https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/host/host_mac.c // shortened a lot of the entries to remove unnecessary info - std::map modelNameByHwModel = { + std::flat_map modelNameByHwModel = { // MacBook Pro { "MacBookPro18,3", "MacBook Pro (14-inch, 2021)" }, { "MacBookPro18,4", "MacBook Pro (14-inch, 2021)" }, @@ -193,7 +193,7 @@ fn GetHost() -> string { { "iMac9,1", "iMac (24/20-inch, 2009)" }, }; - return modelNameByHwModel[hwModel.data()]; + return String(modelNameByHwModel[hwModel.data()]); } // returns free/total @@ -206,6 +206,6 @@ fn GetDiskUsage() -> std::pair { return { (vfs.f_blocks - vfs.f_bfree) * vfs.f_frsize, vfs.f_blocks * vfs.f_frsize }; } -fn GetShell() -> string { return ""; } +fn GetShell() -> String { return ""; } #endif diff --git a/src/os/macos/bridge.h b/src/os/macos/bridge.h index cc46919..094b441 100644 --- a/src/os/macos/bridge.h +++ b/src/os/macos/bridge.h @@ -3,7 +3,6 @@ #ifdef __APPLE__ #include -#include #include "../../util/macros.h" @@ -13,14 +12,14 @@ @interface Bridge : NSObject + (void)fetchCurrentPlayingMetadata:(void (^)(std::expected))completion; -+ (std::expected)macOSVersion; ++ (std::expected)macOSVersion; @end #else extern "C++" { - fn GetCurrentPlayingInfo() -> std::expected; - fn GetMacOSVersion() -> std::expected; + fn GetCurrentPlayingInfo() -> std::expected; + fn GetMacOSVersion() -> std::expected; } #endif diff --git a/src/os/macos/bridge.mm b/src/os/macos/bridge.mm index e20c6fc..e95f679 100644 --- a/src/os/macos/bridge.mm +++ b/src/os/macos/bridge.mm @@ -2,8 +2,11 @@ #import #include +#include +#include #import #include +#include #import "bridge.h" @@ -12,47 +15,51 @@ using MRMediaRemoteGetNowPlayingInfoFunction = @implementation Bridge + (void)fetchCurrentPlayingMetadata:(void (^)(std::expected))completion { - CFURLRef ref = CFURLCreateWithFileSystemPath( + CFURLRef urlRef = CFURLCreateWithFileSystemPath( kCFAllocatorDefault, CFSTR("/System/Library/PrivateFrameworks/MediaRemote.framework"), kCFURLPOSIXPathStyle, false ); - if (!ref) { + if (!urlRef) { completion(std::unexpected("Failed to create CFURL for MediaRemote framework")); return; } - CFBundleRef bundle = CFBundleCreate(kCFAllocatorDefault, ref); - CFRelease(ref); + CFBundleRef bundleRef = CFBundleCreate(kCFAllocatorDefault, urlRef); - if (!bundle) { + CFRelease(urlRef); + + if (!bundleRef) { completion(std::unexpected("Failed to create bundle for MediaRemote framework")); return; } auto mrMediaRemoteGetNowPlayingInfo = std::bit_cast( - CFBundleGetFunctionPointerForName(bundle, CFSTR("MRMediaRemoteGetNowPlayingInfo")) + CFBundleGetFunctionPointerForName(bundleRef, CFSTR("MRMediaRemoteGetNowPlayingInfo")) ); if (!mrMediaRemoteGetNowPlayingInfo) { - CFRelease(bundle); + CFRelease(bundleRef); completion(std::unexpected("Failed to get MRMediaRemoteGetNowPlayingInfo function pointer")); return; } + std::shared_ptr> sharedBundle(bundleRef, [](CFBundleRef bundle) { + if (bundle) + CFRelease(bundle); + }); + mrMediaRemoteGetNowPlayingInfo( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(NSDictionary* information) { - NSDictionary* nowPlayingInfo = information; // Immutable, no copy needed - CFRelease(bundle); completion( - nowPlayingInfo ? std::expected(nowPlayingInfo) - : std::unexpected("No now playing information") + information ? std::expected(information) + : std::unexpected("No now playing information") ); } ); } -+ (std::expected)macOSVersion { ++ (std::expected)macOSVersion { NSProcessInfo* processInfo = [NSProcessInfo processInfo]; NSOperatingSystemVersion osVersion = [processInfo operatingSystemVersion]; @@ -71,15 +78,15 @@ using MRMediaRemoteGetNowPlayingInfoFunction = NSString* versionName = versionNames[majorVersion] ? versionNames[majorVersion] : @"Unknown"; NSString* fullVersion = [NSString stringWithFormat:@"macOS %@ %@", versionNumber, versionName]; - return std::string([fullVersion UTF8String]); + return String([fullVersion UTF8String]); } @end extern "C++" { // NOLINTBEGIN(misc-use-internal-linkage) - fn GetCurrentPlayingInfo() -> std::expected { - __block std::expected result; - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + fn GetCurrentPlayingInfo() -> std::expected { + __block std::expected result; + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); [Bridge fetchCurrentPlayingMetadata:^(std::expected metadataResult) { if (!metadataResult) { @@ -95,17 +102,17 @@ extern "C++" { return; } - NSString* title = [metadata objectForKey:@"kMRMediaRemoteNowPlayingInfoTitle"]; - NSString* artist = [metadata objectForKey:@"kMRMediaRemoteNowPlayingInfoArtist"]; + NSString* title = metadata[@"kMRMediaRemoteNowPlayingInfoTitle"]; + NSString* artist = metadata[@"kMRMediaRemoteNowPlayingInfoArtist"]; if (!title && !artist) - result = std::unexpected("No metadata"); + result = std::unexpected(NowPlayingError { "No metadata" }); else if (!title) - result = std::string([artist UTF8String]); + result = String([artist UTF8String]); else if (!artist) - result = std::string([title UTF8String]); + result = String([title UTF8String]); else - result = std::string([[NSString stringWithFormat:@"%@ - %@", title, artist] UTF8String]); + result = String([[NSString stringWithFormat:@"%@ - %@", title, artist] UTF8String]); dispatch_semaphore_signal(semaphore); }]; @@ -114,7 +121,7 @@ extern "C++" { return result; } - fn GetMacOSVersion() -> std::expected { return [Bridge macOSVersion]; } + fn GetMacOSVersion() -> std::expected { return [Bridge macOSVersion]; } // NOLINTEND(misc-use-internal-linkage) } diff --git a/src/os/os.h b/src/os/os.h index a1c2b85..a5734d6 100644 --- a/src/os/os.h +++ b/src/os/os.h @@ -10,42 +10,42 @@ using std::optional, std::expected; /** * @brief Get the amount of installed RAM in bytes. */ -fn GetMemInfo() -> expected; +fn GetMemInfo() -> expected; /** * @brief Get the currently playing song metadata. */ -fn GetNowPlaying() -> expected; +fn GetNowPlaying() -> expected; /** * @brief Get the OS version. */ -fn GetOSVersion() -> expected; +fn GetOSVersion() -> expected; /** * @brief Get the current desktop environment. */ -fn GetDesktopEnvironment() -> optional; +fn GetDesktopEnvironment() -> optional; /** * @brief Get the current window manager. */ -fn GetWindowManager() -> string; +fn GetWindowManager() -> String; /** * @brief Get the current shell. */ -fn GetShell() -> string; +fn GetShell() -> String; /** * @brief Get the product family */ -fn GetHost() -> string; +fn GetHost() -> String; /** * @brief Get the kernel version. */ -fn GetKernelVersion() -> string; +fn GetKernelVersion() -> String; /** * @brief Get the number of installed packages. diff --git a/src/util/macros.h b/src/util/macros.h index 6c13c67..fb5d51b 100644 --- a/src/util/macros.h +++ b/src/util/macros.h @@ -1,7 +1,9 @@ #pragma once -// probably stupid but it fixes the issue with windows.h defining ERROR +#ifdef _WIN32 #undef ERROR +#endif + #include #include #include @@ -11,18 +13,15 @@ #include "types.h" -#define fn auto // Rust-style function shorthand +#define fn auto -// Terminal color implementation to replace fmt::color namespace term { - // Text styles enum class Emphasis : u8 { none = 0, bold = 1, italic = 2 }; constexpr fn operator|(Emphasis emphA, Emphasis emphB)->Emphasis { return static_cast(static_cast(emphA) | static_cast(emphB)); } - // Terminal colors enum class Color : u8 { black = 30, red = 31, @@ -42,70 +41,57 @@ namespace term { bright_white = 97 }; - // Style wrapper for foreground color struct FgColor { Color col; constexpr explicit FgColor(Color color) : col(color) {} - [[nodiscard]] fn ansiCode() const -> std::string { return std::format("\033[{}m", static_cast(col)); } + [[nodiscard]] fn ansiCode() const -> String { return std::format("\033[{}m", static_cast(col)); } }; - // Create a foreground color modifier constexpr fn Fg(Color color) -> FgColor { return FgColor(color); } - // Combined style (emphasis + color) struct Style { Emphasis emph = Emphasis::none; FgColor fg_col = FgColor(static_cast(-1)); // Invalid color - [[nodiscard]] fn ansiCode() const -> std::string { - std::string result; + [[nodiscard]] fn ansiCode() const -> String { + String result; if (emph != Emphasis::none) { - if ((static_cast(emph) & static_cast(Emphasis::bold)) != 0) { + if ((static_cast(emph) & static_cast(Emphasis::bold)) != 0) result += "\033[1m"; - } - if ((static_cast(emph) & static_cast(Emphasis::italic)) != 0) { + if ((static_cast(emph) & static_cast(Emphasis::italic)) != 0) result += "\033[3m"; - } } - if (static_cast(fg_col.col) != -1) { + if (static_cast(fg_col.col) != -1) result += fg_col.ansiCode(); - } return result; } }; - // Combine emphasis and color 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|(FgColor fgColor, Emphasis emph)->Style { return emph | fgColor; } - - // Reset all styles constexpr const char* reset = "\033[0m"; - // Print with style template fn Print(const Style& style, std::format_string fmt, Args&&... args) -> void { std::print("{}{}{}", style.ansiCode(), std::format(fmt, std::forward(args)...), reset); } - // Print with foreground color only template fn Print(const FgColor& fgColor, std::format_string fmt, Args&&... args) -> void { std::print("{}{}{}", fgColor.ansiCode(), std::format(fmt, std::forward(args)...), reset); } - // Print with emphasis only template fn Print(Emphasis emph, std::format_string fmt, Args&&... args) -> void { Print({ .emph = emph }, fmt, std::forward(args)...); } - // Print without styling (plain text) template fn Print(std::format_string fmt, Args&&... args) -> void { std::print(fmt, std::forward(args)...); @@ -114,15 +100,20 @@ namespace term { namespace log_colors { using term::Color; - constexpr auto debug = Color::cyan, info = Color::green, warn = Color::yellow, error = Color::red, - timestamp = Color::bright_white, file_info = Color::bright_white; + + constexpr Color debug = Color::cyan, info = Color::green, warn = Color::yellow, error = Color::red, + timestamp = Color::bright_white, file_info = Color::bright_white; } enum class LogLevel : u8 { DEBUG, INFO, WARN, ERROR }; template -void LogImpl(const LogLevel level, const std::source_location& loc, std::format_string fmt, Args&&... args) { - const auto now = std::chrono::floor(std::chrono::system_clock::now()); +fn LogImpl(const LogLevel level, const std::source_location& loc, std::format_string fmt, Args&&... args) + -> void { + using namespace std::chrono; + + const time_point>> now = + std::chrono::floor(std::chrono::system_clock::now()); const auto [color, levelStr] = [&] { switch (level) { @@ -141,19 +132,21 @@ void LogImpl(const LogLevel level, const std::source_location& loc, std::format_ #pragma clang diagnostic pop } }(); - const string filename = std::filesystem::path(loc.file_name()).lexically_normal().string(); - // Timestamp and level - using std::chrono with std::format - term::Print(term::Fg(log_colors::timestamp), "[{:%H:%M:%S}] ", now); - term::Print(term::Emphasis::bold | term::Fg(color), "{} ", levelStr); - // Message - term::Print(fmt, std::forward(args)...); - // File info (debug builds only) + const String filename = std::filesystem::path(loc.file_name()).lexically_normal().string(); + + using namespace term; + + Print(Fg(log_colors::timestamp), "[{:%H:%M:%S}] ", now); + Print(Emphasis::bold | Fg(color), "{} ", levelStr); + Print(fmt, std::forward(args)...); + #ifndef NDEBUG - term::Print(term::Fg(log_colors::file_info), "\n{:>14} ", "╰──"); - term::Print(term::Emphasis::italic | term::Fg(log_colors::file_info), "{}:{}", filename, loc.line()); + Print(Fg(log_colors::file_info), "\n{:>14} ", "╰──"); + Print(Emphasis::italic | Fg(log_colors::file_info), "{}:{}", filename, loc.line()); #endif - term::Print("\n"); + + Print("\n"); } #pragma clang diagnostic push diff --git a/src/util/types.h b/src/util/types.h index 1b99c91..e6daf7b 100644 --- a/src/util/types.h +++ b/src/util/types.h @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -133,7 +134,7 @@ using isize = std::ptrdiff_t; * @typedef string * @brief Represents a string. */ -using string = std::string; +using String = std::string; /** * @enum NowPlayingCode @@ -150,13 +151,13 @@ enum class NowPlayingCode : u8 { * @typedef LinuxError * @brief Represents a Linux-specific error. */ -using LinuxError = std::string; +using LinuxError = String; #elif defined(__APPLE__) /** * @typedef MacError * @brief Represents a macOS-specific error. */ -using MacError = std::string; +using MacError = String; #elif defined(_WIN32) /** * @typedef WindowsError @@ -179,7 +180,7 @@ using NowPlayingError = std::variant< enum class EnvError : u8 { NotFound, AccessError }; -inline auto GetEnv(const std::string& name) -> std::expected { +inline auto GetEnv(const String& name) -> std::expected { #ifdef _WIN32 char* rawPtr = nullptr; size_t bufferSize = 0; @@ -190,7 +191,7 @@ inline auto GetEnv(const std::string& name) -> std::expected std::expected