Compare commits

...

No commits in common. "main" and "windows" have entirely different histories.

41 changed files with 273 additions and 3129 deletions

View file

@ -5,18 +5,14 @@ AlignConsecutiveDeclarations: true
AllowShortBlocksOnASingleLine: Always
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true
BasedOnStyle: Chromium
BinPackArguments: false
BinPackParameters: false
ColumnLimit: 120
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: false
FixNamespaceComments: false
IndentAccessModifiers: false
IndentExternBlock: Indent
IndentWidth: 2
LineEnding: LF
NamespaceIndentation: All
SpaceBeforeCpp11BracedList: true
SpacesBeforeTrailingComments: 1

View file

@ -1,32 +1,25 @@
# noinspection SpellCheckingInspection
Checks: >
*,
-ctad-maybe-unsupported,
-abseil-*,
-altera-*,
-boost-*,
-bugprone-easily-swappable-parameters,
-bugprone-implicit-widening-of-multiplication-result,
-cert-env33-c,
-concurrency-mt-unsafe,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-pro-type-member-init,
-cppcoreguidelines-pro-type-vararg,
-fuchsia-*,
-google-*,
-hicpp-*,
-llvm-include-order,
-llvm-include-order,
-llvm-namespace-comment,
-llvmlibc-*,
-misc-non-private-member-variables-in-classes,
-readability-braces-around-statements,
-readability-function-cognitive-complexity,
-readability-implicit-bool-conversion,
-readability-isolate-declaration,
-readability-magic-numbers
*,
-abseil-*,
-altera-*,
-bugprone-easily-swappable-parameters,
-bugprone-implicit-widening-of-multiplication-result,
-concurrency-mt-unsafe,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-pro-type-member-init,
-fuchsia-*,
-google-*,
-hicpp-*,
-llvm-include-order,
-llvm-include-order,
-llvmlibc-*,
-misc-non-private-member-variables-in-classes,
-modernize-use-trailing-return-type,
-readability-braces-around-statements,
-readability-implicit-bool-conversion,
-readability-isolate-declaration,
-readability-magic-numbers
CheckOptions:
readability-identifier-naming.ClassCase: CamelCase
readability-identifier-naming.EnumCase: CamelCase

View file

@ -1,4 +0,0 @@
Diagnostics:
Suppress: >
-Wmissing-template-arg-list-after-template-kw,
-Wctad-maybe-unsupported

1
.envrc
View file

@ -1 +0,0 @@
use_flake

28
.gitignore vendored
View file

@ -1,28 +1,2 @@
.cache/
.cmake/
.direnv/
.idea/
.vs/
.vscode/
.xmake/
.DS_Store
bin/
build/
cmake-build-debug/
CMakeFiles/
out/
Testing/
build.ninja
CMakeCache.txt
cmake_install.cmake
CMakeSettings.json
compile_commands.json
config.toml
draconis++
Makefile
result
/include/
subprojects/*
!subprojects/*.wrap
cmake-build-*/

10
.gitmodules vendored
View file

@ -1,10 +0,0 @@
[submodule "subprojects/reflectcpp"]
path = subprojects/reflectcpp
url = https://github.com/getml/reflect-cpp
[submodule "subprojects/sdbus-c++"]
path = subprojects/sdbus-c++
url = https://github.com/Kistler-Group/sdbus-cpp
[submodule "subprojects/systemd"]
path = subprojects/systemd
url = https://github.com/systemd/systemd
branch = v257-stable

15
CMakeLists.txt Normal file
View file

@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.28)
project(draconis__)
set(CMAKE_CXX_STANDARD 26)
set(CMAKE_MAKE_PROGRAM "Ninja")
add_executable(draconis__ main.cpp
util.h)
find_package(fmt CONFIG REQUIRED)
find_package(cppwinrt CONFIG REQUIRED)
target_link_libraries(draconis__ PRIVATE fmt::fmt-header-only)
target_link_libraries(draconis__ PRIVATE WindowsApp)

View file

@ -1,107 +0,0 @@
{
"fmt": {
"cargoLocks": null,
"date": null,
"extract": null,
"name": "fmt",
"passthru": null,
"pinned": false,
"src": {
"deepClone": false,
"fetchSubmodules": false,
"leaveDotGit": false,
"name": null,
"owner": "fmtlib",
"repo": "fmt",
"rev": "11.1.3",
"sha256": "sha256-6r9D/csVSgS+T/H0J8cSR+YszxnH/h2V2odi2s6VYN8=",
"sparseCheckout": [],
"type": "github"
},
"version": "11.1.3"
},
"reflect-cpp": {
"cargoLocks": null,
"date": null,
"extract": null,
"name": "reflect-cpp",
"passthru": null,
"pinned": false,
"src": {
"deepClone": false,
"fetchSubmodules": false,
"leaveDotGit": false,
"name": null,
"owner": "getml",
"repo": "reflect-cpp",
"rev": "v0.17.0",
"sha256": "sha256-ugMop4Gsw46zFA7mESNzDTAZVzIg3szAX8ND1kUiF2A=",
"sparseCheckout": [],
"type": "github"
},
"version": "v0.17.0"
},
"sdbus-cpp": {
"cargoLocks": null,
"date": null,
"extract": null,
"name": "sdbus-cpp",
"passthru": null,
"pinned": false,
"src": {
"deepClone": false,
"fetchSubmodules": false,
"leaveDotGit": false,
"name": null,
"owner": "kistler-group",
"repo": "sdbus-cpp",
"rev": "v2.1.0",
"sha256": "sha256-JnjabBr7oELLsUV9a+dAAaRyUzaMIriu90vkaVJg2eY=",
"sparseCheckout": [],
"type": "github"
},
"version": "v2.1.0"
},
"tomlplusplus": {
"cargoLocks": null,
"date": null,
"extract": null,
"name": "tomlplusplus",
"passthru": null,
"pinned": false,
"src": {
"deepClone": false,
"fetchSubmodules": false,
"leaveDotGit": false,
"name": null,
"owner": "marzer",
"repo": "tomlplusplus",
"rev": "v3.4.0",
"sha256": "sha256-h5tbO0Rv2tZezY58yUbyRVpsfRjY3i+5TPkkxr6La8M=",
"sparseCheckout": [],
"type": "github"
},
"version": "v3.4.0"
},
"yyjson": {
"cargoLocks": null,
"date": null,
"extract": null,
"name": "yyjson",
"passthru": null,
"pinned": false,
"src": {
"deepClone": false,
"fetchSubmodules": false,
"leaveDotGit": false,
"name": null,
"owner": "ibireme",
"repo": "yyjson",
"rev": "0.10.0",
"sha256": "sha256-mp9Oz08qTyhj3P6F1d81SX96vamUY/JWpD2DTYR+v04=",
"sparseCheckout": [],
"type": "github"
},
"version": "0.10.0"
}
}

View file

@ -1,59 +0,0 @@
# This file was generated by nvfetcher, please do not modify it manually.
{ fetchgit, fetchurl, fetchFromGitHub, dockerTools }:
{
fmt = {
pname = "fmt";
version = "11.1.3";
src = fetchFromGitHub {
owner = "fmtlib";
repo = "fmt";
rev = "11.1.3";
fetchSubmodules = false;
sha256 = "sha256-6r9D/csVSgS+T/H0J8cSR+YszxnH/h2V2odi2s6VYN8=";
};
};
reflect-cpp = {
pname = "reflect-cpp";
version = "v0.17.0";
src = fetchFromGitHub {
owner = "getml";
repo = "reflect-cpp";
rev = "v0.17.0";
fetchSubmodules = false;
sha256 = "sha256-ugMop4Gsw46zFA7mESNzDTAZVzIg3szAX8ND1kUiF2A=";
};
};
sdbus-cpp = {
pname = "sdbus-cpp";
version = "v2.1.0";
src = fetchFromGitHub {
owner = "kistler-group";
repo = "sdbus-cpp";
rev = "v2.1.0";
fetchSubmodules = false;
sha256 = "sha256-JnjabBr7oELLsUV9a+dAAaRyUzaMIriu90vkaVJg2eY=";
};
};
tomlplusplus = {
pname = "tomlplusplus";
version = "v3.4.0";
src = fetchFromGitHub {
owner = "marzer";
repo = "tomlplusplus";
rev = "v3.4.0";
fetchSubmodules = false;
sha256 = "sha256-h5tbO0Rv2tZezY58yUbyRVpsfRjY3i+5TPkkxr6La8M=";
};
};
yyjson = {
pname = "yyjson";
version = "0.10.0";
src = fetchFromGitHub {
owner = "ibireme";
repo = "yyjson";
rev = "0.10.0";
fetchSubmodules = false;
sha256 = "sha256-mp9Oz08qTyhj3P6F1d81SX96vamUY/JWpD2DTYR+v04=";
};
};
}

View file

@ -1,96 +0,0 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1739863612,
"narHash": "sha256-UbtgxplOhFcyjBcNbTVO8+HUHAl/WXFDOb6LvqShiZo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "632f04521e847173c54fa72973ec6c39a371211c",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1735554305,
"narHash": "sha256-zExSA1i/b+1NMRhGGLtNfFGXgLtgo+dcuzHzaWA6w3Q=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "0e82ab234249d8eee3e8c91437802b32c74bb3fd",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"treefmt-nix": "treefmt-nix",
"utils": "utils"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1739829690,
"narHash": "sha256-mL1szCeIsjh6Khn3nH2cYtwO5YXG6gBiTw1A30iGeDU=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "3d0579f5cc93436052d94b73925b48973a104204",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

175
flake.nix
View file

@ -1,175 +0,0 @@
{
description = "C/C++ environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
treefmt-nix.url = "github:numtide/treefmt-nix";
utils.url = "github:numtide/flake-utils";
};
outputs = {
self,
nixpkgs,
treefmt-nix,
utils,
...
}:
utils.lib.eachDefaultSystem (
system: let
pkgs = import nixpkgs {inherit system;};
llvmPackages = with pkgs;
if hostPlatform.isLinux
then llvmPackages_20
else llvmPackages_19;
stdenv = with pkgs;
(
if hostPlatform.isLinux
then stdenvAdapters.useMoldLinker
else lib.id
)
llvmPackages.stdenv;
sources = import ./_sources/generated.nix {
inherit (pkgs) fetchFromGitHub fetchgit fetchurl dockerTools;
};
mkPkg = name:
pkgs.pkgsStatic.${name}.overrideAttrs {
inherit (sources.${name}) pname version src;
};
fmt = mkPkg "fmt";
tomlplusplus = pkgs.pkgsStatic.tomlplusplus.overrideAttrs {
inherit (sources.tomlplusplus) pname version src;
doCheck = false;
};
sdbus-cpp = pkgs.sdbus-cpp.overrideAttrs {
inherit (sources.sdbus-cpp) pname version src;
cmakeFlags = [
(pkgs.lib.cmakeBool "BUILD_CODE_GEN" true)
(pkgs.lib.cmakeBool "BUILD_SHARED_LIBS" false)
];
};
yyjson = pkgs.pkgsStatic.stdenv.mkDerivation {
inherit (sources.yyjson) pname version src;
nativeBuildInputs = with pkgs; [cmake ninja pkg-config];
};
reflect-cpp = stdenv.mkDerivation rec {
inherit (sources.reflect-cpp) pname version src;
buildInputs = [tomlplusplus yyjson];
nativeBuildInputs = buildInputs ++ (with pkgs; [cmake ninja pkg-config]);
cmakeFlags = [
"-DCMAKE_TOOLCHAIN_FILE=OFF"
"-DREFLECTCPP_TOML=ON"
"-DREFLECTCPP_JSON=ON"
"-DREFLECTCPP_USE_STD_EXPECTED=ON"
];
};
deps = with pkgs.pkgsStatic;
[
# curl
# fmt
# libiconv
# tomlplusplus
# yyjson
# reflect-cpp
# sqlitecpp
# ftxui
]
++ linuxPkgs;
linuxPkgs = nixpkgs.lib.optionals stdenv.isLinux (with pkgs;
[
valgrind
]
++ (with pkgsStatic; [
dbus
xorg.libX11
wayland
]));
in
with pkgs; {
packages = rec {
draconisplusplus = stdenv.mkDerivation {
name = "draconis++";
version = "0.1.0";
src = self;
nativeBuildInputs = [
cmake
meson
ninja
pkg-config
];
buildInputs = deps;
configurePhase = ''
meson setup build
'';
buildPhase = ''
meson compile -C build
'';
installPhase = ''
mkdir -p $out/bin
mv build/draconis++ $out/bin/draconis++
'';
};
default = draconisplusplus;
};
formatter = treefmt-nix.lib.mkWrapper pkgs {
projectRootFile = "flake.nix";
programs = {
alejandra.enable = true;
deadnix.enable = true;
clang-format = {
enable = true;
package = pkgs.llvmPackages.clang-tools;
};
};
};
devShell = mkShell.override {inherit stdenv;} {
packages =
[
alejandra
bear
llvmPackages.clang-tools
cmake
lldb
hyperfine
meson
ninja
nvfetcher
pkg-config
unzip
(writeScriptBin "build" "meson compile -C build")
(writeScriptBin "clean" "meson setup build --wipe")
(writeScriptBin "run" "meson compile -C build && build/draconis++")
]
++ deps;
LD_LIBRARY_PATH = "${lib.makeLibraryPath deps}";
name = "C++";
};
}
);
}

199
main.cpp Normal file
View file

@ -0,0 +1,199 @@
#include <fmt/chrono.h>
#include <fmt/core.h>
#include <iostream>
#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 "util.h"
using namespace winrt;
using namespace winrt::Windows::Media::Control;
struct KBToGiB {
u64 value;
};
template <>
struct fmt::formatter<KBToGiB> : formatter<double> {
template <typename FormatContext>
typename FormatContext::iterator
format(const KBToGiB KTG, FormatContext& ctx) {
typename FormatContext::iterator out = formatter<double>::format(
static_cast<double>(KTG.value) / pow(1024, 2), ctx
);
*out++ = 'G';
*out++ = 'i';
*out++ = 'B';
return out;
}
};
enum DateNum : u8 { Ones, Twos, Threes, Default };
DateNum ParseDate(std::string const& input) {
if (input == "1" || input == "21" || input == "31") return Ones;
if (input == "2" || input == "22") return Twos;
if (input == "3" || input == "23") return Threes;
return Default;
}
std::string GetNowPlaying() {
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 std::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."; }
}
std::string GetRegistryValue(
const HKEY& hKey,
const std::string& subKey,
const std::string& valueName
) {
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 "";
}
std::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;
}
std::string GetPrettyWindowsName() {
std::string productName = GetRegistryValue(
HKEY_LOCAL_MACHINE,
R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)",
"ProductName"
);
const std::string displayVersion = GetRegistryValue(
HKEY_LOCAL_MACHINE,
R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)",
"DisplayVersion"
);
const std::string releaseId = GetRegistryValue(
HKEY_LOCAL_MACHINE,
R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)",
"ReleaseId"
);
const int buildNumber = stoi(GetRegistryValue(
HKEY_LOCAL_MACHINE,
R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)",
"CurrentBuildNumber"
));
fmt::println("Build number: {}", buildNumber);
// Check if the build number is 22000 or higher
if (buildNumber >= 22000 &&
productName.find("Windows 10") != std::string::npos)
productName.replace(productName.find("Windows 10"), 10, "Windows 11");
if (!productName.empty()) {
std::string result = productName;
if (!displayVersion.empty())
result += " " + displayVersion;
else if (!releaseId.empty())
result += " " + releaseId;
return result;
}
return "";
}
int main() {
init_apartment();
u64 memInfo = 0;
GetPhysicallyInstalledSystemMemory(&memInfo);
fmt::println("Installed RAM: {:.2f}", KBToGiB {memInfo});
fmt::println("Now playing: {}", GetNowPlaying());
const std::tm localTime = fmt::localtime(time(nullptr));
std::string date = fmt::format("{:%e}", localTime);
auto start = date.begin();
while (start != date.end() && std::isspace(*start)) ++start;
date.erase(date.begin(), start);
switch (ParseDate(date)) {
case Ones:
date += "st";
break;
case Twos:
date += "nd";
break;
case Threes:
date += "rd";
break;
case Default:
date += "th";
break;
}
fmt::println("{:%B} {}, {:%-I:%0M %p}", localTime, date, localTime);
fmt::println("Version: {}", GetPrettyWindowsName());
uninit_apartment();
return 0;
}

View file

@ -1,150 +0,0 @@
project(
'draconis++',
'cpp',
version: '0.1.0',
default_options: [
'default_library=static',
'warning_level=everything',
'buildtype=release',
'cpp_args=-fvisibility=hidden',
],
)
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(
[
'-std=c++2b',
'-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: 'objcpp',
)
endif
common_cpp_args = [
'-std=c++26',
'-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',
'-fno-strict-enums',
'-nostdlib++',
]
if host_machine.system() == 'windows'
common_cpp_args += ['-DCURL_STATICLIB']
endif
add_project_arguments(cpp.get_supported_arguments(common_cpp_args), language: 'cpp')
source_file_names = ['src/main.cpp', 'src/config/config.cpp', 'src/config/weather.cpp']
if host_machine.system() == 'linux'
source_file_names += ['src/os/linux.cpp', 'src/os/linux/issetugid_stub.cpp']
elif host_machine.system() == 'freebsd'
source_file_names += ['src/os/freebsd.cpp']
elif host_machine.system() == 'darwin'
source_file_names += [
'src/os/macos.cpp',
'src/os/macos/bridge.mm',
]
elif host_machine.system() == 'windows'
source_file_names += ['src/os/windows.cpp']
endif
sources = []
foreach file : source_file_names
sources += files(file)
endforeach
deps = [
dependency('fmt', include_type: 'system', static: true),
dependency('openssl', include_type: 'system', static: true),
dependency('libcurl', include_type: 'system', static: true),
dependency('tomlplusplus', include_type: 'system', static: true),
]
if host_machine.system() == 'darwin'
deps += dependency(
'appleframeworks',
modules: ['foundation', 'mediaplayer', 'systemconfiguration'],
static: true,
)
elif host_machine.system() == 'windows'
deps += [
cpp.find_library('dwmapi'),
cpp.find_library('windowsapp'),
]
elif host_machine.system() == 'linux' or host_machine.system() == 'freebsd'
deps += [
dependency('SQLiteCpp'),
dependency('sdbus-c++'),
dependency('x11'),
dependency('xcb'),
dependency('xau'),
dependency('xdmcp'),
dependency('wayland-client'),
dependency('dbus-1'),
]
endif
cmake = import('cmake')
ftxui_dep = dependency(
'ftxui',
modules: ['ftxui::screen', 'ftxui::dom', 'ftxui::component'],
include_type: 'system',
static: true,
required: false,
)
if not ftxui_dep.found()
ftxui_dom = dependency('ftxui-dom', fallback: ['ftxui', 'dom_dep'])
ftxui_screen = dependency('ftxui-screen', fallback: ['ftxui', 'screen_dep'])
ftxui_component = dependency('ftxui-component', fallback: ['ftxui', 'component_dep'])
ftxui_dep = declare_dependency(dependencies: [ftxui_dom, ftxui_screen, ftxui_component])
endif
deps += ftxui_dep
reflectcpp_proj = cmake.subproject('reflectcpp')
reflectcpp_dep = reflectcpp_proj.dependency('reflectcpp')
deps += reflectcpp_dep
objc_args = []
link_args = []
if host_machine.system() == 'darwin'
objc_args += ['-fobjc-arc']
elif host_machine.system() == 'linux'
link_args += ['-static-libgcc', '-static-libstdc++']
endif
executable(
'draconis++',
sources,
objc_args: objc_args,
link_args: link_args,
dependencies: deps,
)

View file

@ -1,44 +0,0 @@
#include <cstdlib>
#include <filesystem>
#include <fmt/core.h>
#include <stdexcept>
#include "config.h"
using rfl::Result;
namespace fs = std::filesystem;
namespace {
inline fn GetConfigPath() -> fs::path {
#ifdef _WIN32
const char* localAppData = std::getenv("LOCALAPPDATA");
if (!localAppData)
throw std::runtime_error("Environment variable LOCALAPPDATA is not set");
return fs::path(localAppData);
#else
const char* home = std::getenv("HOME");
if (!home)
throw std::runtime_error("Environment variable HOME is not set");
return fs::path(home) / ".config";
#endif
}
}
fn Config::getInstance() -> Config {
fs::path configPath = GetConfigPath();
configPath /= "draconis++/config.toml";
const Result<Config> result = rfl::toml::load<Config>(configPath.string());
if (!result) {
ERROR_LOG("Failed to load config file: {}", result.error().what());
exit(1);
}
return result.value();
}

View file

@ -1,35 +0,0 @@
#include <rfl.hpp>
#include <rfl/Field.hpp>
#include "../util/macros.h"
#include "../util/types.h"
#include "weather.h"
using Location = std::variant<string, Coords>;
struct General {
rfl::Field<"name", string> name = "user";
};
struct NowPlaying {
bool enabled = false;
};
struct Weather {
bool enabled = false;
bool show_town_name = false;
Location location;
string api_key;
string units;
[[nodiscard]] fn getWeatherInfo() const -> WeatherOutput;
};
struct Config {
rfl::Field<"general", General> general = General { .name = "user" };
rfl::Field<"now_playing", NowPlaying> now_playing = NowPlaying();
rfl::Field<"weather", Weather> weather = Weather();
static fn getInstance() -> Config;
};

View file

@ -1,176 +0,0 @@
#include <chrono>
#include <curl/curl.h>
#include <expected>
#include <filesystem>
#include <fmt/core.h>
#include <rfl/json.hpp>
#include <rfl/json/load.hpp>
#include "config.h"
namespace fs = std::filesystem;
using namespace std::string_literals;
// Alias for cleaner error handling
template <typename T>
using Result = std::expected<T, std::string>;
namespace {
// Common function to get cache path
fn GetCachePath() -> Result<fs::path> {
std::error_code errc;
fs::path cachePath = fs::temp_directory_path(errc);
if (errc)
return std::unexpected("Failed to get temp directory: "s + errc.message());
cachePath /= "weather_cache.json";
return cachePath;
}
// Function to read cache from file
fn ReadCacheFromFile() -> Result<WeatherOutput> {
Result<fs::path> cachePath = GetCachePath();
if (!cachePath)
return std::unexpected(cachePath.error());
std::ifstream ifs(*cachePath, std::ios::binary);
if (!ifs.is_open())
return std::unexpected("Cache file not found: "s + cachePath->string());
DEBUG_LOG("Reading from cache file...");
std::string content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
rfl::Result<WeatherOutput> result = rfl::json::read<WeatherOutput>(content);
if (!result)
return std::unexpected(result.error().what());
DEBUG_LOG("Successfully read from cache file.");
return *result;
}
// Function to write cache to file
fn WriteCacheToFile(const WeatherOutput& data) -> Result<void> {
Result<fs::path> cachePath = GetCachePath();
if (!cachePath)
return std::unexpected(cachePath.error());
DEBUG_LOG("Writing to cache file...");
fs::path tempPath = *cachePath;
tempPath += ".tmp";
{
std::ofstream ofs(tempPath, std::ios::binary | std::ios::trunc);
if (!ofs.is_open())
return std::unexpected("Failed to open temp file: "s + tempPath.string());
std::string json = rfl::json::write(data);
ofs << json;
if (!ofs)
return std::unexpected("Failed to write to temp file");
}
std::error_code errc;
fs::rename(tempPath, *cachePath, errc);
if (errc) {
fs::remove(tempPath, errc);
return std::unexpected("Failed to replace cache file: "s + errc.message());
}
DEBUG_LOG("Successfully wrote to cache file.");
return {};
}
fn WriteCallback(void* contents, size_t size, size_t nmemb, std::string* str) -> size_t {
const size_t totalSize = size * nmemb;
str->append(static_cast<char*>(contents), totalSize);
return totalSize;
}
// Function to make API request
fn MakeApiRequest(const std::string& url) -> Result<WeatherOutput> {
DEBUG_LOG("Making API request to URL: {}", url);
CURL* curl = curl_easy_init();
std::string responseBuffer;
if (!curl)
return std::unexpected("Failed to initialize cURL");
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseBuffer);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5);
const CURLcode res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (res != CURLE_OK)
return std::unexpected(fmt::format("cURL error: {}", curl_easy_strerror(res)));
DEBUG_LOG("API response size: {}", responseBuffer.size());
DEBUG_LOG("API response: {}", responseBuffer);
rfl::Result<WeatherOutput> output = rfl::json::read<WeatherOutput>(responseBuffer);
if (!output)
return std::unexpected(output.error().what());
return *output;
}
}
// Core function to get weather information
fn Weather::getWeatherInfo() const -> WeatherOutput {
using namespace std::chrono;
if (Result<WeatherOutput> data = ReadCacheFromFile()) {
const WeatherOutput& dataVal = *data;
const duration<double> cacheAge = system_clock::now() - system_clock::time_point(seconds(dataVal.dt));
if (cacheAge < 10min) {
DEBUG_LOG("Using valid cache");
return dataVal;
}
DEBUG_LOG("Cache expired");
} else {
DEBUG_LOG("Cache error: {}", data.error());
}
fn handleApiResult = [](const Result<WeatherOutput>& result) -> WeatherOutput {
if (!result)
ERROR_LOG("API request failed: {}", result.error());
// Fix for second warning: Check the write result
if (Result<void> writeResult = WriteCacheToFile(*result); !writeResult)
ERROR_LOG("Failed to write cache: {}", writeResult.error());
return *result;
};
if (std::holds_alternative<std::string>(location)) {
const auto& city = std::get<std::string>(location);
char* escaped = curl_easy_escape(nullptr, city.c_str(), static_cast<int>(city.length()));
DEBUG_LOG("Requesting city: {}", escaped);
const std::string apiUrl =
fmt::format("https://api.openweathermap.org/data/2.5/weather?q={}&appid={}&units={}", escaped, api_key, units);
curl_free(escaped);
return handleApiResult(MakeApiRequest(apiUrl));
}
const auto& [lat, lon] = std::get<Coords>(location);
DEBUG_LOG("Requesting coordinates: lat={:.3f}, lon={:.3f}", lat, lon);
const std::string apiUrl = fmt::format(
"https://api.openweathermap.org/data/2.5/weather?lat={:.3f}&lon={:.3f}&appid={}&units={}", lat, lon, api_key, units
);
return handleApiResult(MakeApiRequest(apiUrl));
}

View file

@ -1,73 +0,0 @@
#pragma once
#include <rfl.hpp>
#include <rfl/toml.hpp>
#include "../util/types.h"
using degrees = rfl::Validator<u16, rfl::Minimum<0>, rfl::Maximum<360>>;
using percentage = rfl::Validator<u8, rfl::Minimum<0>, rfl::Maximum<100>>;
struct Condition {
string description;
string icon;
string main;
usize id;
};
struct Main {
f64 feels_like;
f64 temp;
f64 temp_max;
f64 temp_min;
isize pressure;
percentage humidity;
std::optional<isize> grnd_level;
std::optional<isize> sea_level;
};
struct Wind {
degrees deg;
f64 speed;
std::optional<f64> gust;
};
struct Precipitation {
rfl::Rename<"1h", std::optional<f64>> one_hour;
rfl::Rename<"3h", std::optional<f64>> three_hours;
};
struct Sys {
string country;
usize id;
usize sunrise;
usize sunset;
usize type;
};
struct Clouds {
percentage all;
};
struct Coords {
double lat;
double lon;
};
struct WeatherOutput {
Clouds clouds;
isize timezone;
isize visibility;
Main main;
rfl::Rename<"coord", Coords> coords;
std::optional<Precipitation> rain;
std::optional<Precipitation> snow;
string base;
string name;
std::vector<Condition> weather;
Sys sys;
usize cod;
usize dt;
usize id;
Wind wind;
};

View file

@ -1,312 +0,0 @@
#include <ctime>
#include <fmt/chrono.h>
#include <fmt/color.h>
#include <fmt/core.h>
#include <fmt/format.h>
#include <ftxui/dom/elements.hpp>
#include <ftxui/screen/screen.hpp>
#include <future>
#include <string>
#include <variant>
#include "config/config.h"
#include "ftxui/screen/color.hpp"
#include "os/os.h"
constexpr const bool SHOW_ICONS = true;
struct BytesToGiB {
u64 value;
};
// 1024^3 (size of 1 GiB)
constexpr u64 GIB = 1'073'741'824;
template <>
struct fmt::formatter<BytesToGiB> : fmt::formatter<double> {
template <typename FmtCtx>
constexpr fn format(const BytesToGiB& BTG, FmtCtx& ctx) const -> typename FmtCtx::iterator {
return fmt::format_to(fmt::formatter<double>::format(static_cast<double>(BTG.value) / GIB, ctx), "GiB");
}
};
namespace {
fn GetDate() -> std::string {
// Get current local time
std::time_t now = std::time(nullptr);
std::tm localTime;
#ifdef __WIN32__
if (localtime_s(&localTime, &now) != 0)
ERROR_LOG("localtime_s failed");
#else
if (localtime_r(&now, &localTime) == nullptr)
ERROR_LOG("localtime_r failed");
#endif
// Format the date using fmt::format
std::string date = fmt::format("{:%e}", localTime);
// Remove leading whitespace
if (!date.empty() && std::isspace(date.front()))
date.erase(date.begin());
// Append appropriate suffix for the date
if (date.ends_with("1") && date != "11")
date += "st";
else if (date.ends_with("2") && date != "12")
date += "nd";
else if (date.ends_with("3") && date != "13")
date += "rd";
else
date += "th";
return fmt::format("{:%B} {}", localTime, date);
}
struct SystemData {
std::string date;
std::string host;
std::string kernel_version;
std::expected<string, string> os_version;
std::expected<u64, std::string> mem_info;
std::optional<string> desktop_environment;
std::string window_manager;
std::optional<std::expected<string, NowPlayingError>> now_playing;
std::optional<WeatherOutput> weather_info;
static fn fetchSystemData(const Config& config) -> SystemData {
SystemData data;
// Group tasks by dependency/type
auto [date, host, kernel] = std::tuple(
std::async(std::launch::async, GetDate),
std::async(std::launch::async, GetHost),
std::async(std::launch::async, GetKernelVersion)
);
auto [osVer, mem, desktop, winManager] = std::tuple(
std::async(std::launch::async, GetOSVersion),
std::async(std::launch::async, GetMemInfo),
std::async(std::launch::async, GetDesktopEnvironment),
std::async(std::launch::async, GetWindowManager)
);
// Conditional async tasks
std::future<WeatherOutput> weather;
std::future<std::expected<string, NowPlayingError>> nowPlaying;
if (config.weather.get().enabled)
weather = std::async(std::launch::async, [&] { return config.weather.get().getWeatherInfo(); });
if (config.now_playing.get().enabled)
nowPlaying = std::async(std::launch::async, GetNowPlaying);
// Ordered wait for fastest completion
data.date = date.get();
data.host = host.get();
data.kernel_version = kernel.get();
data.os_version = osVer.get();
data.mem_info = mem.get();
data.desktop_environment = desktop.get();
data.window_manager = winManager.get();
if (weather.valid())
data.weather_info = weather.get();
if (nowPlaying.valid())
data.now_playing = nowPlaying.get();
return data;
}
};
using namespace ftxui;
fn CreateColorCircles() -> Element {
Elements circles(16);
std::generate_n(circles.begin(), 16, [colorIndex = 0]() mutable {
return hbox({ text("") | bold | color(Color::Palette256(colorIndex++)), text(" ") });
});
return hbox(circles);
}
fn SystemInfoBox(const Config& config, const SystemData& data) -> Element {
// Fetch data
const string& name = config.general.get().name.get();
const Weather weather = config.weather.get();
const bool nowPlayingEnabled = config.now_playing.get().enabled;
const char *calendarIcon = "", *hostIcon = "", *kernelIcon = "", *osIcon = "", *memoryIcon = "", *weatherIcon = "",
*musicIcon = "";
if (SHOW_ICONS) {
calendarIcon = "";
hostIcon = " 󰌢 ";
kernelIcon = "";
osIcon = "";
memoryIcon = "";
weatherIcon = "";
musicIcon = "";
}
const Color::Palette16 labelColor = Color::Yellow;
const Color::Palette16 valueColor = Color::White;
const Color::Palette16 borderColor = Color::GrayLight;
const Color::Palette16 iconColor = Color::Cyan;
Elements content;
content.push_back(text("  Hello " + name + "! ") | bold | color(Color::Cyan));
content.push_back(separator() | color(borderColor));
content.push_back(hbox(
{
text("") | color(iconColor), // Palette icon
CreateColorCircles(),
}
));
content.push_back(separator() | color(borderColor));
// Helper function for aligned rows
fn createRow = [&](const std::string& icon, const std::string& label, const std::string& value) {
return hbox(
{
text(icon) | color(iconColor),
text(label) | color(labelColor),
filler(),
text(value) | color(valueColor),
text(" "),
}
);
};
// System info rows
content.push_back(createRow(calendarIcon, "Date", data.date));
// Weather row
if (weather.enabled && data.weather_info.has_value()) {
const WeatherOutput& weatherInfo = data.weather_info.value();
if (weather.show_town_name)
content.push_back(hbox(
{
text(weatherIcon) | color(iconColor),
text("Weather") | color(labelColor),
filler(),
hbox(
{
text(fmt::format("{}°F ", std::lround(weatherInfo.main.temp))),
text("in "),
text(weatherInfo.name),
text(" "),
}
) |
color(valueColor),
}
));
else
content.push_back(hbox(
{
text(weatherIcon) | color(iconColor),
text("Weather") | color(labelColor),
filler(),
hbox(
{
text(fmt::format("{}°F, {}", std::lround(weatherInfo.main.temp), weatherInfo.weather[0].description)),
text(" "),
}
) |
color(valueColor),
}
));
}
content.push_back(separator() | color(borderColor));
if (!data.host.empty())
content.push_back(createRow(hostIcon, "Host", data.host));
if (!data.kernel_version.empty())
content.push_back(createRow(kernelIcon, "Kernel", data.kernel_version));
if (data.os_version.has_value())
content.push_back(createRow(osIcon, "OS", *data.os_version));
else
ERROR_LOG("Failed to get OS version: {}", data.os_version.error());
if (data.mem_info.has_value())
content.push_back(createRow(memoryIcon, "RAM", fmt::format("{:.2f}", BytesToGiB { *data.mem_info })));
else
ERROR_LOG("Failed to get memory info: {}", data.mem_info.error());
content.push_back(separator() | color(borderColor));
if (data.desktop_environment.has_value() && *data.desktop_environment != data.window_manager)
content.push_back(createRow(" 󰇄 ", "DE", *data.desktop_environment));
if (!data.window_manager.empty())
content.push_back(createRow("  ", "WM", data.window_manager));
// Now Playing row
if (nowPlayingEnabled && data.now_playing.has_value()) {
const std::expected<string, NowPlayingError>& nowPlayingResult = *data.now_playing;
if (nowPlayingResult.has_value()) {
const std::string& npText = *nowPlayingResult;
content.push_back(separator() | color(borderColor));
content.push_back(hbox(
{
text(musicIcon) | color(iconColor),
text("Playing") | color(labelColor),
text(" "),
filler(),
paragraph(npText) | color(Color::Magenta) | size(WIDTH, LESS_THAN, 30),
text(" "),
}
));
} else {
const NowPlayingError& error = nowPlayingResult.error();
if (std::holds_alternative<NowPlayingCode>(error))
switch (std::get<NowPlayingCode>(error)) {
case NowPlayingCode::NoPlayers:
DEBUG_LOG("No players found");
break;
case NowPlayingCode::NoActivePlayer:
DEBUG_LOG("No active player found");
break;
}
#ifdef __linux__
if (std::holds_alternative<LinuxError>(error))
DEBUG_LOG("DBus error: {}", std::get<LinuxError>(error));
#endif
#ifdef __WIN32__
if (std::holds_alternative<WindowsError>(error))
DEBUG_LOG("WinRT error: {}", to_string(std::get<WindowsError>(error).message()));
#endif
}
}
return vbox(content) | borderRounded | color(Color::White);
}
}
fn main() -> i32 {
const Config& config = Config::getInstance();
SystemData data = SystemData::fetchSystemData(config);
Element document = hbox({ SystemInfoBox(config, data), filler() });
Screen screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
Render(screen, document);
screen.Print();
return 0;
}

View file

@ -1,117 +0,0 @@
#ifdef __FreeBSD__
#include <fmt/format.h>
#include <fstream>
#include <iostream>
#include <sdbus-c++/sdbus-c++.h>
#include <sys/sysctl.h>
#include "os.h"
fn GetMemInfo() -> u64 {
u64 mem = 0;
usize size = sizeof(mem);
sysctlbyname("hw.physmem", &mem, &size, nullptr, 0);
return mem;
}
fn GetOSVersion() -> string {
std::ifstream file("/etc/os-release");
if (!file.is_open()) {
std::cerr << "Failed to open /etc/os-release" << std::endl;
return ""; // Return empty string indicating failure
}
string line;
const string prefix = "PRETTY_NAME=";
while (std::getline(file, line)) {
if (line.find(prefix) == 0) {
string prettyName = line.substr(prefix.size());
// Remove surrounding quotes if present
if (!prettyName.empty() && prettyName.front() == '"' && prettyName.back() == '"')
prettyName = prettyName.substr(1, prettyName.size() - 2);
return prettyName;
}
}
return ""; // Return empty string if PRETTY_NAME= line not found
}
fn GetMprisPlayers(sdbus::IConnection& connection) -> std::vector<string> {
const sdbus::ServiceName dbusInterface = sdbus::ServiceName("org.freedesktop.DBus");
const sdbus::ObjectPath dbusObjectPath = sdbus::ObjectPath("/org/freedesktop/DBus");
const char* dbusMethodListNames = "ListNames";
const std::unique_ptr<sdbus::IProxy> dbusProxy =
createProxy(connection, dbusInterface, dbusObjectPath);
std::vector<string> names;
dbusProxy->callMethod(dbusMethodListNames).onInterface(dbusInterface).storeResultsTo(names);
std::vector<string> mprisPlayers;
for (const std::basic_string<char>& name : names)
if (const char* mprisInterfaceName = "org.mpris.MediaPlayer2";
name.find(mprisInterfaceName) != std::string::npos)
mprisPlayers.push_back(name);
return mprisPlayers;
}
fn GetActivePlayer(const std::vector<string>& mprisPlayers) -> string {
if (!mprisPlayers.empty())
return mprisPlayers.front();
return "";
}
fn GetNowPlaying() -> string {
try {
const char *playerObjectPath = "/org/mpris/MediaPlayer2",
*playerInterfaceName = "org.mpris.MediaPlayer2.Player";
std::unique_ptr<sdbus::IConnection> connection = sdbus::createSessionBusConnection();
std::vector<string> mprisPlayers = GetMprisPlayers(*connection);
if (mprisPlayers.empty())
return "";
string activePlayer = GetActivePlayer(mprisPlayers);
if (activePlayer.empty())
return "";
auto playerProxy = sdbus::createProxy(
*connection, sdbus::ServiceName(activePlayer), sdbus::ObjectPath(playerObjectPath)
);
sdbus::Variant metadataVariant =
playerProxy->getProperty("Metadata").onInterface(playerInterfaceName);
if (metadataVariant.containsValueOfType<std::map<std::string, sdbus::Variant>>()) {
const auto& metadata = metadataVariant.get<std::map<std::string, sdbus::Variant>>();
auto iter = metadata.find("xesam:title");
if (iter != metadata.end() && iter->second.containsValueOfType<std::string>())
return iter->second.get<std::string>();
}
} catch (const sdbus::Error& e) {
if (e.getName() != "com.github.altdesktop.playerctld.NoActivePlayer")
return fmt::format("Error: {}", e.what());
return "No active player";
}
return "";
}
#endif

View file

@ -1,694 +0,0 @@
#ifdef __linux__
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <algorithm>
#include <cstring>
#include <dbus/dbus.h>
#include <dirent.h>
#include <expected>
#include <filesystem>
#include <fmt/format.h>
#include <fstream>
#include <iostream>
#include <mutex>
#include <optional>
#include <ranges>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <vector>
#include <wayland-client.h>
#include "os.h"
#include "src/util/macros.h"
using std::errc, std::expected, std::from_chars, std::getline, std::istreambuf_iterator, std::less, std::lock_guard,
std::mutex, std::ofstream, std::pair, std::string_view, std::vector, std::nullopt, std::array, std::optional,
std::bit_cast, std::to_string, std::ifstream, std::getenv, std::string, std::unexpected, std::ranges::is_sorted,
std::ranges::lower_bound, std::ranges::replace, std::ranges::subrange, std::ranges::transform;
using namespace std::literals::string_view_literals;
namespace fs = std::filesystem;
enum SessionType : u8 { Wayland, X11, TTY, Unknown };
namespace {
fn GetX11WindowManager() -> string {
Display* display = XOpenDisplay(nullptr);
// If XOpenDisplay fails, likely in a TTY
if (!display)
return "";
Atom supportingWmCheck = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
Atom wmName = XInternAtom(display, "_NET_WM_NAME", False);
Atom utf8String = XInternAtom(display, "UTF8_STRING", False);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast"
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
Window root = DefaultRootWindow(display); // NOLINT
#pragma clang diagnostic pop
Window wmWindow = 0;
Atom actualType = 0;
int actualFormat = 0;
unsigned long nitems = 0, bytesAfter = 0;
unsigned char* data = nullptr;
if (XGetWindowProperty(
display,
root,
supportingWmCheck,
0,
1,
False,
// XA_WINDOW
static_cast<Atom>(33),
&actualType,
&actualFormat,
&nitems,
&bytesAfter,
&data
) == Success &&
data) {
wmWindow = *bit_cast<Window*>(data);
XFree(data);
data = nullptr;
if (XGetWindowProperty(
display,
wmWindow,
wmName,
0,
1024,
False,
utf8String,
&actualType,
&actualFormat,
&nitems,
&bytesAfter,
&data
) == Success &&
data) {
string name(bit_cast<char*>(data));
XFree(data);
XCloseDisplay(display);
return name;
}
}
XCloseDisplay(display);
return "Unknown (X11)"; // Changed to empty string
}
fn TrimHyprlandWrapper(const string& input) -> string {
if (input.contains("hyprland"))
return "Hyprland";
return input;
}
fn ReadProcessCmdline(int pid) -> string {
string path = "/proc/" + to_string(pid) + "/cmdline";
ifstream cmdlineFile(path);
string cmdline;
if (getline(cmdlineFile, cmdline)) {
// Replace null bytes with spaces
replace(cmdline, '\0', ' ');
return cmdline;
}
return "";
}
fn DetectHyprlandSpecific() -> string {
// Check environment variables first
const char* xdgCurrentDesktop = getenv("XDG_CURRENT_DESKTOP");
if (xdgCurrentDesktop && strcasestr(xdgCurrentDesktop, "hyprland"))
return "Hyprland";
// Check for Hyprland's specific environment variable
if (getenv("HYPRLAND_INSTANCE_SIGNATURE"))
return "Hyprland";
// Check for Hyprland socket
if (fs::exists("/run/user/" + to_string(getuid()) + "/hypr"))
return "Hyprland";
return "";
}
fn GetWaylandCompositor() -> string {
// First try Hyprland-specific detection
string hypr = DetectHyprlandSpecific();
if (!hypr.empty())
return hypr;
// Then try the standard Wayland detection
wl_display* display = wl_display_connect(nullptr);
if (!display)
return "";
int fileDescriptor = wl_display_get_fd(display);
struct ucred cred;
socklen_t len = sizeof(cred);
if (getsockopt(fileDescriptor, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1) {
wl_display_disconnect(display);
return "";
}
// Read both comm and cmdline
string compositorName;
// 1. Check comm (might be wrapped)
string commPath = "/proc/" + to_string(cred.pid) + "/comm";
ifstream commFile(commPath);
if (commFile >> compositorName) {
subrange removedRange = std::ranges::remove(compositorName, '\n');
compositorName.erase(removedRange.begin(), compositorName.end());
}
// 2. Check cmdline for actual binary reference
string cmdline = ReadProcessCmdline(cred.pid);
if (cmdline.contains("hyprland")) {
wl_display_disconnect(display);
return "Hyprland";
}
// 3. Check exe symlink
string exePath = "/proc/" + to_string(cred.pid) + "/exe";
array<char, PATH_MAX> buf;
ssize_t lenBuf = readlink(exePath.c_str(), buf.data(), buf.size() - 1);
if (lenBuf != -1) {
buf.at(static_cast<usize>(lenBuf)) = '\0';
string exe(buf.data());
if (exe.contains("hyprland")) {
wl_display_disconnect(display);
return "Hyprland";
}
}
wl_display_disconnect(display);
// Final cleanup of wrapper names
return TrimHyprlandWrapper(compositorName);
}
fn DetectFromEnvVars() -> optional<string> {
// Use RAII to guard against concurrent env modifications
static mutex EnvMutex;
lock_guard<mutex> lock(EnvMutex);
// XDG_CURRENT_DESKTOP
if (const char* xdgCurrentDesktop = getenv("XDG_CURRENT_DESKTOP")) {
const string_view sview(xdgCurrentDesktop);
const size_t colon = sview.find(':');
return string(sview.substr(0, colon)); // Direct construct from view
}
// DESKTOP_SESSION
if (const char* desktopSession = getenv("DESKTOP_SESSION"))
return string(string_view(desktopSession)); // Avoid intermediate view storage
return nullopt;
}
fn DetectFromSessionFiles() -> optional<string> {
static constexpr array<pair<string_view, string_view>, 12> DE_PATTERNS = {
// clang-format off
pair { "Budgie"sv, "budgie"sv },
pair { "Cinnamon"sv, "cinnamon"sv },
pair { "LXQt"sv, "lxqt"sv },
pair { "MATE"sv, "mate"sv },
pair { "Unity"sv, "unity"sv },
pair { "gnome"sv, "GNOME"sv },
pair { "gnome-wayland"sv, "GNOME"sv },
pair { "gnome-xorg"sv, "GNOME"sv },
pair { "kde"sv, "KDE"sv },
pair { "plasma"sv, "KDE"sv },
pair { "plasmax11"sv, "KDE"sv },
pair { "xfce"sv, "XFCE"sv },
// clang-format on
};
static_assert(is_sorted(DE_PATTERNS, {}, &pair<string_view, string_view>::first));
// Precomputed session paths
static constexpr array<string_view, 2> SESSION_PATHS = { "/usr/share/xsessions", "/usr/share/wayland-sessions" };
// Single memory reserve for lowercase conversions
string lowercaseStem;
lowercaseStem.reserve(32);
for (const auto& path : SESSION_PATHS) {
if (!fs::exists(path))
continue;
for (const auto& entry : fs::directory_iterator(path)) {
if (!entry.is_regular_file())
continue;
// Reuse buffer
lowercaseStem = entry.path().stem().string();
transform(lowercaseStem, lowercaseStem.begin(), ::tolower);
// Modern ranges version
const pair<string_view, string_view>* const patternIter = lower_bound(
DE_PATTERNS, lowercaseStem, less {}, &pair<string_view, string_view>::first // Projection
);
if (patternIter != DE_PATTERNS.end() && patternIter->first == lowercaseStem)
return string(patternIter->second);
}
}
return nullopt;
}
fn DetectFromProcesses() -> optional<string> {
const array processChecks = {
// clang-format off
pair { "plasmashell"sv, "KDE"sv },
pair { "gnome-shell"sv, "GNOME"sv },
pair { "xfce4-session"sv, "XFCE"sv },
pair { "mate-session"sv, "MATE"sv },
pair { "cinnamon-sessio"sv, "Cinnamon"sv },
pair { "budgie-wm"sv, "Budgie"sv },
pair { "lxqt-session"sv, "LXQt"sv },
// clang-format on
};
ifstream cmdline("/proc/self/environ");
string envVars((istreambuf_iterator<char>(cmdline)), istreambuf_iterator<char>());
for (const auto& [process, deName] : processChecks)
if (envVars.contains(process))
return string(deName);
return nullopt;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast"
fn GetMprisPlayers(DBusConnection* connection) -> vector<string> {
vector<string> mprisPlayers;
DBusError err;
dbus_error_init(&err);
// Create a method call to org.freedesktop.DBus.ListNames
DBusMessage* msg = dbus_message_new_method_call(
"org.freedesktop.DBus", // target service
"/org/freedesktop/DBus", // object path
"org.freedesktop.DBus", // interface name
"ListNames" // method name
);
if (!msg) {
DEBUG_LOG("Failed to create message for ListNames.");
return mprisPlayers;
}
// Send the message and block until we get a reply.
DBusMessage* reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &err);
dbus_message_unref(msg);
if (dbus_error_is_set(&err)) {
DEBUG_LOG("DBus error in ListNames: {}", err.message);
dbus_error_free(&err);
return mprisPlayers;
}
if (!reply) {
DEBUG_LOG("No reply received for ListNames.");
return mprisPlayers;
}
// The expected reply signature is "as" (an array of strings)
DBusMessageIter iter;
if (!dbus_message_iter_init(reply, &iter)) {
DEBUG_LOG("Reply has no arguments.");
dbus_message_unref(reply);
return mprisPlayers;
}
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
DEBUG_LOG("Reply argument is not an array.");
dbus_message_unref(reply);
return mprisPlayers;
}
// Iterate over the array of strings
DBusMessageIter subIter;
dbus_message_iter_recurse(&iter, &subIter);
while (dbus_message_iter_get_arg_type(&subIter) != DBUS_TYPE_INVALID) {
if (dbus_message_iter_get_arg_type(&subIter) == DBUS_TYPE_STRING) {
const char* name = nullptr;
dbus_message_iter_get_basic(&subIter, static_cast<void*>(&name));
if (name && std::string_view(name).contains("org.mpris.MediaPlayer2"))
mprisPlayers.emplace_back(name);
}
dbus_message_iter_next(&subIter);
}
dbus_message_unref(reply);
return mprisPlayers;
}
#pragma clang diagnostic pop
fn GetActivePlayer(const vector<string>& mprisPlayers) -> optional<string> {
if (!mprisPlayers.empty())
return mprisPlayers.front();
return nullopt;
}
}
fn GetOSVersion() -> expected<string, string> {
constexpr const char* path = "/etc/os-release";
ifstream file(path);
if (!file.is_open())
return unexpected("Failed to open " + string(path));
string line;
const string prefix = "PRETTY_NAME=";
while (getline(file, line))
if (line.starts_with(prefix)) {
string prettyName = line.substr(prefix.size());
if (!prettyName.empty() && prettyName.front() == '"' && prettyName.back() == '"')
return prettyName.substr(1, prettyName.size() - 2);
return prettyName;
}
return unexpected("PRETTY_NAME line not found in " + string(path));
}
fn GetMemInfo() -> expected<u64, string> {
constexpr const char* path = "/proc/meminfo";
ifstream input(path);
if (!input.is_open())
return unexpected("Failed to open " + string(path));
string line;
while (getline(input, line)) {
if (line.starts_with("MemTotal")) {
const size_t colonPos = line.find(':');
if (colonPos == string::npos)
return unexpected("Invalid MemTotal line: no colon found");
string_view view(line);
view.remove_prefix(colonPos + 1);
// Trim leading whitespace
const size_t firstNonSpace = view.find_first_not_of(' ');
if (firstNonSpace == string_view::npos)
return unexpected("No number found after colon in MemTotal line");
view.remove_prefix(firstNonSpace);
// Find the end of the numeric part
const size_t end = view.find_first_not_of("0123456789");
if (end != string_view::npos)
view = view.substr(0, end);
// Get pointers via iterators
const char* startPtr = &*view.begin();
const char* endPtr = &*view.end();
u64 value = 0;
const auto result = from_chars(startPtr, endPtr, value);
if (result.ec != errc() || result.ptr != endPtr)
return unexpected("Failed to parse number in MemTotal line");
return value * 1024;
}
}
return unexpected("MemTotal line not found in " + string(path));
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast"
fn GetNowPlaying() -> expected<string, NowPlayingError> {
DBusError err;
dbus_error_init(&err);
// Connect to the session bus
DBusConnection* connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
if (!connection)
if (dbus_error_is_set(&err)) {
ERROR_LOG("DBus connection error: {}", err.message);
NowPlayingError error = LinuxError(err.message);
dbus_error_free(&err);
return unexpected(error);
}
vector<string> mprisPlayers = GetMprisPlayers(connection);
if (mprisPlayers.empty()) {
dbus_connection_unref(connection);
return unexpected(NowPlayingError { NowPlayingCode::NoPlayers });
}
optional<string> activePlayer = GetActivePlayer(mprisPlayers);
if (!activePlayer.has_value()) {
dbus_connection_unref(connection);
return unexpected(NowPlayingError { NowPlayingCode::NoActivePlayer });
}
// Prepare a call to the Properties.Get method to fetch "Metadata"
DBusMessage* msg = dbus_message_new_method_call(
activePlayer->c_str(), // target service (active player)
"/org/mpris/MediaPlayer2", // object path
"org.freedesktop.DBus.Properties", // interface
"Get" // method name
);
if (!msg) {
dbus_connection_unref(connection);
return unexpected(NowPlayingError { /* error creating message */ });
}
const char* interfaceName = "org.mpris.MediaPlayer2.Player";
const char* propertyName = "Metadata";
if (!dbus_message_append_args(
msg, DBUS_TYPE_STRING, &interfaceName, DBUS_TYPE_STRING, &propertyName, DBUS_TYPE_INVALID
)) {
dbus_message_unref(msg);
dbus_connection_unref(connection);
return unexpected(NowPlayingError { /* error appending arguments */ });
}
// Call the method and block until reply is received.
DBusMessage* reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &err);
dbus_message_unref(msg);
if (dbus_error_is_set(&err)) {
ERROR_LOG("DBus error in Properties.Get: {}", err.message);
NowPlayingError error = LinuxError(err.message);
dbus_error_free(&err);
dbus_connection_unref(connection);
return unexpected(error);
}
if (!reply) {
dbus_connection_unref(connection);
return unexpected(NowPlayingError { /* no reply error */ });
}
// The reply should contain a variant holding a dictionary ("a{sv}")
DBusMessageIter iter;
if (!dbus_message_iter_init(reply, &iter)) {
dbus_message_unref(reply);
dbus_connection_unref(connection);
return unexpected(NowPlayingError { /* no arguments in reply */ });
}
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
dbus_message_unref(reply);
dbus_connection_unref(connection);
return unexpected(NowPlayingError { /* unexpected argument type */ });
}
// Recurse into the variant to get the dictionary
DBusMessageIter variantIter;
dbus_message_iter_recurse(&iter, &variantIter);
if (dbus_message_iter_get_arg_type(&variantIter) != DBUS_TYPE_ARRAY) {
dbus_message_unref(reply);
dbus_connection_unref(connection);
return unexpected(NowPlayingError { /* expected array type */ });
}
string title;
string artist;
DBusMessageIter arrayIter;
dbus_message_iter_recurse(&variantIter, &arrayIter);
// Iterate over each dictionary entry (each entry is of type dict entry)
while (dbus_message_iter_get_arg_type(&arrayIter) != DBUS_TYPE_INVALID) {
if (dbus_message_iter_get_arg_type(&arrayIter) == DBUS_TYPE_DICT_ENTRY) {
DBusMessageIter dictEntry;
dbus_message_iter_recurse(&arrayIter, &dictEntry);
// Get the key (a string)
const char* key = nullptr;
if (dbus_message_iter_get_arg_type(&dictEntry) == DBUS_TYPE_STRING)
dbus_message_iter_get_basic(&dictEntry, static_cast<void*>(&key));
// Move to the value (a variant)
dbus_message_iter_next(&dictEntry);
if (dbus_message_iter_get_arg_type(&dictEntry) == DBUS_TYPE_VARIANT) {
DBusMessageIter valueIter;
dbus_message_iter_recurse(&dictEntry, &valueIter);
if (key && std::string_view(key) == "xesam:title") {
if (dbus_message_iter_get_arg_type(&valueIter) == DBUS_TYPE_STRING) {
const char* val = nullptr;
dbus_message_iter_get_basic(&valueIter, static_cast<void*>(&val));
if (val)
title = val;
}
} else if (key && std::string_view(key) == "xesam:artist") {
// Expect an array of strings
if (dbus_message_iter_get_arg_type(&valueIter) == DBUS_TYPE_ARRAY) {
DBusMessageIter subIter;
dbus_message_iter_recurse(&valueIter, &subIter);
if (dbus_message_iter_get_arg_type(&subIter) == DBUS_TYPE_STRING) {
const char* val = nullptr;
dbus_message_iter_get_basic(&subIter, static_cast<void*>(&val));
if (val)
artist = val;
}
}
}
}
}
dbus_message_iter_next(&arrayIter);
}
dbus_message_unref(reply);
dbus_connection_unref(connection);
string result;
if (!artist.empty() && !title.empty())
result = artist + " - " + title;
else if (!title.empty())
result = title;
else if (!artist.empty())
result = artist;
else
result = "";
return result;
}
#pragma clang diagnostic pop
fn GetWindowManager() -> string {
// Check environment variables first
const char* xdgSessionType = getenv("XDG_SESSION_TYPE");
const char* waylandDisplay = getenv("WAYLAND_DISPLAY");
// Prefer Wayland detection if Wayland session
if ((waylandDisplay != nullptr) || (xdgSessionType && string_view(xdgSessionType).contains("wayland"))) {
string compositor = GetWaylandCompositor();
if (!compositor.empty())
return compositor;
// Fallback environment check
const char* xdgCurrentDesktop = getenv("XDG_CURRENT_DESKTOP");
if (xdgCurrentDesktop) {
string desktop(xdgCurrentDesktop);
transform(compositor, compositor.begin(), ::tolower);
if (desktop.contains("hyprland"))
return "hyprland";
}
}
// X11 detection
string x11wm = GetX11WindowManager();
if (!x11wm.empty())
return x11wm;
return "Unknown";
}
fn GetDesktopEnvironment() -> optional<string> {
// Try environment variables first
if (auto desktopEnvironment = DetectFromEnvVars(); desktopEnvironment.has_value())
return desktopEnvironment;
// Try session files next
if (auto desktopEnvironment = DetectFromSessionFiles(); desktopEnvironment.has_value())
return desktopEnvironment;
// Fallback to process detection
return DetectFromProcesses();
}
fn GetShell() -> string {
const char* shell = getenv("SHELL");
return shell ? shell : "";
}
fn GetHost() -> string {
constexpr const char* path = "/sys/class/dmi/id/product_family";
ifstream file(path);
if (!file.is_open()) {
ERROR_LOG("Failed to open {}", path);
return "";
}
string productFamily;
if (!getline(file, productFamily)) {
ERROR_LOG("Failed to read from {}", path);
return "";
}
return productFamily;
}
fn GetKernelVersion() -> string {
struct utsname uts;
if (uname(&uts) == -1) {
ERROR_LOG("uname() failed: {}", strerror(errno));
return "";
}
return static_cast<const char*>(uts.release);
}
#endif

View file

@ -1,3 +0,0 @@
#include "src/util/macros.h"
extern "C" fn issetugid() -> usize { return 0; } // NOLINT

View file

@ -1,9 +0,0 @@
#include "src/os/linux/pkg_count.h"
namespace fs = std::filesystem;
fn GetApkPackageCount() -> optional<usize> {
fs::path apkDbPath("/lib/apk/db/installed");
return std::nullopt;
}

View file

@ -1,38 +0,0 @@
#pragma once
#include "src/util/macros.h"
using std::optional;
// Get package count from dpkg (Debian/Ubuntu)
fn GetDpkgPackageCount() -> optional<usize>;
// Get package count from RPM (Red Hat/Fedora/CentOS)
fn GetRpmPackageCount() -> std::optional<usize>;
// Get package count from pacman (Arch Linux)
fn GetPacmanPackageCount() -> std::optional<usize>;
// Get package count from Portage (Gentoo)
fn GetPortagePackageCount() -> std::optional<usize>;
// Get package count from zypper (openSUSE)
fn GetZypperPackageCount() -> std::optional<usize>;
// Get package count from apk (Alpine)
fn GetApkPackageCount() -> std::optional<usize>;
// Get package count from nix
fn GetNixPackageCount() -> std::optional<usize>;
// Get package count from flatpak
fn GetFlatpakPackageCount() -> std::optional<usize>;
// Get package count from snap
fn GetSnapPackageCount() -> std::optional<usize>;
// Get package count from AppImage
fn GetAppimagePackageCount() -> std::optional<usize>;
// Get total package count from all available package managers
fn GetTotalPackageCount() -> std::optional<usize>;

View file

@ -1,197 +0,0 @@
#ifdef __APPLE__
#include <expected>
#include <map>
#include <sys/sysctl.h>
#include "macos/bridge.h"
#include "os.h"
fn GetMemInfo() -> expected<u64, string> {
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 mem;
}
fn GetNowPlaying() -> expected<string, NowPlayingError> { return GetCurrentPlayingInfo(); }
fn GetOSVersion() -> expected<string, string> { return GetMacOSVersion(); }
fn GetDesktopEnvironment() -> optional<string> { return std::nullopt; }
fn GetWindowManager() -> string { return "Yabai"; }
fn GetKernelVersion() -> string {
std::array<char, 256> kernelVersion;
usize kernelVersionLen = sizeof(kernelVersion);
sysctlbyname("kern.osrelease", kernelVersion.data(), &kernelVersionLen, nullptr, 0);
return kernelVersion.data();
}
fn GetHost() -> string {
std::array<char, 256> 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<std::string, std::string> modelNameByHwModel = {
// MacBook Pro
{ "MacBookPro18,3", "MacBook Pro (14-inch, 2021)" },
{ "MacBookPro18,4", "MacBook Pro (14-inch, 2021)" },
{ "MacBookPro18,1", "MacBook Pro (16-inch, 2021)" },
{ "MacBookPro18,2", "MacBook Pro (16-inch, 2021)" },
{ "MacBookPro17,1", "MacBook Pro (13-inch, M1, 2020)" },
{ "MacBookPro16,3", "MacBook Pro (13-inch, 2020)" },
{ "MacBookPro16,2", "MacBook Pro (13-inch, 2020)" },
{ "MacBookPro16,4", "MacBook Pro (16-inch, 2019)" },
{ "MacBookPro16,1", "MacBook Pro (16-inch, 2019)" },
{ "MacBookPro15,4", "MacBook Pro (13-inch, 2019)" },
{ "MacBookPro15,3", "MacBook Pro (15-inch, 2019)" },
{ "MacBookPro15,2", "MacBook Pro (13-inch, 2018/2019)" },
{ "MacBookPro15,1", "MacBook Pro (15-inch, 2018/2019)" },
{ "MacBookPro14,3", "MacBook Pro (15-inch, 2017)" },
{ "MacBookPro14,2", "MacBook Pro (13-inch, 2017)" },
{ "MacBookPro14,1", "MacBook Pro (13-inch, 2017)" },
{ "MacBookPro13,3", "MacBook Pro (15-inch, 2016)" },
{ "MacBookPro13,2", "MacBook Pro (13-inch, 2016)" },
{ "MacBookPro13,1", "MacBook Pro (13-inch, 2016)" },
{ "MacBookPro12,1", "MacBook Pro (13-inch, 2015)" },
{ "MacBookPro11,4", "MacBook Pro (15-inch, 2015)" },
{ "MacBookPro11,5", "MacBook Pro (15-inch, 2015)" },
{ "MacBookPro11,2", "MacBook Pro (15-inch, 2013/2014)" },
{ "MacBookPro11,3", "MacBook Pro (15-inch, 2013/2014)" },
{ "MacBookPro11,1", "MacBook Pro (13-inch, 2013/2014)" },
{ "MacBookPro10,2", "MacBook Pro (13-inch, 2012/2013)" },
{ "MacBookPro10,1", "MacBook Pro (15-inch, 2012/2013)" },
{ "MacBookPro9,2", "MacBook Pro (13-inch, 2012)" },
{ "MacBookPro9,1", "MacBook Pro (15-inch, 2012)" },
{ "MacBookPro8,3", "MacBook Pro (17-inch, 2011)" },
{ "MacBookPro8,2", "MacBook Pro (15-inch, 2011)" },
{ "MacBookPro8,1", "MacBook Pro (13-inch, 2011)" },
{ "MacBookPro7,1", "MacBook Pro (13-inch, 2010)" },
{ "MacBookPro6,2", "MacBook Pro (15-inch, 2010)" },
{ "MacBookPro6,1", "MacBook Pro (17-inch, 2010)" },
{ "MacBookPro5,5", "MacBook Pro (13-inch, 2009)" },
{ "MacBookPro5,3", "MacBook Pro (15-inch, 2009)" },
{ "MacBookPro5,2", "MacBook Pro (17-inch, 2009)" },
{ "MacBookPro5,1", "MacBook Pro (15-inch, 2008)" },
{ "MacBookPro4,1", "MacBook Pro (17/15-inch, 2008)" },
// MacBook Air
{ "MacBookAir10,1", "MacBook Air (M1, 2020)" },
{ "MacBookAir9,1", "MacBook Air (13-inch, 2020)" },
{ "MacBookAir8,2", "MacBook Air (13-inch, 2019)" },
{ "MacBookAir8,1", "MacBook Air (13-inch, 2018)" },
{ "MacBookAir7,2", "MacBook Air (13-inch, 2015/2017)" },
{ "MacBookAir7,1", "MacBook Air (11-inch, 2015)" },
{ "MacBookAir6,2", "MacBook Air (13-inch, 2013/2014)" },
{ "MacBookAir6,1", "MacBook Air (11-inch, 2013/2014)" },
{ "MacBookAir5,2", "MacBook Air (13-inch, 2012)" },
{ "MacBookAir5,1", "MacBook Air (11-inch, 2012)" },
{ "MacBookAir4,2", "MacBook Air (13-inch, 2011)" },
{ "MacBookAir4,1", "MacBook Air (11-inch, 2011)" },
{ "MacBookAir3,2", "MacBook Air (13-inch, 2010)" },
{ "MacBookAir3,1", "MacBook Air (11-inch, 2010)" },
{ "MacBookAir2,1", "MacBook Air (2009)" },
// Mac mini
{ "Macmini9,1", "Mac mini (M1, 2020)" },
{ "Macmini8,1", "Mac mini (2018)" },
{ "Macmini7,1", "Mac mini (2014)" },
{ "Macmini6,1", "Mac mini (2012)" },
{ "Macmini6,2", "Mac mini (2012)" },
{ "Macmini5,1", "Mac mini (2011)" },
{ "Macmini5,2", "Mac mini (2011)" },
{ "Macmini4,1", "Mac mini (2010)" },
{ "Macmini3,1", "Mac mini (2009)" },
// MacBook
{ "MacBook10,1", "MacBook (12-inch, 2017)" },
{ "MacBook9,1", "MacBook (12-inch, 2016)" },
{ "MacBook8,1", "MacBook (12-inch, 2015)" },
{ "MacBook7,1", "MacBook (13-inch, 2010)" },
{ "MacBook6,1", "MacBook (13-inch, 2009)" },
{ "MacBook5,2", "MacBook (13-inch, 2009)" },
// Mac Pro
{ "MacPro7,1", "Mac Pro (2019)" },
{ "MacPro6,1", "Mac Pro (2013)" },
{ "MacPro5,1", "Mac Pro (2010 - 2012)" },
{ "MacPro4,1", "Mac Pro (2009)" },
// Mac (Generic)
{ "Mac16,3", "iMac (24-inch, 2024)" },
{ "Mac16,2", "iMac (24-inch, 2024)" },
{ "Mac16,1", "MacBook Pro (14-inch, 2024)" },
{ "Mac16,6", "MacBook Pro (14-inch, 2024)" },
{ "Mac16,8", "MacBook Pro (14-inch, 2024)" },
{ "Mac16,7", "MacBook Pro (16-inch, 2024)" },
{ "Mac16,5", "MacBook Pro (16-inch, 2024)" },
{ "Mac16,15", "Mac mini (2024)" },
{ "Mac16,10", "Mac mini (2024)" },
{ "Mac15,13", "MacBook Air (15-inch, M3, 2024)" },
{ "Mac15,2", "MacBook Air (13-inch, M3, 2024)" },
{ "Mac15,3", "MacBook Pro (14-inch, Nov 2023)" },
{ "Mac15,4", "iMac (24-inch, 2023)" },
{ "Mac15,5", "iMac (24-inch, 2023)" },
{ "Mac15,6", "MacBook Pro (14-inch, Nov 2023)" },
{ "Mac15,8", "MacBook Pro (14-inch, Nov 2023)" },
{ "Mac15,10", "MacBook Pro (14-inch, Nov 2023)" },
{ "Mac15,7", "MacBook Pro (16-inch, Nov 2023)" },
{ "Mac15,9", "MacBook Pro (16-inch, Nov 2023)" },
{ "Mac15,11", "MacBook Pro (16-inch, Nov 2023)" },
{ "Mac14,15", "MacBook Air (15-inch, M2, 2023)" },
{ "Mac14,14", "Mac Studio (M2 Ultra, 2023)" },
{ "Mac14,13", "Mac Studio (M2 Max, 2023)" },
{ "Mac14,8", "Mac Pro (2023)" },
{ "Mac14,6", "MacBook Pro (16-inch, 2023)" },
{ "Mac14,10", "MacBook Pro (16-inch, 2023)" },
{ "Mac14,5", "MacBook Pro (14-inch, 2023)" },
{ "Mac14,9", "MacBook Pro (14-inch, 2023)" },
{ "Mac14,3", "Mac mini (M2, 2023)" },
{ "Mac14,12", "Mac mini (M2, 2023)" },
{ "Mac14,7", "MacBook Pro (13-inch, M2, 2022)" },
{ "Mac14,2", "MacBook Air (M2, 2022)" },
{ "Mac13,1", "Mac Studio (M1 Max, 2022)" },
{ "Mac13,2", "Mac Studio (M1 Ultra, 2022)" },
// iMac
{ "iMac21,1", "iMac (24-inch, M1, 2021)" },
{ "iMac21,2", "iMac (24-inch, M1, 2021)" },
{ "iMac20,1", "iMac (27-inch, 2020)" },
{ "iMac20,2", "iMac (27-inch, 2020)" },
{ "iMac19,1", "iMac (27-inch, 2019)" },
{ "iMac19,2", "iMac (21.5-inch, 2019)" },
{ "iMacPro1,1", "iMac Pro (2017)" },
{ "iMac18,3", "iMac (27-inch, 2017)" },
{ "iMac18,2", "iMac (21.5-inch, 2017)" },
{ "iMac18,1", "iMac (21.5-inch, 2017)" },
{ "iMac17,1", "iMac (27-inch, 2015)" },
{ "iMac16,2", "iMac (21.5-inch, 2015)" },
{ "iMac16,1", "iMac (21.5-inch, 2015)" },
{ "iMac15,1", "iMac (27-inch, 2014/2015)" },
{ "iMac14,4", "iMac (21.5-inch, 2014)" },
{ "iMac14,2", "iMac (27-inch, 2013)" },
{ "iMac14,1", "iMac (21.5-inch, 2013)" },
{ "iMac13,2", "iMac (27-inch, 2012)" },
{ "iMac13,1", "iMac (21.5-inch, 2012)" },
{ "iMac12,2", "iMac (27-inch, 2011)" },
{ "iMac12,1", "iMac (21.5-inch, 2011)" },
{ "iMac11,3", "iMac (27-inch, 2010)" },
{ "iMac11,2", "iMac (21.5-inch, 2010)" },
{ "iMac10,1", "iMac (27/21.5-inch, 2009)" },
{ "iMac9,1", "iMac (24/20-inch, 2009)" },
};
return modelNameByHwModel[hwModel.data()];
}
#endif

View file

@ -1,27 +0,0 @@
#pragma once
#ifdef __APPLE__
#include <expected>
#include <string>
#include "../../util/macros.h"
#ifdef __OBJC__
#import <Foundation/Foundation.h>
@interface Bridge : NSObject
+ (void)fetchCurrentPlayingMetadata:(void (^)(std::expected<NSDictionary*, const char*>))completion;
+ (std::expected<string, string>)macOSVersion;
@end
#else
extern "C++" {
fn GetCurrentPlayingInfo() -> std::expected<std::string, NowPlayingError>;
fn GetMacOSVersion() -> std::expected<std::string, const char*>;
}
#endif
#endif

View file

@ -1,121 +0,0 @@
#ifdef __APPLE__
#import <dispatch/dispatch.h>
#include <expected>
#import <objc/runtime.h>
#include <string>
#import "bridge.h"
using MRMediaRemoteGetNowPlayingInfoFunction =
void (*)(dispatch_queue_t queue, void (^handler)(NSDictionary* information));
@implementation Bridge
+ (void)fetchCurrentPlayingMetadata:(void (^)(std::expected<NSDictionary*, const char*>))completion {
CFURLRef ref = CFURLCreateWithFileSystemPath(
kCFAllocatorDefault, CFSTR("/System/Library/PrivateFrameworks/MediaRemote.framework"), kCFURLPOSIXPathStyle, false
);
if (!ref) {
completion(std::unexpected("Failed to create CFURL for MediaRemote framework"));
return;
}
CFBundleRef bundle = CFBundleCreate(kCFAllocatorDefault, ref);
CFRelease(ref);
if (!bundle) {
completion(std::unexpected("Failed to create bundle for MediaRemote framework"));
return;
}
auto mrMediaRemoteGetNowPlayingInfo = std::bit_cast<MRMediaRemoteGetNowPlayingInfoFunction>(
CFBundleGetFunctionPointerForName(bundle, CFSTR("MRMediaRemoteGetNowPlayingInfo"))
);
if (!mrMediaRemoteGetNowPlayingInfo) {
CFRelease(bundle);
completion(std::unexpected("Failed to get MRMediaRemoteGetNowPlayingInfo function pointer"));
return;
}
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<NSDictionary*, const char*>(nowPlayingInfo)
: std::unexpected("No now playing information")
);
}
);
}
+ (std::expected<string, string>)macOSVersion {
NSProcessInfo* processInfo = [NSProcessInfo processInfo];
NSOperatingSystemVersion osVersion = [processInfo operatingSystemVersion];
NSString* versionNumber = nil;
if (osVersion.patchVersion == 0) {
versionNumber = [NSString stringWithFormat:@"%ld.%ld", osVersion.majorVersion, osVersion.minorVersion];
} else {
versionNumber = [NSString
stringWithFormat:@"%ld.%ld.%ld", osVersion.majorVersion, osVersion.minorVersion, osVersion.patchVersion];
}
NSDictionary* versionNames =
@{ @11 : @"Big Sur", @12 : @"Monterey", @13 : @"Ventura", @14 : @"Sonoma", @15 : @"Sequoia" };
NSNumber* majorVersion = @(osVersion.majorVersion);
NSString* versionName = versionNames[majorVersion] ? versionNames[majorVersion] : @"Unknown";
NSString* fullVersion = [NSString stringWithFormat:@"macOS %@ %@", versionNumber, versionName];
return std::string([fullVersion UTF8String]);
}
@end
extern "C++" {
// NOLINTBEGIN(misc-use-internal-linkage)
fn GetCurrentPlayingInfo() -> std::expected<std::string, NowPlayingError> {
__block std::expected<std::string, NowPlayingError> result;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[Bridge fetchCurrentPlayingMetadata:^(std::expected<NSDictionary*, const char*> metadataResult) {
if (!metadataResult) {
result = std::unexpected(NowPlayingError { metadataResult.error() });
dispatch_semaphore_signal(semaphore);
return;
}
NSDictionary* metadata = *metadataResult;
if (!metadata) {
result = std::unexpected(NowPlayingError { NowPlayingCode::NoPlayers });
dispatch_semaphore_signal(semaphore);
return;
}
NSString* title = [metadata objectForKey:@"kMRMediaRemoteNowPlayingInfoTitle"];
NSString* artist = [metadata objectForKey:@"kMRMediaRemoteNowPlayingInfoArtist"];
if (!title && !artist)
result = std::unexpected("No metadata");
else if (!title)
result = std::string([artist UTF8String]);
else if (!artist)
result = std::string([title UTF8String]);
else
result = std::string([[NSString stringWithFormat:@"%@ - %@", title, artist] UTF8String]);
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return result;
}
fn GetMacOSVersion() -> std::expected<string, string> { return [Bridge macOSVersion]; }
// NOLINTEND(misc-use-internal-linkage)
}
#endif

View file

@ -1,53 +0,0 @@
#pragma once
#include <expected>
#include "../util/macros.h"
#include "../util/types.h"
using std::optional, std::expected;
/**
* @brief Get the amount of installed RAM in bytes.
*/
fn GetMemInfo() -> expected<u64, string>;
/**
* @brief Get the currently playing song metadata.
*/
fn GetNowPlaying() -> expected<string, NowPlayingError>;
/**
* @brief Get the OS version.
*/
fn GetOSVersion() -> expected<string, string>;
/**
* @brief Get the current desktop environment.
*/
fn GetDesktopEnvironment() -> optional<string>;
/**
* @brief Get the current window manager.
*/
fn GetWindowManager() -> string;
/**
* @brief Get the current shell.
*/
fn GetShell() -> string;
/**
* @brief Get the product family
*/
fn GetHost() -> string;
/**
* @brief Get the kernel version.
*/
fn GetKernelVersion() -> string;
/**
* @brief Get the number of installed packages.
*/
fn GetPackageCount() -> u64;

View file

@ -1,240 +0,0 @@
#ifdef __WIN32__
#include <iostream>
#include <windows.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Media.Control.h>
#include <winrt/base.h>
#include <winrt/impl/Windows.Media.Control.2.h>
// clang-format off
#include <dwmapi.h>
#include <tlhelp32.h>
#include <algorithm>
#include <vector>
#include <cstring>
// clang-format on
#include "os.h"
using RtlGetVersionPtr = NTSTATUS(WINAPI*)(PRTL_OSVERSIONINFOW);
namespace {
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, std::bit_cast<LPBYTE>(value.data()), &dataSize) !=
ERROR_SUCCESS) {
RegCloseKey(key);
return "";
}
RegCloseKey(key);
// Remove null terminator if present
if (!value.empty() && value.back() == '\0')
value.pop_back();
return value;
}
// Add these function implementations
fn GetRunningProcesses() -> std::vector<string> {
std::vector<string> processes;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
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 {
return std::ranges::any_of(processes, [&name](const string& proc) {
return _stricmp(proc.c_str(), name.c_str()) == 0;
});
}
}
fn GetMemInfo() -> expected<u64, string> {
u64 mem = 0;
if (!GetPhysicallyInstalledSystemMemory(&mem))
return std::unexpected("Failed to get physical system memory.");
return mem * 1024;
}
fn GetNowPlaying() -> expected<string, NowPlayingError> {
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 MediaProperties mediaProperties = currentSession.TryGetMediaPropertiesAsync().get();
// Convert the hstring title to string
return to_string(mediaProperties.Title());
}
// If we reach this point, there is no current session
return std::unexpected(NowPlayingError { NowPlayingCode::NoActivePlayer });
} catch (const winrt::hresult_error& e) { return std::unexpected(NowPlayingError { e }); }
}
fn GetOSVersion() -> expected<string, 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 std::unexpected("Failed to get OS version.");
}
fn GetHost() -> string {
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;
}
fn GetKernelVersion() -> string {
std::stringstream versionStream;
HMODULE ntdllHandle = GetModuleHandleW(L"ntdll.dll");
if (ntdllHandle) {
auto rtlGetVersion = std::bit_cast<RtlGetVersionPtr>(GetProcAddress(ntdllHandle, "RtlGetVersion"));
if (rtlGetVersion) {
RTL_OSVERSIONINFOW osInfo = {};
osInfo.dwOSVersionInfoSize = sizeof(osInfo);
if (rtlGetVersion(&osInfo) == 0)
versionStream << osInfo.dwMajorVersion << "." << osInfo.dwMinorVersion << "." << osInfo.dwBuildNumber << "."
<< osInfo.dwPlatformId;
}
}
return versionStream.str();
}
fn GetWindowManager() -> string {
const std::vector<string> processes = GetRunningProcesses();
string windowManager;
// Check for third-party WMs
if (IsProcessRunning(processes, "glazewm.exe")) {
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
if (windowManager.empty()) {
BOOL compositionEnabled = FALSE;
if (SUCCEEDED(DwmIsCompositionEnabled(&compositionEnabled))) {
windowManager = compositionEnabled ? "Desktop Window Manager" : "Windows Manager (Basic)";
} else {
windowManager = "Windows Manager";
}
}
return windowManager;
}
fn GetDesktopEnvironment() -> optional<string> {
// Get version information from registry
const string buildStr =
GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "CurrentBuildNumber");
if (buildStr.empty())
return std::nullopt;
try {
const i32 build = stoi(buildStr);
// Windows 11+ (Fluent)
if (build >= 22000)
return "Fluent (Windows 11)";
// Windows 10 Fluent Era
if (build >= 15063)
return "Fluent (Windows 10)";
// Windows 8.1/10 Metro Era
if (build >= 9200) { // Windows 8+
// Distinguish between Windows 8 and 10
const string productName =
GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "ProductName");
if (productName.find("Windows 10") != string::npos)
return "Metro (Windows 10)";
if (build >= 9600)
return "Metro (Windows 8.1)";
return "Metro (Windows 8)";
}
// Windows 7 Aero
if (build >= 7600)
return "Aero (Windows 7)";
// Older versions
return "Classic";
} catch (...) { return std::nullopt; }
}
#endif

View file

@ -1,71 +0,0 @@
#pragma once
// probably stupid but it fixes the issue with windows.h defining ERROR
#undef ERROR
#include <filesystem>
#include <fmt/chrono.h>
#include <fmt/color.h>
#include <fmt/format.h>
#include <source_location>
#include "types.h"
#define fn auto // Rust-style function shorthand
namespace log_colors {
using fmt::terminal_color;
constexpr fmt::terminal_color debug = terminal_color::cyan, info = terminal_color::green,
warn = terminal_color::yellow, error = terminal_color::red,
timestamp = terminal_color::bright_white, file_info = terminal_color::bright_white;
}
enum class LogLevel : u8 { DEBUG, INFO, WARN, ERROR };
template <typename... Args>
void LogImpl(LogLevel level, const std::source_location& loc, fmt::format_string<Args...> fmt, Args&&... args) {
const time_t now = std::time(nullptr);
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");
}
}();
const string filename = std::filesystem::path(loc.file_name()).lexically_normal().string();
const struct tm time = *std::localtime(&now);
// Timestamp and level
fmt::print(fg(log_colors::timestamp), "[{:%H:%M:%S}] ", time);
fmt::print(fmt::emphasis::bold | fg(color), "{} ", levelStr);
// Message
fmt::print(fmt, std::forward<Args>(args)...);
// File info (debug builds only)
#ifndef NDEBUG
fmt::print(fg(log_colors::file_info), "\n{:>14} ", "╰──");
fmt::print(fmt::emphasis::italic | fg(log_colors::file_info), "{}:{}", filename, loc.line());
#endif
fmt::print("\n");
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-macros"
#ifdef NDEBUG
#define DEBUG_LOG(...) static_cast<void>(0)
#else
#define DEBUG_LOG(...) LogImpl(LogLevel::DEBUG, std::source_location::current(), __VA_ARGS__)
#endif
#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__)
#pragma clang diagnostic pop

View file

@ -1,176 +0,0 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <string>
#ifdef __WIN32__
#include <winrt/base.h>
#endif
#ifdef __APPLE__
#include <variant>
#endif
/**
* @typedef u8
* @brief Represents an 8-bit unsigned integer.
*
* This type alias is used for 8-bit unsigned integers, ranging from 0 to 255.
* It is based on the std::uint8_t type.
*/
using u8 = std::uint8_t;
/**
* @typedef u16
* @brief Represents a 16-bit unsigned integer.
*
* This type alias is used for 16-bit unsigned integers, ranging from 0 to 65,535.
* It is based on the std::uint16_t type.
*/
using u16 = std::uint16_t;
/**
* @typedef u32
* @brief Represents a 32-bit unsigned integer.
*
* This type alias is used for 32-bit unsigned integers, ranging from 0 to 4,294,967,295.
* It is based on the std::uint32_t type.
*/
using u32 = std::uint32_t;
/**
* @typedef u64
* @brief Represents a 64-bit unsigned integer.
*
* This type alias is used for 64-bit unsigned integers, ranging from 0 to
* 18,446,744,073,709,551,615. It is based on the std::uint64_t type.
*/
using u64 = std::uint64_t;
// Type Aliases for Signed Integers
/**
* @typedef i8
* @brief Represents an 8-bit signed integer.
*
* This type alias is used for 8-bit signed integers, ranging from -128 to 127.
* It is based on the std::int8_t type.
*/
using i8 = std::int8_t;
/**
* @typedef i16
* @brief Represents a 16-bit signed integer.
*
* This type alias is used for 16-bit signed integers, ranging from -32,768 to 32,767.
* It is based on the std::int16_t type.
*/
using i16 = std::int16_t;
/**
* @typedef i32
* @brief Represents a 32-bit signed integer.
*
* This type alias is used for 32-bit signed integers, ranging from -2,147,483,648 to 2,147,483,647.
* It is based on the std::int32_t type.
*/
using i32 = std::int32_t;
/**
* @typedef i64
* @brief Represents a 64-bit signed integer.
*
* This type alias is used for 64-bit signed integers, ranging from -9,223,372,036,854,775,808 to
* 9,223,372,036,854,775,807. It is based on the std::int64_t type.
*/
using i64 = std::int64_t;
// Type Aliases for Floating-Point Numbers
/**
* @typedef f32
* @brief Represents a 32-bit floating-point number.
*
* This type alias is used for 32-bit floating-point numbers, which follow the IEEE 754 standard.
* It is based on the float type.
*/
using f32 = float;
/**
* @typedef f64
* @brief Represents a 64-bit floating-point number.
*
* This type alias is used for 64-bit floating-point numbers, which follow the IEEE 754 standard.
* It is based on the double type.
*/
using f64 = double;
// Type Aliases for Size Types
/**
* @typedef usize
* @brief Represents an unsigned size type.
*
* This type alias is used for representing the size of objects in bytes.
* It is based on the std::size_t type, which is the result type of the sizeof operator.
*/
using usize = std::size_t;
/**
* @typedef isize
* @brief Represents a signed size type.
*
* This type alias is used for representing pointer differences.
* It is based on the std::ptrdiff_t type, which is the signed integer type returned when
* subtracting two pointers.
*/
using isize = std::ptrdiff_t;
/**
* @typedef string
* @brief Represents a string.
*/
using string = std::string;
/**
* @enum NowPlayingCode
* @brief Represents error codes for Now Playing functionality.
*/
enum class NowPlayingCode : u8 {
NoPlayers,
NoActivePlayer,
};
// Platform-specific error details
#ifdef __linux__
/**
* @typedef LinuxError
* @brief Represents a Linux-specific error.
*/
using LinuxError = std::string;
#elif defined(__APPLE__)
/**
* @typedef MacError
* @brief Represents a macOS-specific error.
*/
using MacError = std::string;
#elif defined(__WIN32__)
/**
* @typedef WindowsError
* @brief Represents a Windows-specific error.
*/
using WindowsError = winrt::hresult_error;
#endif
// Unified error type
using NowPlayingError = std::variant<
NowPlayingCode,
#ifdef __linux__
LinuxError
#elif defined(__APPLE__)
MacError
#elif defined(__WIN32__)
WindowsError
#endif
>;

View file

@ -1,13 +0,0 @@
[wrap-file]
directory = curl-8.10.1
source_url = https://github.com/curl/curl/releases/download/curl-8_10_1/curl-8.10.1.tar.xz
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/curl_8.10.1-1/curl-8.10.1.tar.xz
source_filename = curl-8.10.1.tar.xz
source_hash = 73a4b0e99596a09fa5924a4fb7e4b995a85fda0d18a2c02ab9cf134bebce04ee
patch_filename = curl_8.10.1-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/curl_8.10.1-1/get_patch
patch_hash = 707c28f35fc9b0e8d68c0c2800712007612f922a31da9637ce706a2159f3ddd8
wrapdb_version = 8.10.1-1
[provide]
dependency_names = libcurl

View file

@ -1,13 +0,0 @@
[wrap-file]
directory = fmt-11.1.1
source_url = https://github.com/fmtlib/fmt/archive/11.1.1.tar.gz
source_filename = fmt-11.1.1.tar.gz
source_hash = 482eed9efbc98388dbaee5cb5f368be5eca4893456bb358c18b7ff71f835ae43
patch_filename = fmt_11.1.1-2_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/fmt_11.1.1-2/get_patch
patch_hash = eee2e90d5d43061a0a1f0b9f8eb188c5b8820ef3e1b15e4b8a4eb791ef82b325
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/fmt_11.1.1-2/fmt-11.1.1.tar.gz
wrapdb_version = 11.1.1-2
[provide]
fmt = fmt_dep

View file

@ -1,15 +0,0 @@
[wrap-file]
directory = FTXUI-5.0.0
source_url = https://github.com/ArthurSonzogni/FTXUI/archive/refs/tags/v5.0.0.tar.gz
source_filename = FTXUI-5.0.0.tar.gz
source_hash = a2991cb222c944aee14397965d9f6b050245da849d8c5da7c72d112de2786b5b
patch_filename = ftxui_5.0.0-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/ftxui_5.0.0-1/get_patch
patch_hash = 21c654e82739b90b95bd98c1d321264608d37c50d29fbcc3487f790fd5412909
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/ftxui_5.0.0-1/FTXUI-5.0.0.tar.gz
wrapdb_version = 5.0.0-1
[provide]
ftxui-screen = screen_dep
ftxui-dom = dom_dep
ftxui-component = component_dep

View file

@ -1,15 +0,0 @@
[wrap-file]
directory = openssl-3.0.8
source_url = https://www.openssl.org/source/openssl-3.0.8.tar.gz
source_filename = openssl-3.0.8.tar.gz
source_hash = 6c13d2bf38fdf31eac3ce2a347073673f5d63263398f1f69d0df4a41253e4b3e
patch_filename = openssl_3.0.8-3_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/openssl_3.0.8-3/get_patch
patch_hash = 300da189e106942347d61a4a4295aa2edbcf06184f8d13b4cee0bed9fb936963
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/openssl_3.0.8-3/openssl-3.0.8.tar.gz
wrapdb_version = 3.0.8-3
[provide]
libcrypto = libcrypto_dep
libssl = libssl_dep
openssl = openssl_dep

@ -1 +0,0 @@
Subproject commit 33eac03e0d2e87d778e7a8d7ad1f0e1c40b581a5

@ -1 +0,0 @@
Subproject commit 48ea775531523ac290abec91107c628f00156543

View file

@ -1,2 +0,0 @@
[wrap-redirect]
filename = SQLiteCpp-3.3.2/subprojects/sqlite3.wrap

View file

@ -1,10 +0,0 @@
[wrap-file]
directory = SQLiteCpp-3.3.2
source_url = https://github.com/SRombauts/SQLiteCpp/archive/refs/tags/3.3.2.zip
source_filename = sqlitecpp-3.3.2.zip
source_hash = 1f41ef7322da573fdfca95655bd1329282638b4d9d3dc16a48f4bad16008eda8
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/sqlitecpp_3.3.2-1/sqlitecpp-3.3.2.zip
wrapdb_version = 3.3.2-1
[provide]
sqlitecpp = sqlitecpp_dep

View file

@ -1,10 +0,0 @@
[wrap-file]
directory = tomlplusplus-3.4.0
source_url = https://github.com/marzer/tomlplusplus/archive/v3.4.0.tar.gz
source_filename = tomlplusplus-3.4.0.tar.gz
source_hash = 8517f65938a4faae9ccf8ebb36631a38c1cadfb5efa85d9a72e15b9e97d25155
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/tomlplusplus_3.4.0-1/tomlplusplus-3.4.0.tar.gz
wrapdb_version = 3.4.0-1
[provide]
dependency_names = tomlplusplus

23
util.h Normal file
View file

@ -0,0 +1,23 @@
#pragma once
#include <cstdint>
// Unsigned integers
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
// Signed integers
using i8 = std::int8_t;
using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
// Floating-points
using f32 = float;
using f64 = double;
// Size types
using usize = std::size_t;
using isize = std::ptrdiff_t;

12
vcpkg.json Normal file
View file

@ -0,0 +1,12 @@
{
"name" : "draconispp",
"version-string" : "1.0.0",
"builtin-baseline" : "14b91796a68c87bc8d5cb35911b39287ccb7bd95",
"dependencies" : [ {
"name" : "fmt",
"version>=" : "10.2.1#2"
}, {
"name" : "cppwinrt",
"version>=" : "2.0.240111.5"
} ]
}