Compare commits

...

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

30 changed files with 273 additions and 2434 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: 100
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,29 +1,25 @@
# noinspection SpellCheckingInspection
Checks: >
*,
-ctad-maybe-unsupported,
-abseil-*,
-altera-*,
-bugprone-easily-swappable-parameters,
-bugprone-implicit-widening-of-multiplication-result,
-cert-env33-c,
-concurrency-mt-unsafe,
-cppcoreguidelines-avoid-magic-numbers,
-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-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

25
.gitignore vendored
View file

@ -1,25 +1,2 @@
.cache/
.cmake/
.direnv/
.idea/
.vs/
.vscode/
.xmake/
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/
cmake-build-*/

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,122 +0,0 @@
{
"curl": {
"cargoLocks": null,
"date": null,
"extract": null,
"name": "curl",
"passthru": null,
"pinned": false,
"src": {
"deepClone": false,
"fetchSubmodules": false,
"leaveDotGit": false,
"name": null,
"owner": "curl",
"repo": "curl",
"rev": "curl-8_9_1",
"sha256": "sha256-b+Giv6n3LCjOIvUeQawR9qrMBTuz8emRRdhbb2XO5ZU=",
"type": "github"
},
"version": "curl-8_9_1"
},
"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.0.2",
"sha256": "sha256-IKNt4xUoVi750zBti5iJJcCk3zivTt7nU12RIf8pM+0=",
"type": "github"
},
"version": "11.0.2"
},
"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.13.0",
"sha256": "sha256-dEqdPk5ixnNILxTcdSAOhzP8fzeefMu6pqrL/WgnPlE=",
"type": "github"
},
"version": "v0.13.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.0.0",
"sha256": "sha256-W8V5FRhV3jtERMFrZ4gf30OpIQLYoj2yYGpnYOmH2+g=",
"type": "github"
},
"version": "v2.0.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=",
"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=",
"type": "github"
},
"version": "0.10.0"
}
}

View file

@ -1,70 +0,0 @@
# This file was generated by nvfetcher, please do not modify it manually.
{ fetchgit, fetchurl, fetchFromGitHub, dockerTools }:
{
curl = {
pname = "curl";
version = "curl-8_9_1";
src = fetchFromGitHub {
owner = "curl";
repo = "curl";
rev = "curl-8_9_1";
fetchSubmodules = false;
sha256 = "sha256-b+Giv6n3LCjOIvUeQawR9qrMBTuz8emRRdhbb2XO5ZU=";
};
};
fmt = {
pname = "fmt";
version = "11.0.2";
src = fetchFromGitHub {
owner = "fmtlib";
repo = "fmt";
rev = "11.0.2";
fetchSubmodules = false;
sha256 = "sha256-IKNt4xUoVi750zBti5iJJcCk3zivTt7nU12RIf8pM+0=";
};
};
reflect-cpp = {
pname = "reflect-cpp";
version = "v0.13.0";
src = fetchFromGitHub {
owner = "getml";
repo = "reflect-cpp";
rev = "v0.13.0";
fetchSubmodules = false;
sha256 = "sha256-dEqdPk5ixnNILxTcdSAOhzP8fzeefMu6pqrL/WgnPlE=";
};
};
sdbus-cpp = {
pname = "sdbus-cpp";
version = "v2.0.0";
src = fetchFromGitHub {
owner = "kistler-group";
repo = "sdbus-cpp";
rev = "v2.0.0";
fetchSubmodules = false;
sha256 = "sha256-W8V5FRhV3jtERMFrZ4gf30OpIQLYoj2yYGpnYOmH2+g=";
};
};
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,546 +0,0 @@
{
"nodes": {
"codeium": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1727497393,
"narHash": "sha256-AHL2qezvAAZtOP61cHtnB0RtqPej4LqtGkvXNOAESNw=",
"owner": "jcdickinson",
"repo": "codeium.nvim",
"rev": "17bbefff02be8fd66931f366bd4ed76a76e4a57e",
"type": "github"
},
"original": {
"owner": "jcdickinson",
"repo": "codeium.nvim",
"type": "github"
}
},
"devshell": {
"inputs": {
"nixpkgs": [
"nixvim",
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1722113426,
"narHash": "sha256-Yo/3loq572A8Su6aY5GP56knpuKYRvM2a1meP9oJZCw=",
"owner": "numtide",
"repo": "devshell",
"rev": "67cce7359e4cd3c45296fb4aaf6a19e2a9c757ae",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "devshell",
"type": "github"
}
},
"flake-compat": {
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"revCount": 57,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"nixvim",
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1726153070,
"narHash": "sha256-HO4zgY0ekfwO5bX0QH/3kJ/h4KvUDFZg8YpkNwIbg1U=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "bcef6817a8b2aa20a5a6dbb19b43e63c5bf8619a",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1701680307,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1726560853,
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_3": {
"inputs": {
"systems": "systems_3"
},
"locked": {
"lastModified": 1726560853,
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": [
"nixvim",
"nixvim",
"flake-compat"
],
"gitignore": "gitignore",
"nixpkgs": [
"nixvim",
"nixvim",
"nixpkgs"
],
"nixpkgs-stable": [
"nixvim",
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1727514110,
"narHash": "sha256-0YRcOxJG12VGDFH8iS8pJ0aYQQUAgo/r3ZAL+cSh9nk=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "85f7a7177c678de68224af3402ab8ee1bcee25c8",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"nixvim",
"nixvim",
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
"nixvim",
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1727383923,
"narHash": "sha256-4/vacp3CwdGoPf8U4e/N8OsGYtO09WTcQK5FqYfJbKs=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "ffe2d07e771580a005e675108212597e5b367d2d",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "home-manager",
"type": "github"
}
},
"nix-darwin": {
"inputs": {
"nixpkgs": [
"nixvim",
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1727507295,
"narHash": "sha256-I/FrX1peu4URoj5T5odfuKR2rm4GjYJJpCGF9c0/lDA=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "f2e1c4aa29fc211947c3a7113cba1dd707433b70",
"type": "github"
},
"original": {
"owner": "lnl7",
"repo": "nix-darwin",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1729311994,
"narHash": "sha256-qbXHPupmSGjSJkPmmaOSRd6b2kRgAk+CH/FMRWiYdUM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7e2eb4642f7ca9c4ee2c069d65a48575068b4f57",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1702346276,
"narHash": "sha256-eAQgwIWApFQ40ipeOjVSoK4TEHVd6nbSd9fApiHIw5A=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "cf28ee258fd5f9a52de6b9865cdb93a1f96d09b7",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1727742380,
"narHash": "sha256-5lFzlmiwrl3OHrw7QhZn/ZpZtygzx1UspKOaZqpxXSQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9dc3c339a2aecbd61ab2549248d8b64ba1c28af5",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1727348695,
"narHash": "sha256-J+PeFKSDV+pHL7ukkfpVzCOO7mBSrrpJ3svwBFABbhI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1925c603f17fc89f4c8f6bf6f631a802ad85d784",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_5": {
"locked": {
"lastModified": 1726871744,
"narHash": "sha256-V5LpfdHyQkUF7RfOaDPrZDP+oqz88lTJrMT1+stXNwo=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "a1d92660c6b3b7c26fb883500a80ea9d33321be2",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_6": {
"locked": {
"lastModified": 1726871744,
"narHash": "sha256-V5LpfdHyQkUF7RfOaDPrZDP+oqz88lTJrMT1+stXNwo=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "a1d92660c6b3b7c26fb883500a80ea9d33321be2",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixvim": {
"inputs": {
"codeium": "codeium",
"flake-utils": "flake-utils_2",
"nixpkgs": "nixpkgs_3",
"nixvim": "nixvim_2",
"treefmt-nix": "treefmt-nix_2"
},
"locked": {
"lastModified": 1728515236,
"narHash": "sha256-gYObAkD39KbYdeSxOxPFaVVgfbRPPgG470E8sFMaQDM=",
"owner": "pupbrained",
"repo": "nvim-config",
"rev": "584a15c6dcf5bea4a9ab3c804b86324b128d3b67",
"type": "github"
},
"original": {
"owner": "pupbrained",
"repo": "nvim-config",
"type": "github"
}
},
"nixvim_2": {
"inputs": {
"devshell": "devshell",
"flake-compat": "flake-compat",
"flake-parts": "flake-parts",
"git-hooks": "git-hooks",
"home-manager": "home-manager",
"nix-darwin": "nix-darwin",
"nixpkgs": "nixpkgs_4",
"nuschtosSearch": "nuschtosSearch",
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1727645871,
"narHash": "sha256-Os3PAThU5XliKkKa+SHsFyV/EsCHogHcYONmpzb6500=",
"owner": "nix-community",
"repo": "nixvim",
"rev": "5f4a4b47597d3b9ac26c41ff4e8da28fa662f200",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixvim",
"type": "github"
}
},
"nuschtosSearch": {
"inputs": {
"flake-utils": "flake-utils_3",
"nixpkgs": [
"nixvim",
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1727452028,
"narHash": "sha256-ehl/A4HQFRyqj1Fk7cl+dgSf/2Fb1jLwWJtZaMU6RfU=",
"owner": "NuschtOS",
"repo": "search",
"rev": "9f7426e532ef8dfc839c4a3fcc567b13a20a70d3",
"type": "github"
},
"original": {
"owner": "NuschtOS",
"repo": "search",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"nixvim": "nixvim",
"treefmt-nix": "treefmt-nix_3",
"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"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_3": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_4": {
"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": [
"nixvim",
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1727431250,
"narHash": "sha256-uGRlRT47ecicF9iLD1G3g43jn2e+b5KaMptb59LHnvM=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "879b29ae9a0378904fbbefe0dadaed43c8905754",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"treefmt-nix_2": {
"inputs": {
"nixpkgs": "nixpkgs_5"
},
"locked": {
"lastModified": 1727431250,
"narHash": "sha256-uGRlRT47ecicF9iLD1G3g43jn2e+b5KaMptb59LHnvM=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "879b29ae9a0378904fbbefe0dadaed43c8905754",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"treefmt-nix_3": {
"inputs": {
"nixpkgs": "nixpkgs_6"
},
"locked": {
"lastModified": 1729242555,
"narHash": "sha256-6jWSWxv2crIXmYSEb3LEVsFkCkyVHNllk61X4uhqfCs=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "d986489c1c757f6921a48c1439f19bfb9b8ecab5",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems_4"
},
"locked": {
"lastModified": 1726560853,
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

151
flake.nix
View file

@ -1,151 +0,0 @@
{
description = "C/C++ environment";
inputs = {
nixvim.url = "github:pupbrained/nvim-config";
nixpkgs.url = "github:NixOS/nixpkgs";
treefmt-nix.url = "github:numtide/treefmt-nix";
utils.url = "github:numtide/flake-utils";
};
outputs = {
self,
nixpkgs,
treefmt-nix,
utils,
nixvim,
...
}:
utils.lib.eachDefaultSystem (
system: let
pkgs = import nixpkgs {inherit system;};
stdenv =
if pkgs.hostPlatform.isLinux
then pkgs.stdenvAdapters.useMoldLinker pkgs.llvmPackages_18.stdenv
else pkgs.llvmPackages_18.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 = mkPkg "tomlplusplus";
yyjson = mkPkg "yyjson";
sdbus-cpp = pkgs.sdbus-cpp.overrideAttrs {
inherit (sources.sdbus-cpp) pname version src;
};
reflect-cpp = stdenv.mkDerivation {
inherit (sources.reflect-cpp) pname version src;
nativeBuildInputs = with pkgs; [cmake ninja pkg-config];
cmakeFlags = [
"-DCMAKE_TOOLCHAIN_FILE=OFF"
"-DCMAKE_BUILD_TYPE=Release"
"-DREFLECTCPP_TOML=ON"
];
};
deps = with pkgs.pkgsStatic;
[
curl
fmt
libiconv
tomlplusplus
yyjson
reflect-cpp
]
++ linuxPkgs
++ darwinPkgs;
linuxPkgs = nixpkgs.lib.optionals stdenv.isLinux (with pkgs; [
pkgsStatic.glib
systemdLibs
sdbus-cpp
valgrind
]);
darwinPkgs = nixpkgs.lib.optionals stdenv.isDarwin (with pkgs.pkgsStatic.darwin.apple_sdk.frameworks; [
Foundation
MediaPlayer
SystemConfiguration
]);
in
with pkgs; {
packages = rec {
draconisplusplus = stdenv.mkDerivation {
name = "draconis++";
version = "0.1.0";
src = self;
nativeBuildInputs = [
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.clang-tools_18;
};
};
};
devShell = mkShell.override {inherit stdenv;} {
packages =
[
alejandra
bear
clang-tools_18
lldb
meson
ninja
nvfetcher
pkg-config
unzip
nixvim.packages.${system}.default
(writeScriptBin "build" "meson compile -C build")
(writeScriptBin "clean" "meson setup build --wipe")
(writeScriptBin "run" "meson compile -C build && build/draconis++")
]
++ 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,117 +0,0 @@
project(
'draconis++', 'cpp',
version: '0.1.0',
default_options: [
'cpp_std=c++20',
'default_library=static',
'warning_level=everything',
'buildtype=release'
]
)
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([
'-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',
'-fvisibility=hidden'
]),
language: 'objcpp'
)
endif
common_cpp_args = [
'-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',
'-fvisibility=hidden'
]
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']
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'),
dependency('libcurl'),
dependency('tomlplusplus'),
dependency('yyjson')
]
if host_machine.system() == 'darwin'
deps += dependency('Foundation')
deps += dependency('MediaPlayer')
deps += dependency('SystemConfiguration')
deps += dependency('iconv')
elif host_machine.system() == 'linux' or host_machine.system() == 'freebsd'
deps += dependency('sdbus-c++')
deps += dependency('x11')
endif
objc_args = []
link_args = []
if host_machine.system() == 'darwin'
objc_args += ['-fobjc-arc']
elif host_machine.system() == 'windows'
windows_sdk_lib_dir = 'C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/x64'
link_args += ['-L' + windows_sdk_lib_dir, '-lwindowsapp', '-static']
endif
executable(
'draconis++',
sources,
objc_args: objc_args,
link_args: link_args,
dependencies: deps
)

View file

@ -1,19 +0,0 @@
[fmt]
src.github = "fmtlib/fmt"
fetch.github = "fmtlib/fmt"
[reflect-cpp]
src.github = "getml/reflect-cpp"
fetch.github = "getml/reflect-cpp"
[sdbus-cpp]
src.github = "kistler-group/sdbus-cpp"
fetch.github = "kistler-group/sdbus-cpp"
[tomlplusplus]
src.github = "marzer/tomlplusplus"
fetch.github = "marzer/tomlplusplus"
[yyjson]
src.github = "ibireme/yyjson"
fetch.github = "ibireme/yyjson"

View file

@ -1,42 +0,0 @@
#include <cstdlib>
#include <filesystem>
#include <fmt/core.h>
#include <stdexcept>
#include <string>
#include "config.h"
using rfl::Result;
namespace fs = std::filesystem;
inline fn GetConfigPath() -> std::string {
#ifdef _WIN32
const char* localAppData = std::getenv("LOCALAPPDATA");
if (!localAppData)
throw std::runtime_error("Environment variable LOCALAPPDATA is not set");
return localAppData;
#else
const char* home = std::getenv("HOME");
if (!home)
throw std::runtime_error("Environment variable HOME is not set");
return std::string(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) {
fmt::println(stderr, "Failed to load config file: {}", result.error()->what());
exit(1);
}
return result.value();
}

View file

@ -1,36 +0,0 @@
#pragma once
#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;
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,154 +0,0 @@
#include <chrono>
#include <curl/curl.h>
#include <fmt/core.h>
#include <rfl/json.hpp>
#include <rfl/json/load.hpp>
#include "config.h"
using rfl::Error;
using rfl::Result;
// Function to read cache from file
fn ReadCacheFromFile() -> Result<WeatherOutput> {
#ifdef __WIN32__
const char* tempPath = getenv("TEMP");
const string path = string(tempPath) + "\\weather_cache.json";
std::ifstream ifs(path);
#else
std::ifstream ifs("/tmp/weather_cache.json");
#endif
if (!ifs.is_open())
return Error("Cache file not found.");
fmt::println("Reading from cache file...");
std::stringstream buf;
buf << ifs.rdbuf();
Result<WeatherOutput> val = rfl::json::read<WeatherOutput>(buf.str());
fmt::println("Successfully read from cache file.");
return val;
}
// Function to write cache to file
fn WriteCacheToFile(const WeatherOutput& data) -> Result<u8> {
fmt::println("Writing to cache file...");
#ifdef __WIN32__
const char* tempPath = getenv("TEMP");
const string path = string(tempPath) + "\\weather_cache.json";
std::ofstream ofs(path);
#else
std::ofstream ofs("/tmp/weather_cache.json");
#endif
if (!ofs.is_open())
return Error("Failed to open cache file for writing.");
ofs << rfl::json::write(data);
fmt::println("Successfully wrote to cache file.");
return 0;
}
fn WriteCallback(void* contents, const usize size, const usize nmemb, string* str) -> usize {
const usize totalSize = size * nmemb;
str->append(static_cast<char*>(contents), totalSize);
return totalSize;
}
// Function to make API request
fn MakeApiRequest(const string& url) -> Result<WeatherOutput> {
fmt::println("Making API request to URL: {}", url);
CURL* curl = curl_easy_init();
string responseBuffer;
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseBuffer);
const CURLcode res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (res != CURLE_OK)
return Error(fmt::format("Failed to perform cURL request: {}", curl_easy_strerror(res)));
fmt::println("Received response from API. Response size: {}", responseBuffer.size());
fmt::println("Response: {}", responseBuffer);
WeatherOutput output = rfl::json::read<WeatherOutput>(responseBuffer).value();
return output; // Return an empty result for now
}
return Error("Failed to initialize cURL.");
}
// Core function to get weather information
fn Weather::getWeatherInfo() const -> WeatherOutput {
using namespace std::chrono;
// Check if cache is valid
if (Result<WeatherOutput> data = ReadCacheFromFile()) {
WeatherOutput dataVal = *data;
if (system_clock::now() - system_clock::time_point(seconds(dataVal.dt)) < minutes(10)) {
fmt::println("Cache is valid. Returning cached data.");
return dataVal;
}
fmt::println("Cache is expired.");
} else {
fmt::println("No valid cache found.");
}
WeatherOutput result;
if (holds_alternative<string>(location)) {
const string city = get<string>(location);
const char* loc = curl_easy_escape(nullptr, city.c_str(), static_cast<int>(city.length()));
fmt::println("City: {}", loc);
const string apiUrl = fmt::format(
"https://api.openweathermap.org/data/2.5/"
"weather?q={}&appid={}&units={}",
loc,
api_key,
units
);
result = MakeApiRequest(apiUrl).value();
} else {
const auto [lat, lon] = get<Coords>(location);
fmt::println("Coordinates: lat = {:.3f}, lon = {:.3f}", lat, lon);
const string apiUrl = fmt::format(
"https://api.openweathermap.org/data/2.5/"
"weather?lat={:.3f}&lon={:.3f}&appid={}&units={}",
lat,
lon,
api_key,
units
);
result = MakeApiRequest(apiUrl).value();
}
// Update the cache with the new data
WriteCacheToFile(result);
fmt::println("Returning new data.");
return result;
}

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<i8, 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", f64> one_hour;
rfl::Rename<"3h", 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,92 +0,0 @@
#include <ctime>
#include <fmt/chrono.h>
#include <fmt/core.h>
#include <fmt/format.h>
#include <string>
#include "config/config.h"
#include "os/os.h"
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 auto format(const BytesToGiB& BTG, FmtCtx& ctx) const -> typename FmtCtx::iterator {
auto out = fmt::formatter<double>::format(static_cast<double>(BTG.value) / GIB, ctx);
*out++ = 'G';
*out++ = 'i';
*out++ = 'B';
return out;
}
};
fn GetDate() -> std::string {
// Get current local time
std::time_t now = std::time(nullptr);
std::tm localTime = *std::localtime(&now);
// 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 == "1" || date == "21" || date == "31")
date += "st";
else if (date == "2" || date == "22")
date += "nd";
else if (date == "3" || date == "23")
date += "rd";
else
date += "th";
return fmt::format("{:%B} {}", localTime, date);
}
fn main() -> i32 {
const Config& config = Config::getInstance();
// Fetching weather information
Weather weather = config.weather.get();
WeatherOutput weatherInfo = weather.getWeatherInfo();
// Fetching OS version
std::string osVersion = GetOSVersion();
// Checking if now playing is enabled
bool nowPlayingEnabled = config.now_playing.get().enabled;
// Fetching current date
std::string date = GetDate();
// Fetching memory info
u64 memInfo = GetMemInfo();
const std::string& name = config.general.get().name.get();
fmt::println("Hello {}!", name);
fmt::println("Today is: {}", date);
fmt::println("Installed RAM: {:.2f}", BytesToGiB(memInfo));
fmt::println("OS: {}", osVersion);
if (weather.enabled)
fmt::println("It is {}°F in {}", std::lround(weatherInfo.main.temp), weatherInfo.name);
if (nowPlayingEnabled) {
const string nowPlaying = GetNowPlaying();
if (!nowPlaying.empty())
fmt::println("{}", nowPlaying);
else
fmt::println("No song playing");
}
return 0;
}

View file

@ -1,116 +0,0 @@
#ifdef __FreeBSD__
#include <fmt/format.h>
#include <fstream>
#include <iostream>
#include <sys/sysctl.h>
#include <sdbus-c++/sdbus-c++.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,135 +0,0 @@
#ifdef __linux__
#include <cstring>
#include <filesystem>
#include <fmt/format.h>
#include <fstream>
#include <sdbus-c++/sdbus-c++.h>
#include <vector>
#include "os.h"
enum SessionType { Wayland, X11, TTY, Unknown };
fn ParseLineAsNumber(const std::string& input) -> u64 {
usize start = input.find_first_of("0123456789");
if (start == std::string::npos)
throw std::runtime_error("No number found in input");
usize end = input.find_first_not_of("0123456789", start);
return std::stoull(input.substr(start, end - start));
}
fn MeminfoParse(const std::filesystem::path& filepath) -> u64 {
std::ifstream input(filepath);
if (!input.is_open())
throw std::runtime_error("Failed to open " + filepath.string());
std::string line;
while (std::getline(input, line))
if (line.starts_with("MemTotal"))
return ParseLineAsNumber(line);
throw std::runtime_error("MemTotal line not found in " + filepath.string());
}
fn GetMemInfo() -> u64 { return MeminfoParse("/proc/meminfo") * 1024; }
fn GetOSVersion() -> std::string {
std::ifstream file("/etc/os-release");
if (!file.is_open())
throw std::runtime_error("Failed to open /etc/os-release");
string line;
const string prefix = "PRETTY_NAME=";
while (std::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;
}
throw std::runtime_error("PRETTY_NAME line not found in /etc/os-release");
}
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,28 +0,0 @@
#ifdef __APPLE__
#include <sys/sysctl.h>
#include "macos/bridge.h"
#include "os.h"
fn GetMemInfo() -> u64 {
u64 mem = 0;
usize size = sizeof(mem);
sysctlbyname("hw.memsize", &mem, &size, nullptr, 0);
return mem;
}
fn GetNowPlaying() -> string {
if (const char* title = GetCurrentPlayingTitle(); const char* artist = GetCurrentPlayingArtist())
return "Now Playing: " + string(artist) + " - " + string(title);
return "No song playing";
}
fn GetOSVersion() -> string { return GetMacOSVersion(); }
fn GetDesktopEnvironment() -> string { return "Aqua"; }
#endif

View file

@ -1,25 +0,0 @@
#pragma once
#ifdef __APPLE__
#include "../../util/macros.h"
#ifdef __OBJC__
#import <Foundation/Foundation.h>
@interface Bridge : NSObject
+ (NSDictionary*)currentPlayingMetadata;
+ (NSString*)macOSVersion;
@end
#else
extern "C" {
fn GetCurrentPlayingTitle() -> const char*;
fn GetCurrentPlayingArtist() -> const char*;
fn GetMacOSVersion() -> const char*;
}
#endif
#endif

View file

@ -1,132 +0,0 @@
#ifdef __APPLE__
#import <dispatch/dispatch.h>
#import <objc/runtime.h>
#import "bridge.h"
using MRMediaRemoteGetNowPlayingInfoFunction =
void (*)(dispatch_queue_t queue, void (^handler)(NSDictionary* information));
@implementation Bridge
+ (NSDictionary*)currentPlayingMetadata {
CFURLRef ref = CFURLCreateWithFileSystemPath(
kCFAllocatorDefault,
CFSTR("/System/Library/PrivateFrameworks/MediaRemote.framework"),
kCFURLPOSIXPathStyle,
false
);
if (!ref) {
NSLog(@"Failed to load MediaRemote framework");
return nil;
}
CFBundleRef bundle = CFBundleCreate(kCFAllocatorDefault, ref);
CFRelease(ref);
if (!bundle) {
NSLog(@"Failed to load MediaRemote framework");
return nil;
}
MRMediaRemoteGetNowPlayingInfoFunction mrMediaRemoteGetNowPlayingInfo =
reinterpret_cast<MRMediaRemoteGetNowPlayingInfoFunction>(
CFBundleGetFunctionPointerForName(bundle, CFSTR("MRMediaRemoteGetNowPlayingInfo"))
);
if (!mrMediaRemoteGetNowPlayingInfo) {
NSLog(@"Failed to get function pointer for MRMediaRemoteGetNowPlayingInfo");
CFRelease(bundle);
return nil;
}
__block NSDictionary* nowPlayingInfo = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
mrMediaRemoteGetNowPlayingInfo(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^(NSDictionary* information) {
nowPlayingInfo = [information copy];
dispatch_semaphore_signal(semaphore);
}
);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
CFRelease(bundle);
return nowPlayingInfo;
}
+ (NSString*)macOSVersion {
NSProcessInfo* processInfo = [NSProcessInfo processInfo];
NSOperatingSystemVersion osVersion = [processInfo operatingSystemVersion];
NSString* version;
if (osVersion.patchVersion == 0) {
version =
[NSString stringWithFormat:@"%ld.%ld", osVersion.majorVersion, osVersion.minorVersion];
} else {
version = [NSString stringWithFormat:@"%ld.%ld.%ld",
osVersion.majorVersion,
osVersion.minorVersion,
osVersion.patchVersion];
}
// Dictionary to map macOS versions to their respective names
NSDictionary<NSNumber*, NSString*>* versionNames =
@{ @11 : @"Big Sur", @12 : @"Monterey", @13 : @"Ventura", @14 : @"Sonoma", @15 : @"Sequoia" };
NSNumber* majorVersionNumber = @(osVersion.majorVersion);
NSString* versionName = versionNames[majorVersionNumber];
if (versionName == nil)
versionName = @"Unknown";
NSString* fullVersion = [NSString stringWithFormat:@"macOS %@ %@", version, versionName];
return fullVersion;
}
@end
extern "C" {
fn GetCurrentPlayingTitle() -> const char* {
NSDictionary* metadata = [Bridge currentPlayingMetadata];
if (metadata == nil)
return nullptr;
NSString* title = [metadata objectForKey:@"kMRMediaRemoteNowPlayingInfoTitle"];
if (title)
return strdup([title UTF8String]);
return nullptr;
}
fn GetCurrentPlayingArtist() -> const char* {
NSDictionary* metadata = [Bridge currentPlayingMetadata];
if (metadata == nil)
return nullptr;
NSString* artist = [metadata objectForKey:@"kMRMediaRemoteNowPlayingInfoArtist"];
if (artist)
return strdup([artist UTF8String]);
return nullptr;
}
fn GetMacOSVersion() -> const char* {
NSString* version = [Bridge macOSVersion];
if (version)
return strdup([version UTF8String]);
return nullptr;
}
}
#endif

View file

@ -1,29 +0,0 @@
#pragma once
#include "../util/macros.h"
#include "../util/types.h"
/**
* @brief Get the amount of installed RAM in bytes.
*/
fn GetMemInfo() -> u64;
/**
* @brief Get the currently playing song metadata.
*/
fn GetNowPlaying() -> string;
/**
* @brief Get the OS version.
*/
fn GetOSVersion() -> string;
/**
* @brief Get the current desktop environment.
*/
fn GetDesktopEnvironment() -> string;
/**
* @brief Get the current window manager.
*/
fn GetWindowManager() -> string;

View file

@ -1,111 +0,0 @@
#ifdef __WIN32__
#include <exception>
#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>
#include "os.h"
fn GetMemInfo() -> u64 {
u64 mem = 0;
GetPhysicallyInstalledSystemMemory(&mem);
return mem * 1024;
}
fn GetNowPlaying() -> string {
using namespace winrt::Windows::Media::Control;
using namespace winrt::Windows::Foundation;
using MediaProperties = GlobalSystemMediaTransportControlsSessionMediaProperties;
using Session = GlobalSystemMediaTransportControlsSession;
using SessionManager = GlobalSystemMediaTransportControlsSessionManager;
try {
// Request the session manager asynchronously
const IAsyncOperation<SessionManager> sessionManagerOp = SessionManager::RequestAsync();
const SessionManager sessionManager = sessionManagerOp.get();
if (const Session currentSession = sessionManager.GetCurrentSession()) {
// Try to get the media properties asynchronously
const 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 "No current media session.";
} catch (...) { return "Failed to get media properties."; }
}
fn GetRegistryValue(const HKEY& hKey, const string& subKey, const string& valueName) -> string {
HKEY key = nullptr;
if (RegOpenKeyExA(hKey, subKey.c_str(), 0, KEY_READ, &key) != ERROR_SUCCESS)
return "";
DWORD dataSize = 0;
if (RegQueryValueExA(key, valueName.c_str(), nullptr, nullptr, nullptr, &dataSize) !=
ERROR_SUCCESS) {
RegCloseKey(key);
return "";
}
string value(dataSize, '\0');
if (RegQueryValueExA(
key,
valueName.c_str(),
nullptr,
nullptr,
reinterpret_cast<LPBYTE>(value.data()), // NOLINT(*-reinterpret-cast)
&dataSize
) != ERROR_SUCCESS) {
RegCloseKey(key);
return "";
}
RegCloseKey(key);
// Remove null terminator if present
if (!value.empty() && value.back() == '\0')
value.pop_back();
return value;
}
fn GetOSVersion() -> string {
string productName = GetRegistryValue(
HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "ProductName"
);
const string displayVersion = GetRegistryValue(
HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "DisplayVersion"
);
const string releaseId = GetRegistryValue(
HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "ReleaseId"
);
// Check for Windows 11
if (const i32 buildNumber = stoi(GetRegistryValue(
HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "CurrentBuildNumber"
));
buildNumber >= 22000 && productName.find("Windows 10") != string::npos)
productName.replace(productName.find("Windows 10"), 10, "Windows 11");
if (!productName.empty()) {
string result = productName;
if (!displayVersion.empty())
result += " " + displayVersion;
else if (!releaseId.empty())
result += " " + releaseId;
return result;
}
return "";
}
#endif

View file

@ -1,32 +0,0 @@
#pragma once
/**
* @brief Allows for rust-style function definitions
*/
#define fn auto
/**
* @brief Allows for easy getter creation
*
* @param class_name The class to use
* @param type Type of the getter
* @param name Name of the getter
*/
#define DEFINE_GETTER(class_name, type, name) \
fn class_name::get##name() const->type { return m_##name; }
/**
* @brief Helper for making reflect-cpp impls
*
* @param struct_name The struct name
* @param lower_name The arg name
* @param ... Values of the class to convert
*/
#define DEF_IMPL(struct_name, ...) \
struct struct_name##Impl { \
__VA_ARGS__; \
\
static fn from_class(const struct_name& instance) noexcept -> struct_name##Impl; \
\
[[nodiscard]] fn to_class() const -> struct_name; \
};

View file

@ -1,218 +0,0 @@
#pragma once
#include <stdexcept>
#include <utility>
#include <variant>
#include "macros.h"
#include "types.h"
/**
* @class Error
* @brief Represents an error with a message.
*
* This class is used to encapsulate error messages that can be returned from functions.
*/
class Error {
public:
/**
* @brief Constructs an Error with a message.
* @param message The error message.
*/
explicit Error(string message) : m_Message(std::move(message)) {}
/**
* @brief Retrieves the error message.
* @return A constant reference to the error message string.
*/
[[nodiscard]] fn message() const -> const string& { return m_Message; }
private:
string m_Message; ///< The error message.
};
// Primary template for Result with a default type of void
/**
* @class Result
* @brief Represents a result that can either be a value or an error.
*
* This is the primary template for Result, which defaults to handling void results.
*/
template <typename T = void>
class Result;
// Specialization for Result<void>
/**
* @class Result<void>
* @brief Specialization of Result for handling void results.
*
* This class is used when a function either succeeds with no value or fails with an error.
*/
template <>
class Result<void> {
public:
/**
* @brief Constructs a successful Result.
*/
Result() : m_Result(std::monostate {}) {}
/**
* @brief Constructs an error Result.
* @param error The error object.
*/
Result(const Error& error) : m_Result(error) {}
/**
* @brief Constructs an error Result.
* @param error An rvalue reference to the error object.
*/
Result(Error&& error) : m_Result(std::move(error)) {}
/**
* @brief Checks if the Result is successful.
* @return True if the Result is successful, otherwise false.
*/
[[nodiscard]] fn isOk() const -> bool { return std::holds_alternative<std::monostate>(m_Result); }
/**
* @brief Checks if the Result contains an error.
* @return True if the Result contains an error, otherwise false.
*/
[[nodiscard]] fn isErr() const -> bool { return std::holds_alternative<Error>(m_Result); }
/**
* @brief Throws an exception if the Result contains an error.
*
* This function should be called only if the Result is successful.
*/
void value() const {
if (isErr()) {
throw std::logic_error("Attempted to access value of an error Result");
}
}
/**
* @brief Retrieves the error object.
* @return A constant reference to the Error object.
* @throws std::logic_error if the Result is successful.
*/
[[nodiscard]] fn error() const -> const Error& {
if (isOk()) {
throw std::logic_error("Attempted to access error of an ok Result");
}
return std::get<Error>(m_Result);
}
private:
std::variant<std::monostate, Error>
m_Result; ///< The underlying result, which can be either void or an Error.
};
// Primary template for Result
/**
* @class Result
* @brief Represents a result that can either be a value of type T or an error.
*
* This template class is used to handle results that can either be a successful value or an error.
* @tparam T The type of the successful value.
*/
template <typename T>
class Result {
public:
/**
* @brief Constructs a successful Result with a value.
* @param value The value of the Result.
*/
Result(const T& value) : m_Result(value) {}
/**
* @brief Constructs a successful Result with a value.
* @param value An rvalue reference to the value.
*/
Result(T&& value) : m_Result(std::move(value)) {}
/**
* @brief Constructs an error Result.
* @param error The error object.
*/
Result(const Error& error) : m_Result(error) {}
/**
* @brief Constructs an error Result.
* @param error An rvalue reference to the error object.
*/
Result(Error&& error) : m_Result(std::move(error)) {}
/**
* @brief Checks if the Result is successful.
* @return True if the Result is successful, otherwise false.
*/
[[nodiscard]] fn isOk() const -> bool { return std::holds_alternative<T>(m_Result); }
/**
* @brief Checks if the Result contains an error.
* @return True if the Result contains an error, otherwise false.
*/
[[nodiscard]] fn isErr() const -> bool { return std::holds_alternative<Error>(m_Result); }
/**
* @brief Retrieves the value.
* @return A constant reference to the value.
* @throws std::logic_error if the Result contains an error.
*/
fn value() const -> const T& {
if (isErr()) {
throw std::logic_error("Attempted to access value of an error Result");
}
return std::get<T>(m_Result);
}
/**
* @brief Retrieves the error object.
* @return A constant reference to the Error object.
* @throws std::logic_error if the Result is successful.
*/
[[nodiscard]] fn error() const -> const Error& {
if (isOk()) {
throw std::logic_error("Attempted to access error of an ok Result");
}
return std::get<Error>(m_Result);
}
/**
* @brief Retrieves the value or returns a default value.
* @param defaultValue The default value to return if the Result contains an error.
* @return The value if the Result is successful, otherwise the default value.
*/
fn valueOr(const T& defaultValue) const -> T {
return isOk() ? std::get<T>(m_Result) : defaultValue;
}
private:
std::variant<T, Error>
m_Result; ///< The underlying result, which can be either a value of type T or an Error.
};
/**
* @brief Helper function to create a successful Result.
*
* This function deduces the type of the value and creates a successful Result.
* @tparam T The type of the value.
* @param value The value to be stored in the Result.
* @return A Result object containing the value.
*/
template <typename T>
fn Ok(T&& value) {
return Result<std::decay_t<T>>(std::forward<T>(value));
}
/**
* @brief Helper function to create a successful Result<void>.
*
* This function creates a successful Result that does not contain a value.
* @return A Result<void> object indicating success.
*/
inline fn Ok() -> Result<void> { return {}; }

View file

@ -1,126 +0,0 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <string>
/**
* @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;

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"
} ]
}