mirror of
https://github.com/hyprwm/hyprsysteminfo.git
synced 2026-05-09 08:18:10 +02:00
core: rewrite entire app in hyprtoolkit (#25)
* core: rewrite entire app in hyprtoolkit * Nix: update deps * CMake: fix .desktop path, dedup deps --------- Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
This commit is contained in:
parent
fe81610278
commit
1959f049f5
25 changed files with 1072 additions and 775 deletions
|
|
@ -1,34 +1,75 @@
|
|||
cmake_minimum_required(VERSION 3.16)
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
# Get version
|
||||
file(READ "${CMAKE_SOURCE_DIR}/VERSION" VER_RAW)
|
||||
string(STRIP ${VER_RAW} VER)
|
||||
string(STRIP ${VER_RAW} HYPRSYSTEMINFO_VERSION)
|
||||
|
||||
project(hsi VERSION ${VER} LANGUAGES CXX)
|
||||
add_compile_definitions(HYPRSYSTEMINFO_VERSION="${HYPRSYSTEMINFO_VERSION}")
|
||||
|
||||
project(
|
||||
hyprsysteminfo
|
||||
VERSION ${HYPRSYSTEMINFO_VERSION}
|
||||
DESCRIPTION "System info utility for Hyprland")
|
||||
|
||||
include(CTest)
|
||||
include(CheckIncludeFile)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(PREFIX ${CMAKE_INSTALL_PREFIX})
|
||||
set(INCLUDE ${CMAKE_INSTALL_FULL_INCLUDEDIR})
|
||||
set(LIBDIR ${CMAKE_INSTALL_FULL_LIBDIR})
|
||||
|
||||
find_package(glaze QUIET)
|
||||
if (NOT glaze_FOUND)
|
||||
set(GLAZE_VERSION v6.1.0)
|
||||
message(STATUS "glaze dependency not found, retrieving ${GLAZE_VERSION} with FetchContent")
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
glaze
|
||||
GIT_REPOSITORY https://github.com/stephenberry/glaze.git
|
||||
GIT_TAG ${GLAZE_VERSION}
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
FetchContent_MakeAvailable(glaze)
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(
|
||||
deps
|
||||
REQUIRED
|
||||
IMPORTED_TARGET
|
||||
hyprtoolkit
|
||||
hyprutils>=0.10.2
|
||||
libdrm
|
||||
libpci
|
||||
pixman-1
|
||||
)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wno-unused-parameter
|
||||
-Wno-unused-value
|
||||
-Wno-missing-field-initializers
|
||||
-Wpedantic)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
|
||||
|
||||
find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||
message(STATUS "Configuring hyprsysteminfo in Debug")
|
||||
add_compile_definitions(hyprsysteminfo_DEBUG)
|
||||
else()
|
||||
add_compile_options(-O3)
|
||||
message(STATUS "Configuring hyprsysteminfo in Release")
|
||||
endif()
|
||||
|
||||
pkg_check_modules(hyprutils REQUIRED IMPORTED_TARGET hyprutils)
|
||||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "include/*.hpp")
|
||||
|
||||
qt_standard_project_setup(REQUIRES 6.5)
|
||||
add_executable(hyprsysteminfo ${SRCFILES})
|
||||
|
||||
add_subdirectory(src)
|
||||
target_link_libraries(hyprsysteminfo PkgConfig::deps)
|
||||
|
||||
qt_add_resources(hyprsysteminfo "resource"
|
||||
PREFIX "/"
|
||||
FILES
|
||||
resource/hyprlandlogo.svg
|
||||
)
|
||||
install(
|
||||
FILES assets/install/hyprsysteminfo.desktop
|
||||
DESTINATION "share/applications")
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS hyprsysteminfo
|
||||
BUNDLE DESTINATION .
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
install(FILES assets/install/hyprsysteminfo.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
|
||||
install(TARGETS hyprsysteminfo)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
# hyprsysteminfo
|
||||
A tiny qt6/qml application to display information about the running system,
|
||||
## hyprsysteminfo
|
||||
A tiny hyprtoolkit application to display information about the running system,
|
||||
or copy diagnostics data, without the terminal.
|
||||
|
||||

|
||||
|
||||
## Footnotes
|
||||
[^1]:
|
||||
Some distributions may not support a graphical logo in hyprsysteminfo. To check if your distribution supports a graphical logo, run `cat /etc/os-release`. If the output includes a `LOGO` line, a graphical logo should appear in hyprsysteminfo. If there is no `LOGO` line, a graphical logo will not be displayed.
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 103 KiB |
158
flake.lock
generated
158
flake.lock
generated
|
|
@ -1,8 +1,13 @@
|
|||
{
|
||||
"nodes": {
|
||||
"hyprland-qt-support": {
|
||||
"aquamarine": {
|
||||
"inputs": {
|
||||
"hyprlang": "hyprlang",
|
||||
"hyprutils": [
|
||||
"hyprutils"
|
||||
],
|
||||
"hyprwayland-scanner": [
|
||||
"hyprwayland-scanner"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
|
|
@ -11,37 +16,63 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1737634706,
|
||||
"narHash": "sha256-nGCibkfsXz7ARx5R+SnisRtMq21IQIhazp6viBU8I/A=",
|
||||
"lastModified": 1767024902,
|
||||
"narHash": "sha256-sMdk6QkMDhIOnvULXKUM8WW8iyi551SWw2i6KQHbrrU=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qt-support",
|
||||
"rev": "8810df502cdee755993cb803eba7b23f189db795",
|
||||
"repo": "aquamarine",
|
||||
"rev": "b8a0c5ba5a9fbd2c660be7dd98bdde0ff3798556",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qt-support",
|
||||
"repo": "aquamarine",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprgraphics": {
|
||||
"inputs": {
|
||||
"hyprutils": [
|
||||
"hyprutils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1766946335,
|
||||
"narHash": "sha256-MRD+Jr2bY11MzNDfenENhiK6pvN+nHygxdHoHbZ1HtE=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprgraphics",
|
||||
"rev": "4af02a3925b454deb1c36603843da528b67ded6c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprgraphics",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprlang": {
|
||||
"inputs": {
|
||||
"hyprutils": "hyprutils",
|
||||
"hyprutils": [
|
||||
"hyprutils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"hyprland-qt-support",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"hyprland-qt-support",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1737634606,
|
||||
"narHash": "sha256-W7W87Cv6wqZ9PHegI6rH1+ve3zJPiyevMFf0/HwdbCQ=",
|
||||
"lastModified": 1764612430,
|
||||
"narHash": "sha256-54ltTSbI6W+qYGMchAgCR6QnC1kOdKXN6X6pJhOWxFg=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprlang",
|
||||
"rev": "f41271d35cc0f370d300413d756c2677f386af9d",
|
||||
"rev": "0d00dc118981531aa731150b6ea551ef037acddd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -50,25 +81,59 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprtoolkit": {
|
||||
"inputs": {
|
||||
"aquamarine": [
|
||||
"aquamarine"
|
||||
],
|
||||
"hyprgraphics": [
|
||||
"hyprgraphics"
|
||||
],
|
||||
"hyprlang": [
|
||||
"hyprlang"
|
||||
],
|
||||
"hyprutils": [
|
||||
"hyprutils"
|
||||
],
|
||||
"hyprwayland-scanner": [
|
||||
"hyprwayland-scanner"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1767025790,
|
||||
"narHash": "sha256-LEGGn0KRXr3swO80ESKE72KR6J4arRX/9psBnNF7O0A=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprtoolkit",
|
||||
"rev": "b42b3281d766e67eca990dba89e85cc7c1d3e26d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprtoolkit",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprutils": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"hyprland-qt-support",
|
||||
"hyprlang",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"hyprland-qt-support",
|
||||
"hyprlang",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1737632363,
|
||||
"narHash": "sha256-X9I8POSlHxBVjD0fiX1O2j7U9Zi1+4rIkrsyHP0uHXY=",
|
||||
"lastModified": 1766253372,
|
||||
"narHash": "sha256-1+p4Kw8HdtMoFSmJtfdwjxM4bPxDK9yg27SlvUMpzWA=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"rev": "006620eb29d54ea9086538891404c78563d1bae1",
|
||||
"rev": "51a4f93ce8572e7b12b7284eb9e6e8ebf16b4be9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -77,7 +142,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprutils_2": {
|
||||
"hyprwayland-scanner": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
|
|
@ -87,26 +152,52 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1737632363,
|
||||
"narHash": "sha256-X9I8POSlHxBVjD0fiX1O2j7U9Zi1+4rIkrsyHP0uHXY=",
|
||||
"lastModified": 1763640274,
|
||||
"narHash": "sha256-Uan1Nl9i4TF/kyFoHnTq1bd/rsWh4GAK/9/jDqLbY5A=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"rev": "006620eb29d54ea9086538891404c78563d1bae1",
|
||||
"repo": "hyprwayland-scanner",
|
||||
"rev": "f6cf414ca0e16a4d30198fd670ec86df3c89f671",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"repo": "hyprwayland-scanner",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprwire": {
|
||||
"inputs": {
|
||||
"hyprutils": [
|
||||
"hyprutils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1766253414,
|
||||
"narHash": "sha256-O70C7PD8r/8fwrVofU5gaswXrQ7WFg0m/M1eWnm3+1U=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprwire",
|
||||
"rev": "c0541f6fa55bfc98a7bfdfe07b0e448d616a3a1b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprwire",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1737469691,
|
||||
"narHash": "sha256-nmKOgAU48S41dTPIXAq0AHZSehWUn6ZPrUKijHAMmIk=",
|
||||
"lastModified": 1766902085,
|
||||
"narHash": "sha256-coBu0ONtFzlwwVBzmjacUQwj3G+lybcZ1oeNSQkgC0M=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9e4d5190a9482a1fb9d18adf0bdb83c6e506eaab",
|
||||
"rev": "c0b0e0fddf73fd517c3471e546c0df87a42d53f4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -118,8 +209,13 @@
|
|||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"hyprland-qt-support": "hyprland-qt-support",
|
||||
"hyprutils": "hyprutils_2",
|
||||
"aquamarine": "aquamarine",
|
||||
"hyprgraphics": "hyprgraphics",
|
||||
"hyprlang": "hyprlang",
|
||||
"hyprtoolkit": "hyprtoolkit",
|
||||
"hyprutils": "hyprutils",
|
||||
"hyprwayland-scanner": "hyprwayland-scanner",
|
||||
"hyprwire": "hyprwire",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"systems": "systems"
|
||||
}
|
||||
|
|
|
|||
44
flake.nix
44
flake.nix
|
|
@ -5,16 +5,56 @@
|
|||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
systems.url = "github:nix-systems/default-linux";
|
||||
|
||||
aquamarine = {
|
||||
url = "github:hyprwm/aquamarine";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.systems.follows = "systems";
|
||||
inputs.hyprutils.follows = "hyprutils";
|
||||
inputs.hyprwayland-scanner.follows = "hyprwayland-scanner";
|
||||
};
|
||||
|
||||
hyprgraphics = {
|
||||
url = "github:hyprwm/hyprgraphics";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.systems.follows = "systems";
|
||||
inputs.hyprutils.follows = "hyprutils";
|
||||
};
|
||||
|
||||
hyprutils = {
|
||||
url = "github:hyprwm/hyprutils";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.systems.follows = "systems";
|
||||
};
|
||||
|
||||
hyprland-qt-support = {
|
||||
url = "github:hyprwm/hyprland-qt-support";
|
||||
hyprlang = {
|
||||
url = "github:hyprwm/hyprlang";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.systems.follows = "systems";
|
||||
inputs.hyprutils.follows = "hyprutils";
|
||||
};
|
||||
|
||||
hyprwayland-scanner = {
|
||||
url = "github:hyprwm/hyprwayland-scanner";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.systems.follows = "systems";
|
||||
};
|
||||
|
||||
hyprwire = {
|
||||
url = "github:hyprwm/hyprwire";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.systems.follows = "systems";
|
||||
inputs.hyprutils.follows = "hyprutils";
|
||||
};
|
||||
|
||||
hyprtoolkit = {
|
||||
url = "github:hyprwm/hyprtoolkit";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.systems.follows = "systems";
|
||||
inputs.aquamarine.follows = "aquamarine";
|
||||
inputs.hyprutils.follows = "hyprutils";
|
||||
inputs.hyprlang.follows = "hyprlang";
|
||||
inputs.hyprgraphics.follows = "hyprgraphics";
|
||||
inputs.hyprwayland-scanner.follows = "hyprwayland-scanner";
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,46 +4,48 @@
|
|||
stdenv,
|
||||
|
||||
cmake,
|
||||
qt6,
|
||||
pkg-config,
|
||||
aquamarine,
|
||||
cairo,
|
||||
glaze-hyprsysteminfo,
|
||||
hyprgraphics,
|
||||
hyprtoolkit,
|
||||
hyprutils,
|
||||
libdrm,
|
||||
pciutils,
|
||||
hyprland-qt-support,
|
||||
pixman,
|
||||
version ? "0",
|
||||
}: let
|
||||
inherit (lib.strings) makeBinPath;
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
pname = "hyprsysteminfo";
|
||||
inherit version;
|
||||
}:
|
||||
|
||||
src = nix-gitignore.gitignoreSource [] ./..;
|
||||
stdenv.mkDerivation {
|
||||
pname = "hyprsysteminfo";
|
||||
inherit version;
|
||||
|
||||
nativeBuildInputs = [
|
||||
cmake
|
||||
pkg-config
|
||||
qt6.wrapQtAppsHook
|
||||
];
|
||||
src = nix-gitignore.gitignoreSource [] ./..;
|
||||
|
||||
buildInputs = [
|
||||
qt6.qtbase
|
||||
qt6.qtdeclarative
|
||||
qt6.qtsvg
|
||||
qt6.qtwayland
|
||||
hyprutils
|
||||
hyprland-qt-support
|
||||
];
|
||||
nativeBuildInputs = [
|
||||
cmake
|
||||
pkg-config
|
||||
];
|
||||
|
||||
preFixup = ''
|
||||
qtWrapperArgs+=(--prefix PATH : "${makeBinPath [pciutils]}")
|
||||
'';
|
||||
buildInputs = [
|
||||
aquamarine
|
||||
cairo
|
||||
glaze-hyprsysteminfo
|
||||
hyprgraphics
|
||||
hyprtoolkit
|
||||
hyprutils
|
||||
libdrm
|
||||
pciutils
|
||||
pixman
|
||||
];
|
||||
|
||||
meta = {
|
||||
description = "A tiny qt6/qml application to display information about the running system";
|
||||
homepage = "https://github.com/hyprwm/hyprsysteminfo";
|
||||
license = lib.licenses.bsd3;
|
||||
maintainers = [lib.maintainers.fufexan];
|
||||
mainProgram = "hyprsysteminfo";
|
||||
platforms = lib.platforms.linux;
|
||||
};
|
||||
}
|
||||
meta = {
|
||||
description = "System info utility for Hyprland";
|
||||
homepage = "https://github.com/hyprwm/hyprsysteminfo";
|
||||
license = lib.licenses.bsd3;
|
||||
maintainers = [lib.maintainers.fufexan];
|
||||
mainProgram = "hyprsysteminfo";
|
||||
platforms = lib.platforms.linux;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,12 +14,28 @@ in {
|
|||
default = self.overlays.hyprsysteminfo;
|
||||
|
||||
hyprsysteminfo = lib.composeManyExtensions [
|
||||
inputs.aquamarine.overlays.default
|
||||
inputs.hyprgraphics.overlays.default
|
||||
inputs.hyprlang.overlays.default
|
||||
inputs.hyprutils.overlays.default
|
||||
inputs.hyprland-qt-support.overlays.default
|
||||
inputs.hyprwayland-scanner.overlays.default
|
||||
inputs.hyprtoolkit.overlays.default
|
||||
inputs.hyprwire.overlays.default
|
||||
self.overlays.glaze
|
||||
(final: prev: {
|
||||
hyprsysteminfo = final.callPackage ./. {
|
||||
version = "${version}+date=${date}_${self.shortRev or "dirty"}";
|
||||
stdenv = final.gcc15Stdenv;
|
||||
};
|
||||
})
|
||||
];
|
||||
|
||||
# Even though glaze itself disables it by default, nixpkgs sets ENABLE_SSL set to true.
|
||||
# Since we don't include openssl, the build failes without the `enableSSL = false;` override
|
||||
glaze = final: prev: {
|
||||
glaze-hyprsysteminfo = prev.glaze.override {
|
||||
enableSSL = false;
|
||||
enableInterop = false;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
find_package(Qt6 REQUIRED COMPONENTS WaylandClientPrivate)
|
||||
|
||||
qt_add_executable(hyprsysteminfo
|
||||
main.cpp
|
||||
util/Utils.cpp
|
||||
SystemInfo.cpp
|
||||
SystemIconProvider.cpp
|
||||
WaylandScreen.cpp
|
||||
)
|
||||
|
||||
qt_add_qml_module(hyprsysteminfo
|
||||
URI org.hyprland.systeminfo
|
||||
VERSION 1.0
|
||||
QML_FILES main.qml
|
||||
)
|
||||
|
||||
target_link_libraries(hyprsysteminfo PRIVATE
|
||||
Qt6::Widgets Qt6::QuickControls2 Qt6::WaylandClientPrivate PkgConfig::hyprutils
|
||||
)
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
#include "SystemIconProvider.hpp"
|
||||
#include <qicon.h>
|
||||
#include <qlogging.h>
|
||||
#include <qpixmap.h>
|
||||
|
||||
QPixmap CSystemIconProvider::requestPixmap(const QString& id, QSize* size, const QSize& requestedSize) {
|
||||
auto icon = QIcon::fromTheme(id);
|
||||
|
||||
if (!requestedSize.isValid()) {
|
||||
qCritical() << "Icon requests without an explicit size are not allowed.";
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
auto pixmap = icon.pixmap(requestedSize.width(), requestedSize.height());
|
||||
|
||||
if (pixmap.isNull()) {
|
||||
qWarning() << "Could not load icon" << id;
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
if (size != nullptr)
|
||||
*size = pixmap.size();
|
||||
|
||||
return pixmap;
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QIcon>
|
||||
#include <QObject>
|
||||
#include <QPixmap>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQuickImageProvider>
|
||||
|
||||
class CSystemIconProvider : public QQuickImageProvider {
|
||||
public:
|
||||
CSystemIconProvider() : QQuickImageProvider(QQuickImageProvider::Pixmap) {}
|
||||
|
||||
QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize) override;
|
||||
};
|
||||
|
|
@ -1,226 +0,0 @@
|
|||
#include "SystemInfo.hpp"
|
||||
#include "WaylandScreen.hpp"
|
||||
#include "util/Utils.hpp"
|
||||
#include <qclipboard.h>
|
||||
#include <array>
|
||||
#include <qcontainerfwd.h>
|
||||
#include <qfiledevice.h>
|
||||
#include <qimage.h>
|
||||
#include <qobject.h>
|
||||
#include <qscreen.h>
|
||||
#include <qfile.h>
|
||||
#include <qstringliteral.h>
|
||||
#include <string>
|
||||
#include <format>
|
||||
|
||||
#include <qguiapplication.h>
|
||||
#include <qtenvironmentvariables.h>
|
||||
#include <qwindowdefs.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <hyprutils/string/VarList.hpp>
|
||||
#include <hyprutils/string/String.hpp>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace Hyprutils::String;
|
||||
|
||||
CSystemInternals::CSystemInternals(QObject* parent) : QObject(parent) {
|
||||
setenv("LC_ALL", "en_US.UTF-8", true);
|
||||
|
||||
// gather data from os-release
|
||||
if (auto data = readFile("/etc/os-release")) {
|
||||
CVarList lines(data.value(), 0, '\n');
|
||||
|
||||
for (auto& line : lines) {
|
||||
CVarList param(line, 2, '=');
|
||||
|
||||
const auto& key = param[0];
|
||||
auto value = param[1];
|
||||
|
||||
if (value.length() >= 2 && value.at(0) == '\"')
|
||||
value = value.substr(1, value.length() - 2);
|
||||
|
||||
if (key == "PRETTY_NAME") {
|
||||
systemName = QString::fromLocal8Bit(value);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key == "HOME_URL") {
|
||||
systemURL = QString::fromLocal8Bit(value);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key == "LOGO") {
|
||||
systemLogoName = QString::fromLocal8Bit(value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get kernel ver
|
||||
utsname unamebuf;
|
||||
if (uname(&unamebuf) == 0) {
|
||||
systemKernel = unamebuf.release;
|
||||
}
|
||||
|
||||
// get hyprland info
|
||||
if (getenv("HYPRLAND_INSTANCE_SIGNATURE")) {
|
||||
hlSystemVersion = execAndGet("hyprctl", {"version"});
|
||||
auto DATA = hlSystemVersion.toStdString();
|
||||
|
||||
if (DATA.contains("Tag: ")) {
|
||||
auto temp = DATA.substr(DATA.find("Tag: ") + 5);
|
||||
temp = temp.substr(0, temp.find(","));
|
||||
hyprlandVersionLong = QString::fromLocal8Bit(temp);
|
||||
hyprlandVersion = QString::fromLocal8Bit(temp.substr(0, temp.find("-")));
|
||||
}
|
||||
|
||||
if (hyprlandVersionLong.length() <= 0 && DATA.contains("at commit")) {
|
||||
auto temp = DATA.substr(DATA.find("at commit") + 10);
|
||||
temp = temp.substr(0, temp.find(" "));
|
||||
hyprlandVersionLong = QString::fromLocal8Bit(temp.substr(0, 7));
|
||||
hyprlandVersion = QString::fromLocal8Bit(temp.substr(0, 7));
|
||||
}
|
||||
|
||||
if (hyprlandVersionLong.isEmpty()) {
|
||||
hyprlandVersionLong = QStringLiteral("unknown");
|
||||
hyprlandVersion = QStringLiteral("unknown");
|
||||
}
|
||||
|
||||
hlSystemInfo = execAndGet("hyprctl", {"systeminfo"});
|
||||
if (!hlSystemInfo.contains("Hyprland"))
|
||||
hlSystemInfo = "";
|
||||
}
|
||||
|
||||
// get cpu info
|
||||
{
|
||||
const auto DATA = execAndGet("lscpu").toStdString();
|
||||
if (DATA.contains("odel name")) {
|
||||
std::string arch, model, ghz, nproc;
|
||||
|
||||
CVarList data(DATA, 0, '\n');
|
||||
for (auto& line : data) {
|
||||
std::string left, right;
|
||||
left = trim(line.substr(0, line.find(":")));
|
||||
right = trim(line.substr(line.find(":") + 1));
|
||||
|
||||
if (left == "Architecture") {
|
||||
arch = right;
|
||||
continue;
|
||||
}
|
||||
if (left == "Model name") {
|
||||
model = right;
|
||||
continue;
|
||||
}
|
||||
if (left == "CPU(s)") {
|
||||
nproc = right;
|
||||
continue;
|
||||
}
|
||||
if (left == "CPU max MHz") {
|
||||
try {
|
||||
ghz = std::format("{:.02}GHz", std::stof(right) / 1000.F);
|
||||
} catch (...) { ghz = "?GHz"; }
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
cpuInfo = QString::fromLocal8Bit(std::format("{} at {}x{} ({})", model, nproc, ghz, arch));
|
||||
}
|
||||
}
|
||||
|
||||
// get gpu info
|
||||
{
|
||||
auto ok = false;
|
||||
const auto DATA = execAndGet("lspci", {"-vnn"}, &ok).toStdString();
|
||||
CVarList lines(DATA, 0, '\n');
|
||||
|
||||
if (ok) {
|
||||
for (auto& line : lines) {
|
||||
if (!line.contains("VGA"))
|
||||
continue;
|
||||
gpuInfo.emplace_back(QString::fromLocal8Bit(std::format("{}", line.substr(line.find(":", line.find("VGA")) + 2))));
|
||||
}
|
||||
|
||||
if (gpuInfo.isEmpty())
|
||||
gpuInfo.emplaceBack(QStringLiteral("No GPUs found"));
|
||||
} else
|
||||
gpuInfo.emplaceBack(QStringLiteral("missing dependency: lspci"));
|
||||
}
|
||||
|
||||
// get ram info
|
||||
{
|
||||
const auto DATA = execAndGet("free").toStdString();
|
||||
if (DATA.contains("total")) {
|
||||
CVarList data(DATA, 0, '\n');
|
||||
|
||||
auto ramIntToReadable = [](const std::string& datapoint) -> std::string {
|
||||
try {
|
||||
auto asInt = std::stoull(datapoint);
|
||||
return std::format("{:.03}GB", asInt / 1000000.0);
|
||||
} catch (...) { return "[error]"; }
|
||||
};
|
||||
|
||||
CVarList props(data[1], 0, 's', true);
|
||||
|
||||
ramInfo = QString::fromLocal8Bit(std::format("{} / {}", ramIntToReadable(props[2]), ramIntToReadable(props[1])));
|
||||
}
|
||||
}
|
||||
|
||||
// other, misc
|
||||
if (auto current = qEnvironmentVariable("XDG_CURRENT_DESKTOP"); !current.isEmpty())
|
||||
DE = current;
|
||||
|
||||
if (auto procUptime = readFile("/proc/uptime")) {
|
||||
CVarList data(procUptime.value(), 0, 's', true);
|
||||
|
||||
try {
|
||||
int uptimeSeconds = std::round(std::stof(data[0]));
|
||||
int uptimeDays = std::floor(uptimeSeconds / 3600.0 / 24.0);
|
||||
int uptimeHours = std::floor((uptimeSeconds % (3600 * 24)) / 3600.0);
|
||||
int uptimeMinutes = std::floor((uptimeSeconds % (3600)) / 60.0);
|
||||
|
||||
auto upStr = std::format("{}{}{}", (uptimeDays > 0 ? std::format("{} days, ", uptimeDays) : ""), (uptimeHours > 0 ? std::format("{} hours, ", uptimeHours) : ""),
|
||||
(uptimeMinutes > 0 ? std::format("{} minutes, ", uptimeMinutes) : ""));
|
||||
|
||||
if (!upStr.empty())
|
||||
upStr = upStr.substr(0, upStr.length() - 2);
|
||||
|
||||
uptime = QString::fromLocal8Bit(upStr);
|
||||
} catch (...) { ; }
|
||||
}
|
||||
|
||||
{
|
||||
std::string screens;
|
||||
for (const auto& s : SWaylandScreenInfo::enumerateScreens()) {
|
||||
screens += std::format("{} ({}x{}), ", s.name.toStdString(), s.pixelSize.width(), s.pixelSize.height());
|
||||
}
|
||||
|
||||
if (!screens.empty())
|
||||
screens = screens.substr(0, screens.length() - 2);
|
||||
|
||||
this->screens = QString::fromLocal8Bit(screens);
|
||||
}
|
||||
|
||||
if (auto* username = getlogin()) {
|
||||
std::array<char, 128> hostname;
|
||||
if (gethostname(hostname.data(), hostname.size()) == 0)
|
||||
user = QString::fromLocal8Bit(std::format("{}@{}", username, hostname.data()));
|
||||
}
|
||||
|
||||
{
|
||||
if (auto productName = readFile("/sys/devices/virtual/dmi/id/product_name"))
|
||||
board = QString::fromLocal8Bit(trim(productName.value()));
|
||||
else if (auto boardName = readFile("/sys/devices/virtual/dmi/id/board_name"))
|
||||
board = QString::fromLocal8Bit(trim(boardName.value()));
|
||||
}
|
||||
}
|
||||
|
||||
void CSystemInternals::copySystemInfo() {
|
||||
QGuiApplication::clipboard()->setText(hlSystemInfo);
|
||||
execAndGet("hyprctl", {"notify", "5", "5000", "0", "Copied system info to the clipboard."});
|
||||
}
|
||||
|
||||
void CSystemInternals::copyVersion() {
|
||||
QGuiApplication::clipboard()->setText(hlSystemVersion);
|
||||
execAndGet("hyprctl", {"notify", "5", "5000", "0", "Copied version info to the clipboard."});
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QPixmap>
|
||||
#include <QIcon>
|
||||
#include <qcontainerfwd.h>
|
||||
#include <qqmlintegration.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class CSystemInternals : public QObject {
|
||||
Q_OBJECT;
|
||||
QML_NAMED_ELEMENT(SystemInfo);
|
||||
QML_SINGLETON;
|
||||
Q_PROPERTY(QString systemLogoName MEMBER systemLogoName CONSTANT);
|
||||
Q_PROPERTY(QString systemName MEMBER systemName CONSTANT);
|
||||
Q_PROPERTY(QString systemUrl MEMBER systemURL CONSTANT);
|
||||
Q_PROPERTY(QString systemKernel MEMBER systemKernel CONSTANT);
|
||||
Q_PROPERTY(QString hyprlandVersion MEMBER hyprlandVersion CONSTANT);
|
||||
Q_PROPERTY(QString hyprlandVersionLong MEMBER hyprlandVersionLong CONSTANT);
|
||||
Q_PROPERTY(QString cpuInfo MEMBER cpuInfo CONSTANT);
|
||||
Q_PROPERTY(QVector<QString> gpuInfo MEMBER gpuInfo CONSTANT);
|
||||
Q_PROPERTY(QString ramInfo MEMBER ramInfo CONSTANT);
|
||||
Q_PROPERTY(QString uptime MEMBER uptime CONSTANT);
|
||||
Q_PROPERTY(QString de MEMBER DE CONSTANT);
|
||||
Q_PROPERTY(QString screens MEMBER screens CONSTANT);
|
||||
Q_PROPERTY(QString model MEMBER board CONSTANT);
|
||||
Q_PROPERTY(QString user MEMBER user CONSTANT);
|
||||
|
||||
public:
|
||||
explicit CSystemInternals(QObject* parent = nullptr);
|
||||
|
||||
QString systemLogoName, systemName = "Linux", systemURL = "https://kernel.org/", systemKernel = "unknown";
|
||||
QString hyprlandVersion, hyprlandVersionLong;
|
||||
|
||||
QString cpuInfo = "missing dependency: lscpu";
|
||||
QVector<QString> gpuInfo;
|
||||
QString ramInfo = "?";
|
||||
|
||||
QString hlSystemInfo = "[error]", hlSystemVersion = "[error]";
|
||||
|
||||
QString uptime = "unknown", DE = "Unknown", screens = "unknown", board = "", user = "";
|
||||
|
||||
Q_INVOKABLE void copySystemInfo();
|
||||
Q_INVOKABLE void copyVersion();
|
||||
};
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
#include "WaylandScreen.hpp"
|
||||
|
||||
#include <QtWaylandClient/private/qwayland-wayland.h>
|
||||
#include <QtWaylandClient/private/qwaylanddisplay_p.h>
|
||||
#include <QtWaylandClient/private/qwaylandintegration_p.h>
|
||||
#include <cstdint>
|
||||
#include <qcontainerfwd.h>
|
||||
#include <qsize.h>
|
||||
#include <qstringliteral.h>
|
||||
#include <qtclasshelpermacros.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace QtWaylandClient;
|
||||
|
||||
class CWaylandScreen : public QtWayland::wl_output {
|
||||
public:
|
||||
explicit CWaylandScreen(QWaylandDisplay* display, uint32_t version, uint32_t id);
|
||||
~CWaylandScreen() override;
|
||||
Q_DISABLE_COPY_MOVE(CWaylandScreen);
|
||||
SWaylandScreenInfo info;
|
||||
|
||||
protected:
|
||||
void output_mode(uint32_t flags, int32_t width, int32_t height, int32_t refresh) override;
|
||||
void output_name(const QString& name) override;
|
||||
};
|
||||
|
||||
std::vector<SWaylandScreenInfo> SWaylandScreenInfo::enumerateScreens() {
|
||||
auto* display = QWaylandIntegration::instance()->display();
|
||||
|
||||
std::vector<CWaylandScreen*> screens;
|
||||
for (const auto& global : display->globals()) {
|
||||
if (global.interface == QStringLiteral("wl_output")) {
|
||||
screens.emplace_back(new CWaylandScreen(display, global.version, global.id));
|
||||
}
|
||||
}
|
||||
|
||||
display->forceRoundTrip();
|
||||
|
||||
std::vector<SWaylandScreenInfo> info;
|
||||
for (auto* screen : screens) {
|
||||
info.push_back(screen->info);
|
||||
delete screen;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
CWaylandScreen::CWaylandScreen(QWaylandDisplay* display, uint32_t version, uint32_t id) : QtWayland::wl_output(display->wl_registry(), id, 4u) {}
|
||||
|
||||
CWaylandScreen::~CWaylandScreen() {
|
||||
release();
|
||||
}
|
||||
|
||||
void CWaylandScreen::output_mode(uint32_t flags, int width, int height, int refresh) {
|
||||
info.pixelSize = QSize(width, height);
|
||||
}
|
||||
|
||||
void CWaylandScreen::output_name(const QString& name) {
|
||||
info.name = name;
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <qcontainerfwd.h>
|
||||
#include <qsize.h>
|
||||
#include <qstring.h>
|
||||
#include <qcontainerfwd.h>
|
||||
#include <vector>
|
||||
|
||||
struct SWaylandScreenInfo {
|
||||
QString name;
|
||||
QSize pixelSize;
|
||||
|
||||
static std::vector<SWaylandScreenInfo> enumerateScreens();
|
||||
};
|
||||
12
src/helpers/Logger.hpp
Normal file
12
src/helpers/Logger.hpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <hyprutils/cli/Logger.hpp>
|
||||
#include "Memory.hpp"
|
||||
|
||||
#define LOG_DEBUG Hyprutils::CLI::LOG_DEBUG
|
||||
#define LOG_ERR Hyprutils::CLI::LOG_ERR
|
||||
#define LOG_WARN Hyprutils::CLI::LOG_WARN
|
||||
#define LOG_TRACE Hyprutils::CLI::LOG_TRACE
|
||||
#define LOG_CRIT Hyprutils::CLI::LOG_CRIT
|
||||
|
||||
inline UP<Hyprutils::CLI::CLogger> g_logger = makeUnique<Hyprutils::CLI::CLogger>();
|
||||
15
src/helpers/Memory.hpp
Normal file
15
src/helpers/Memory.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <bit>
|
||||
|
||||
#include <hyprutils/memory/SharedPtr.hpp>
|
||||
#include <hyprutils/memory/UniquePtr.hpp>
|
||||
#include <hyprutils/memory/Atomic.hpp>
|
||||
#include <hyprutils/memory/Casts.hpp>
|
||||
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
#define SP CSharedPointer
|
||||
#define WP CWeakPointer
|
||||
#define UP CUniquePointer
|
||||
#define ASP CAtomicSharedPointer
|
||||
9
src/icons/Icons.hpp
Normal file
9
src/icons/Icons.hpp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Icons {
|
||||
inline constexpr uint8_t HYPRLAND_LOGO[] = {
|
||||
#embed "../../resource/hyprlandlogo.svg"
|
||||
};
|
||||
};
|
||||
287
src/main.cpp
287
src/main.cpp
|
|
@ -1,21 +1,278 @@
|
|||
#include "SystemIconProvider.hpp"
|
||||
#include <qapplication.h>
|
||||
#include <qqmlapplicationengine.h>
|
||||
#include <qquickstyle.h>
|
||||
#include <qtenvironmentvariables.h>
|
||||
#include <hyprtoolkit/core/Backend.hpp>
|
||||
#include <hyprtoolkit/window/Window.hpp>
|
||||
#include <hyprtoolkit/element/Rectangle.hpp>
|
||||
#include <hyprtoolkit/element/RowLayout.hpp>
|
||||
#include <hyprtoolkit/element/ColumnLayout.hpp>
|
||||
#include <hyprtoolkit/element/Text.hpp>
|
||||
#include <hyprtoolkit/element/Image.hpp>
|
||||
#include <hyprtoolkit/element/Button.hpp>
|
||||
#include <hyprtoolkit/element/Null.hpp>
|
||||
#include <hyprtoolkit/system/Icons.hpp>
|
||||
#include <hyprtoolkit/core/Output.hpp>
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
QApplication app(argc, argv);
|
||||
#include <hyprutils/memory/SharedPtr.hpp>
|
||||
#include <hyprutils/memory/UniquePtr.hpp>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
|
||||
app.setApplicationName("Hyprland System Info");
|
||||
#include "utils/SystemInfo.hpp"
|
||||
#include "utils/HyprlandIPC.hpp"
|
||||
|
||||
if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE"))
|
||||
QQuickStyle::setStyle("org.hyprland.style");
|
||||
#include <cstring>
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
engine.addImageProvider("systemIcons", new CSystemIconProvider);
|
||||
QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection);
|
||||
engine.load("qrc:/qt/qml/org/hyprland/systeminfo/main.qml");
|
||||
using namespace Hyprutils::Memory;
|
||||
using namespace Hyprutils::Math;
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprtoolkit;
|
||||
|
||||
return app.exec();
|
||||
#define SP CSharedPointer
|
||||
#define WP CWeakPointer
|
||||
#define UP CUniquePointer
|
||||
|
||||
static SP<IBackend> backend;
|
||||
|
||||
//
|
||||
static SP<IElement> space(SP<IElement> e) {
|
||||
auto n = CNullBuilder::begin()->size({CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_AUTO, {1, 1}})->commence();
|
||||
n->addChild(e);
|
||||
e->setPositionMode(Hyprtoolkit::IElement::HT_POSITION_ABSOLUTE);
|
||||
e->setPositionFlag(Hyprtoolkit::IElement::HT_POSITION_FLAG_LEFT, true);
|
||||
e->setPositionFlag(Hyprtoolkit::IElement::HT_POSITION_FLAG_VCENTER, true);
|
||||
return n;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
setenv("HT_QUIET", "1", true);
|
||||
backend = IBackend::create();
|
||||
|
||||
const auto FONT_SIZE = CFontSize{CFontSize::HT_FONT_TEXT}.ptSize();
|
||||
const auto WINDOW_SIZE = Vector2D{FONT_SIZE * 100.F, FONT_SIZE * 60.F};
|
||||
|
||||
auto window =
|
||||
CWindowBuilder::begin()->preferredSize(WINDOW_SIZE)->minSize(WINDOW_SIZE)->maxSize(WINDOW_SIZE)->appTitle("System Information")->appClass("hyprsysteminfo")->commence();
|
||||
|
||||
window->m_rootElement->addChild(CRectangleBuilder::begin()->color([] { return backend->getPalette()->m_colors.background; })->commence());
|
||||
|
||||
auto mainLayout = CColumnLayoutBuilder::begin()->size({CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_PERCENT, {1, 1}})->gap(2)->commence();
|
||||
mainLayout->setMargin(4);
|
||||
|
||||
window->m_rootElement->addChild(mainLayout);
|
||||
|
||||
auto topLayout = CRowLayoutBuilder::begin()->size({CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_AUTO, {1, 1}})->commence();
|
||||
|
||||
// Left: Distro
|
||||
{
|
||||
auto distroNull = CNullBuilder::begin()->size({CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_ABSOLUTE, {0.5F, FONT_SIZE * 10.F}})->commence();
|
||||
auto distroLayout = CRowLayoutBuilder::begin()->size({CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_PERCENT, {0.9F, 1.F}})->commence();
|
||||
distroLayout->setPositionMode(Hyprtoolkit::IElement::HT_POSITION_ABSOLUTE);
|
||||
distroLayout->setPositionFlag(Hyprtoolkit::IElement::HT_POSITION_FLAG_CENTER, true);
|
||||
|
||||
auto spacer1 = CNullBuilder::begin()->size({CDynamicSize::HT_SIZE_ABSOLUTE, CDynamicSize::HT_SIZE_ABSOLUTE, {1, 1}})->commence();
|
||||
spacer1->setGrow(true);
|
||||
|
||||
distroLayout->addChild(spacer1);
|
||||
|
||||
auto distroLogoName = Info::getDistroLogoName();
|
||||
if (distroLogoName) {
|
||||
auto icon = backend->systemIcons()->lookupIcon(*distroLogoName);
|
||||
|
||||
if (icon->exists()) {
|
||||
auto img = CImageBuilder::begin()->size({CDynamicSize::HT_SIZE_AUTO, CDynamicSize::HT_SIZE_PERCENT, {1.F, 0.9F}})->icon(icon)->commence();
|
||||
distroLayout->addChild(img);
|
||||
}
|
||||
}
|
||||
|
||||
auto distroTextLayout = CColumnLayoutBuilder::begin()->size({CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_AUTO, {0.5F, 1.F}})->commence();
|
||||
|
||||
auto distroTextA = CTextBuilder::begin()
|
||||
->text(Info::getFromEtcOsRelease("NAME").value_or("Unknown"))
|
||||
->fontSize({CFontSize::HT_FONT_TEXT, 1.F})
|
||||
->color([] { return backend->getPalette()->m_colors.text; })
|
||||
->commence();
|
||||
auto distroTextB = CTextBuilder::begin()
|
||||
->text(std::format("<a href=\"{}\">{}</a>", Info::getFromEtcOsRelease("HOME_URL").value_or(""), Info::getFromEtcOsRelease("HOME_URL").value_or("")))
|
||||
->fontSize({CFontSize::HT_FONT_TEXT, 1.F})
|
||||
->color([] { return backend->getPalette()->m_colors.text; })
|
||||
->commence();
|
||||
auto distroTextC =
|
||||
CTextBuilder::begin()->text(Info::kernel())->fontSize({CFontSize::HT_FONT_TEXT, 1.F})->color([] { return backend->getPalette()->m_colors.text; })->commence();
|
||||
|
||||
distroTextLayout->addChild(distroTextA);
|
||||
distroTextLayout->addChild(distroTextB);
|
||||
distroTextLayout->addChild(distroTextC);
|
||||
|
||||
distroLayout->addChild(distroTextLayout);
|
||||
|
||||
distroNull->addChild(distroLayout);
|
||||
|
||||
topLayout->addChild(distroNull);
|
||||
}
|
||||
|
||||
// Right: Hyprland
|
||||
|
||||
{
|
||||
auto hlNull = CNullBuilder::begin()->size({CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_ABSOLUTE, {0.5F, FONT_SIZE * 10.F}})->commence();
|
||||
auto hlLayout = CRowLayoutBuilder::begin()->size({CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_PERCENT, {0.9F, 1.F}})->commence();
|
||||
hlLayout->setPositionMode(Hyprtoolkit::IElement::HT_POSITION_ABSOLUTE);
|
||||
hlLayout->setPositionFlag(Hyprtoolkit::IElement::HT_POSITION_FLAG_CENTER, true);
|
||||
|
||||
auto spacer1 = CNullBuilder::begin()->size({CDynamicSize::HT_SIZE_ABSOLUTE, CDynamicSize::HT_SIZE_ABSOLUTE, {1, 1}})->commence();
|
||||
spacer1->setGrow(true);
|
||||
|
||||
auto logoData = Info::deLogo();
|
||||
if (logoData) {
|
||||
std::vector<uint8_t> data;
|
||||
data.resize(logoData->size());
|
||||
std::memcpy(data.data(), logoData->data(), logoData->size());
|
||||
|
||||
auto img = CImageBuilder::begin()->size({CDynamicSize::HT_SIZE_AUTO, CDynamicSize::HT_SIZE_PERCENT, {1.F, 0.9F}})->data(std::move(data))->commence();
|
||||
hlLayout->addChild(img);
|
||||
}
|
||||
|
||||
auto hlTextLayout = CColumnLayoutBuilder::begin()->size({CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_AUTO, {0.5F, 1.F}})->commence();
|
||||
|
||||
auto hlTextA =
|
||||
CTextBuilder::begin()->text(Info::desktop())->fontSize({CFontSize::HT_FONT_TEXT, 1.F})->color([] { return backend->getPalette()->m_colors.text; })->commence();
|
||||
auto hlTextB = CTextBuilder::begin()
|
||||
->text(std::format("<a href=\"{}\">{}</a>", Info::desktopHome(), Info::desktopHome()))
|
||||
->fontSize({CFontSize::HT_FONT_TEXT, 1.F})
|
||||
->color([] { return backend->getPalette()->m_colors.text; })
|
||||
->commence();
|
||||
auto hlTextC =
|
||||
CTextBuilder::begin()->text(Info::desktopVersion())->fontSize({CFontSize::HT_FONT_TEXT, 1.F})->color([] { return backend->getPalette()->m_colors.text; })->commence();
|
||||
|
||||
hlTextLayout->addChild(hlTextA);
|
||||
hlTextLayout->addChild(hlTextB);
|
||||
hlTextLayout->addChild(hlTextC);
|
||||
|
||||
hlLayout->addChild(hlTextLayout);
|
||||
|
||||
hlLayout->addChild(spacer1);
|
||||
|
||||
hlNull->addChild(hlLayout);
|
||||
|
||||
topLayout->addChild(hlNull);
|
||||
}
|
||||
|
||||
mainLayout->addChild(topLayout);
|
||||
|
||||
// Spacer, then system props
|
||||
|
||||
auto hr = Hyprtoolkit::CRectangleBuilder::begin() //
|
||||
->color([] { return Hyprtoolkit::CHyprColor{backend->getPalette()->m_colors.text.darken(0.65)}; })
|
||||
->size({Hyprtoolkit::CDynamicSize::HT_SIZE_PERCENT, Hyprtoolkit::CDynamicSize::HT_SIZE_ABSOLUTE, {1.F, 9.F}})
|
||||
->commence();
|
||||
hr->setMargin(4);
|
||||
|
||||
mainLayout->addChild(hr);
|
||||
|
||||
auto container = CColumnLayoutBuilder::begin()->size({CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_AUTO, {0.95F, 1.F}})->gap(2)->commence();
|
||||
|
||||
container->setMargin(10);
|
||||
|
||||
container->setPositionMode(Hyprtoolkit::IElement::HT_POSITION_ABSOLUTE);
|
||||
container->setPositionFlag(Hyprtoolkit::IElement::HT_POSITION_FLAG_HCENTER, true);
|
||||
|
||||
container->addChild(space(CTextBuilder::begin()
|
||||
->text(std::format("User: {}", Info::user()))
|
||||
->fontSize({CFontSize::HT_FONT_TEXT, 1.F})
|
||||
->color([] { return backend->getPalette()->m_colors.text; })
|
||||
->commence()));
|
||||
|
||||
container->addChild(space(CTextBuilder::begin()
|
||||
->text(std::format("Model: {}", Info::model()))
|
||||
->fontSize({CFontSize::HT_FONT_TEXT, 1.F})
|
||||
->color([] { return backend->getPalette()->m_colors.text; })
|
||||
->commence()));
|
||||
|
||||
container->addChild(space(CTextBuilder::begin()
|
||||
->text(std::format("CPU: {}", Info::cpu()))
|
||||
->fontSize({CFontSize::HT_FONT_TEXT, 1.F})
|
||||
->color([] { return backend->getPalette()->m_colors.text; })
|
||||
->commence()));
|
||||
|
||||
for (const auto& g : Info::gpus()) {
|
||||
container->addChild(space(CTextBuilder::begin()
|
||||
->text(std::format("GPU: {}", g))
|
||||
->fontSize({CFontSize::HT_FONT_TEXT, 1.F})
|
||||
->color([] { return backend->getPalette()->m_colors.text; })
|
||||
->commence()));
|
||||
}
|
||||
|
||||
container->addChild(space(CTextBuilder::begin()
|
||||
->text(std::format("RAM: {}", Info::mem()))
|
||||
->fontSize({CFontSize::HT_FONT_TEXT, 1.F})
|
||||
->color([] { return backend->getPalette()->m_colors.text; })
|
||||
->commence()));
|
||||
|
||||
container->addChild(space(CTextBuilder::begin()
|
||||
->text(std::format("Uptime: {}", Info::uptime()))
|
||||
->fontSize({CFontSize::HT_FONT_TEXT, 1.F})
|
||||
->color([] { return backend->getPalette()->m_colors.text; })
|
||||
->commence()));
|
||||
|
||||
mainLayout->addChild(container);
|
||||
|
||||
// bottom layout
|
||||
|
||||
auto bottomNull = CNullBuilder::begin()->size({CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_AUTO, {1, 1}})->commence();
|
||||
bottomNull->setMargin(6);
|
||||
bottomNull->setPositionMode(Hyprtoolkit::IElement::HT_POSITION_ABSOLUTE);
|
||||
bottomNull->setPositionFlag(Hyprtoolkit::IElement::HT_POSITION_FLAG_BOTTOM, true);
|
||||
|
||||
auto bottomLayout = CRowLayoutBuilder::begin()->size({CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_AUTO, {1, 1}})->gap(10)->commence();
|
||||
|
||||
auto spacer = CNullBuilder::begin()->size({CDynamicSize::HT_SIZE_ABSOLUTE, CDynamicSize::HT_SIZE_ABSOLUTE, {1, 1}})->commence();
|
||||
spacer->setGrow(true, false);
|
||||
|
||||
bottomLayout->addChild(spacer);
|
||||
|
||||
bottomLayout->addChild( //
|
||||
CButtonBuilder::begin()
|
||||
->label("Copy Hyprland System Info")
|
||||
->onMainClick([](SP<CButtonElement> e) {
|
||||
// FIXME: toolkit needs to provide clipboard stuff
|
||||
|
||||
auto info = HyprlandIPC::getFromSocket("/systeminfo");
|
||||
|
||||
if (!info) {
|
||||
e->rebuild()->label("Failed copying")->commence();
|
||||
return;
|
||||
}
|
||||
|
||||
CProcess proc("wl-copy", {*info});
|
||||
if (!proc.runAsync()) { // FIXME: why does runSync hang?!
|
||||
e->rebuild()->label("Failed copying")->commence();
|
||||
return;
|
||||
}
|
||||
|
||||
e->rebuild()->label("Copied!")->commence();
|
||||
})
|
||||
->commence() //
|
||||
);
|
||||
|
||||
bottomLayout->addChild( //
|
||||
CButtonBuilder::begin()
|
||||
->label("Exit")
|
||||
->onMainClick([w = WP<IWindow>{window}](SP<CButtonElement> e) {
|
||||
w->close();
|
||||
backend->destroy();
|
||||
})
|
||||
->commence() //
|
||||
);
|
||||
|
||||
//
|
||||
|
||||
bottomNull->addChild(bottomLayout);
|
||||
window->m_rootElement->addChild(bottomNull);
|
||||
|
||||
window->m_events.closeRequest.listenStatic([w = WP<IWindow>{window}] {
|
||||
w->close();
|
||||
backend->destroy();
|
||||
});
|
||||
|
||||
window->open();
|
||||
|
||||
backend->enterLoop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
207
src/main.qml
207
src/main.qml
|
|
@ -1,207 +0,0 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import org.hyprland.systeminfo
|
||||
|
||||
ApplicationWindow {
|
||||
id: window
|
||||
|
||||
FontMetrics { id: fontMetrics }
|
||||
|
||||
property var firstPanelHeight: fontMetrics.height * 7
|
||||
|
||||
minimumWidth: Math.max(fontMetrics.height * 50, mainLayout.Layout.minimumWidth) + mainLayout.anchors.margins * 2
|
||||
minimumHeight: Math.max(fontMetrics.height * 30, mainLayout.Layout.minimumHeight) + mainLayout.anchors.margins * 2
|
||||
maximumWidth: minimumWidth
|
||||
maximumHeight: minimumHeight
|
||||
visible: true
|
||||
|
||||
component Separator: Rectangle {
|
||||
color: Qt.darker(window.palette.text, 1.5)
|
||||
}
|
||||
|
||||
component VSeparator: Separator {
|
||||
implicitWidth: 1
|
||||
Layout.fillHeight: true
|
||||
Layout.topMargin: fontMetrics.height
|
||||
Layout.bottomMargin: fontMetrics.height
|
||||
}
|
||||
|
||||
component HSeparator: Separator {
|
||||
implicitHeight: 1
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: fontMetrics.height * 8
|
||||
Layout.rightMargin: fontMetrics.height * 8
|
||||
}
|
||||
|
||||
SystemPalette {
|
||||
id: system
|
||||
colorGroup: SystemPalette.Active
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
spacing: fontMetrics.height
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: 4
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
// First panel hyprland and distro info
|
||||
Layout.preferredHeight: firstPanelHeight
|
||||
Layout.maximumHeight: firstPanelHeight
|
||||
Layout.topMargin: fontMetrics.height
|
||||
spacing: fontMetrics.height
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
RowLayout {
|
||||
id: distroLogoName
|
||||
spacing: fontMetrics.height
|
||||
|
||||
Image {
|
||||
visible: SystemInfo.systemLogoName != ""
|
||||
source: "image://systemIcons/" + SystemInfo.systemLogoName
|
||||
sourceSize.width: firstPanelHeight
|
||||
sourceSize.height: firstPanelHeight
|
||||
Layout.preferredWidth: firstPanelHeight
|
||||
Layout.preferredHeight: firstPanelHeight
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
smooth: true
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: distroText
|
||||
|
||||
Layout.preferredWidth: hyprlandInfo.visible ? Math.max(Layout.minimumWidth, hyprlandText.Layout.minimumWidth) : Layout.minimumWidth
|
||||
spacing: 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
Label {
|
||||
color: system.windowText
|
||||
text: SystemInfo.systemName
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
Label {
|
||||
color: system.windowText
|
||||
text: SystemInfo.systemUrl
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
Label {
|
||||
color: system.windowText
|
||||
text: SystemInfo.systemKernel
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { visible: hyprlandInfo.visible; Layout.fillWidth: true }
|
||||
VSeparator { visible: hyprlandInfo.visible }
|
||||
Item { visible: hyprlandInfo.visible; Layout.fillWidth: true }
|
||||
|
||||
RowLayout {
|
||||
id: hyprlandInfo
|
||||
visible: SystemInfo.hyprlandVersionLong != ""
|
||||
spacing: fontMetrics.height
|
||||
|
||||
Image {
|
||||
source: "qrc:/resource/hyprlandlogo.svg"
|
||||
sourceSize.width: firstPanelHeight
|
||||
sourceSize.height: firstPanelHeight
|
||||
Layout.preferredWidth: firstPanelHeight
|
||||
Layout.preferredHeight: firstPanelHeight
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
smooth: true
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: hyprlandText
|
||||
Layout.preferredWidth: Math.max(Layout.minimumWidth, distroText.Layout.minimumWidth)
|
||||
spacing: 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
Label {
|
||||
color: system.windowText
|
||||
text: "Hyprland"
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
Label {
|
||||
color: system.windowText
|
||||
text: SystemInfo.hyprlandVersion
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
Label {
|
||||
color: system.windowText
|
||||
visible: SystemInfo.hyprlandVersion != text
|
||||
text: SystemInfo.hyprlandVersionLong
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
}
|
||||
|
||||
HSeparator {}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 6
|
||||
Layout.leftMargin: fontMetrics.height * 4
|
||||
Layout.rightMargin: fontMetrics.height * 4
|
||||
|
||||
component DetailsLabel: Label {
|
||||
color: system.windowText
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
|
||||
DetailsLabel { text: "User: " + SystemInfo.user; visible: text != "" }
|
||||
DetailsLabel { text: "Model: " + SystemInfo.model; visible: text != "" }
|
||||
DetailsLabel { text: "CPU: " + SystemInfo.cpuInfo }
|
||||
|
||||
Repeater {
|
||||
model: SystemInfo.gpuInfo
|
||||
|
||||
DetailsLabel {
|
||||
required property string modelData
|
||||
text: "GPU: " + modelData
|
||||
}
|
||||
}
|
||||
|
||||
DetailsLabel { text: "Memory: " + SystemInfo.ramInfo }
|
||||
DetailsLabel { text: "DE: " + SystemInfo.de }
|
||||
DetailsLabel { text: "Uptime: " + SystemInfo.uptime }
|
||||
DetailsLabel { text: "Displays: " + SystemInfo.screens }
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
HSeparator {}
|
||||
|
||||
RowLayout {
|
||||
visible: SystemInfo.hyprlandVersionLong != ""
|
||||
spacing: 6
|
||||
Layout.leftMargin: 20
|
||||
Layout.rightMargin: 20
|
||||
|
||||
Button {
|
||||
text: "Copy Hyprland System Info"
|
||||
onClicked: SystemInfo.copySystemInfo();
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Copy Hyprland Version"
|
||||
onClicked: SystemInfo.copyVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
#include "Utils.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <qdebug.h>
|
||||
#include <qlogging.h>
|
||||
#include <qprocess.h>
|
||||
#include <optional>
|
||||
|
||||
QString execAndGet(const QString& program, const QStringList& arguments, bool* ok) {
|
||||
QProcess process;
|
||||
process.setProcessChannelMode(QProcess::SeparateChannels);
|
||||
process.start(program, arguments, QIODevice::ReadOnly);
|
||||
|
||||
if (!process.waitForStarted(-1)) {
|
||||
qCritical() << "Failed to start process" << program << arguments;
|
||||
if (ok)
|
||||
*ok = false;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
if (!process.waitForFinished(-1)) {
|
||||
qCritical() << "Failed to run process" << program << arguments;
|
||||
if (ok)
|
||||
*ok = false;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ok)
|
||||
*ok = true;
|
||||
return process.readAll();
|
||||
}
|
||||
|
||||
std::optional<std::string> readFile(const std::string& filename) {
|
||||
try {
|
||||
std::ifstream ifs(filename);
|
||||
if (ifs.good()) {
|
||||
std::string data(std::istreambuf_iterator<char>{ifs}, {});
|
||||
ifs.close();
|
||||
return data;
|
||||
}
|
||||
} catch (...) {}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <qlist.h>
|
||||
#include <qstring.h>
|
||||
#include <optional>
|
||||
|
||||
QString execAndGet(const QString& program, const QStringList& arguments = {}, bool* ok = nullptr);
|
||||
std::optional<std::string> readFile(const std::string& filename);
|
||||
86
src/utils/HyprlandIPC.cpp
Normal file
86
src/utils/HyprlandIPC.cpp
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#include "HyprlandIPC.hpp"
|
||||
|
||||
#include <pwd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <format>
|
||||
|
||||
#include <hyprutils/memory/Casts.hpp>
|
||||
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
static int getUID() {
|
||||
const auto UID = getuid();
|
||||
const auto PWUID = getpwuid(UID);
|
||||
return PWUID ? PWUID->pw_uid : UID;
|
||||
}
|
||||
|
||||
static std::string getRuntimeDir() {
|
||||
const auto XDG = getenv("XDG_RUNTIME_DIR");
|
||||
|
||||
if (!XDG) {
|
||||
const std::string USERID = std::to_string(getUID());
|
||||
return "/run/user/" + USERID + "/hypr";
|
||||
}
|
||||
|
||||
return std::string{XDG} + "/hypr";
|
||||
}
|
||||
|
||||
std::expected<std::string, std::string> HyprlandIPC::getFromSocket(const std::string& cmd) {
|
||||
static const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||
|
||||
if (!HIS || HIS[0] == '\0')
|
||||
return std::unexpected("HYPRLAND_INSTANCE_SIGNATURE empty: are we under hyprland?");
|
||||
|
||||
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
auto t = timeval{.tv_sec = 5, .tv_usec = 0};
|
||||
setsockopt(SERVERSOCKET, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(struct timeval));
|
||||
|
||||
if (SERVERSOCKET < 0)
|
||||
return std::unexpected("couldn't open a socket (1)");
|
||||
|
||||
sockaddr_un serverAddress = {0};
|
||||
serverAddress.sun_family = AF_UNIX;
|
||||
|
||||
std::string socketPath = getRuntimeDir() + "/" + HIS + "/.socket.sock";
|
||||
|
||||
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
|
||||
|
||||
if (connect(SERVERSOCKET, rc<sockaddr*>(&serverAddress), SUN_LEN(&serverAddress)) < 0)
|
||||
return std::unexpected(std::format("couldn't connect to the hyprland socket at {}", socketPath));
|
||||
|
||||
auto sizeWritten = write(SERVERSOCKET, cmd.c_str(), cmd.length());
|
||||
|
||||
if (sizeWritten < 0)
|
||||
return std::unexpected("couldn't write (4)");
|
||||
|
||||
std::string reply = "";
|
||||
char buffer[8192] = {0};
|
||||
|
||||
sizeWritten = read(SERVERSOCKET, buffer, 8192);
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
if (errno == EWOULDBLOCK)
|
||||
return std::unexpected("Hyprland IPC didn't respond in time");
|
||||
return std::unexpected("couldn't read (5)");
|
||||
}
|
||||
|
||||
reply += std::string(buffer, sizeWritten);
|
||||
|
||||
while (sizeWritten == 8192) {
|
||||
sizeWritten = read(SERVERSOCKET, buffer, 8192);
|
||||
if (sizeWritten < 0) {
|
||||
return std::unexpected("couldn't read (5)");
|
||||
}
|
||||
reply += std::string(buffer, sizeWritten);
|
||||
}
|
||||
|
||||
close(SERVERSOCKET);
|
||||
|
||||
return reply;
|
||||
}
|
||||
8
src/utils/HyprlandIPC.hpp
Normal file
8
src/utils/HyprlandIPC.hpp
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <expected>
|
||||
|
||||
namespace HyprlandIPC {
|
||||
std::expected<std::string, std::string> getFromSocket(const std::string& cmd);
|
||||
};
|
||||
358
src/utils/SystemInfo.cpp
Normal file
358
src/utils/SystemInfo.cpp
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
#include "SystemInfo.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <format>
|
||||
#include <algorithm>
|
||||
|
||||
#include <hyprutils/os/File.hpp>
|
||||
#include <hyprutils/string/VarList2.hpp>
|
||||
#include <hyprutils/string/String.hpp>
|
||||
|
||||
#include "HyprlandIPC.hpp"
|
||||
#include "../helpers/Logger.hpp"
|
||||
#include "../icons/Icons.hpp"
|
||||
|
||||
#include <sys/utsname.h>
|
||||
#include <glaze/glaze.hpp>
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <linux/sysinfo.h>
|
||||
#include <unistd.h>
|
||||
#include <glob.h>
|
||||
#include <pci/pci.h>
|
||||
|
||||
using namespace Hyprutils::File;
|
||||
using namespace Hyprutils::String;
|
||||
|
||||
std::optional<std::string> Info::getFromEtcOsRelease(const std::string_view& sv) {
|
||||
static std::string content = "";
|
||||
static bool once = true;
|
||||
|
||||
if (once) {
|
||||
once = false;
|
||||
|
||||
auto read = readFileAsString("/etc/os-release");
|
||||
content = read.value_or("");
|
||||
}
|
||||
|
||||
static CVarList2 vars(std::move(content), 0, '\n', true);
|
||||
|
||||
for (const auto& v : vars) {
|
||||
if (v.starts_with(sv) && v.contains('=')) {
|
||||
// found
|
||||
auto value = trim(v.substr(v.find('=') + 1));
|
||||
|
||||
if (value.back() == value.front() && value.back() == '"')
|
||||
value = value.substr(1, value.size() - 2);
|
||||
|
||||
return std::string{value};
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::expected<std::string, std::string> Info::getDistroLogoName() {
|
||||
auto v = getFromEtcOsRelease("LOGO");
|
||||
if (!v)
|
||||
return std::unexpected("Prop missing");
|
||||
return *v;
|
||||
}
|
||||
|
||||
std::string Info::kernel() {
|
||||
struct utsname un;
|
||||
|
||||
if (::uname(&un) != 0)
|
||||
return "";
|
||||
|
||||
return std::string(un.release);
|
||||
}
|
||||
|
||||
std::string Info::desktop() {
|
||||
static std::string x = [] -> std::string {
|
||||
auto ENV = getenv("XDG_CURRENT_DESKTOP");
|
||||
|
||||
if (!ENV)
|
||||
return "Unknown";
|
||||
|
||||
if (!std::string_view{ENV}.contains(':'))
|
||||
return ENV;
|
||||
|
||||
CVarList2 vl(ENV, 0, ':', true);
|
||||
|
||||
return std::string{vl[0]};
|
||||
}();
|
||||
return x;
|
||||
}
|
||||
|
||||
std::string Info::desktopHome() {
|
||||
static std::string x = [] -> std::string {
|
||||
auto D = desktop();
|
||||
std::ranges::transform(D, D.begin(), ::tolower);
|
||||
|
||||
if (D == "hyprland")
|
||||
return "https://hypr.land/";
|
||||
if (D == "sway")
|
||||
return "https://swaywm.org/";
|
||||
if (D == "wayfire")
|
||||
return "https://wayfire.org/";
|
||||
if (D == "kde")
|
||||
return "https://kde.org/";
|
||||
if (D == "gnome")
|
||||
return "https://gnome.org/";
|
||||
|
||||
return "";
|
||||
}();
|
||||
return x;
|
||||
}
|
||||
|
||||
std::string Info::desktopVersion() {
|
||||
static std::string x = [] -> std::string {
|
||||
auto D = desktop();
|
||||
std::ranges::transform(D, D.begin(), ::tolower);
|
||||
|
||||
if (D == "hyprland") {
|
||||
auto res = HyprlandIPC::getFromSocket("j/version");
|
||||
|
||||
if (res) {
|
||||
auto json = glz::read_json<glz::generic>(*res);
|
||||
if (json && json->contains("tag"))
|
||||
return (*json)["tag"].get_string();
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}();
|
||||
return x;
|
||||
}
|
||||
|
||||
std::string Info::user() {
|
||||
std::string username, host;
|
||||
|
||||
uid_t uid = geteuid();
|
||||
struct passwd pwd, *result = NULL;
|
||||
char buf[16384];
|
||||
|
||||
if (getpwuid_r(uid, &pwd, buf, sizeof(buf), &result) == 0 && result)
|
||||
username = pwd.pw_name;
|
||||
|
||||
if (gethostname(buf, sizeof(buf)) == 0) {
|
||||
buf[HOST_NAME_MAX] = '\0';
|
||||
host = buf;
|
||||
}
|
||||
|
||||
return std::format("{}@{}", username, host);
|
||||
}
|
||||
|
||||
std::string Info::model() {
|
||||
auto manu = cat("/sys/class/dmi/id/sys_vendor");
|
||||
auto model = cat("/sys/class/dmi/id/product_name");
|
||||
|
||||
if (manu && model) {
|
||||
if (model->starts_with(*manu))
|
||||
return *model;
|
||||
return std::format("{} {}", *manu, *model);
|
||||
}
|
||||
|
||||
if (manu)
|
||||
return *manu;
|
||||
if (model)
|
||||
return *model;
|
||||
return "";
|
||||
}
|
||||
|
||||
std::optional<std::string> Info::cat(const std::filesystem::path& p) {
|
||||
auto f = readFileAsString(p.string());
|
||||
|
||||
if (!f)
|
||||
return std::nullopt;
|
||||
|
||||
return trim(*f);
|
||||
}
|
||||
|
||||
static std::string formatKHz(const uint64_t khz) {
|
||||
if (khz >= 1000000)
|
||||
return std::format("{:.1f}GHz", khz / 1000000.F);
|
||||
return std::format("{:.1f}MHz", khz / 1000.F);
|
||||
}
|
||||
|
||||
static std::optional<std::string> getFromCpuinfo(const std::string_view& sv) {
|
||||
static std::string content = "";
|
||||
static bool once = true;
|
||||
|
||||
if (once) {
|
||||
once = false;
|
||||
|
||||
auto read = readFileAsString("/proc/cpuinfo");
|
||||
content = read.value_or("");
|
||||
}
|
||||
|
||||
static CVarList2 vars(std::move(content), 0, '\n', true);
|
||||
|
||||
for (const auto& v : vars) {
|
||||
if (v.starts_with(sv) && v.contains(':')) {
|
||||
// found
|
||||
auto value = trim(v.substr(v.find(':') + 2));
|
||||
|
||||
if (value.back() == value.front() && value.back() == '"')
|
||||
value = value.substr(1, value.size() - 2);
|
||||
|
||||
return std::string{value};
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string Info::cpu() {
|
||||
auto nproc = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
std::string hz = "?GHz";
|
||||
try {
|
||||
hz = formatKHz(std::stoull(cat("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq").value()));
|
||||
} catch (...) {
|
||||
; // oh well
|
||||
}
|
||||
|
||||
struct utsname u;
|
||||
std::string arch;
|
||||
if (uname(&u) == 0)
|
||||
arch = u.machine;
|
||||
|
||||
// try to read proc names from /proc/cpuinfo
|
||||
std::string cpuname = getFromCpuinfo("model name").value_or("Unknown");
|
||||
|
||||
return std::format("{} {}x{} ({})", cpuname, nproc, hz, arch);
|
||||
}
|
||||
|
||||
static std::optional<uint32_t> readU32Hex(const std::string& p) {
|
||||
auto str = readFileAsString(p);
|
||||
|
||||
if (!str)
|
||||
return std::nullopt;
|
||||
|
||||
auto s = std::move(*str);
|
||||
|
||||
if (s.rfind("0x", 0) == 0)
|
||||
s.erase(0, 2);
|
||||
|
||||
try {
|
||||
return sc<uint32_t>(std::stoul(s, nullptr, 16));
|
||||
} catch (...) { return std::nullopt; }
|
||||
}
|
||||
|
||||
std::vector<std::string> Info::gpus() {
|
||||
glob_t g = {0};
|
||||
if (glob("/sys/class/drm/card[0-9]*", 0, NULL, &g) != 0)
|
||||
return {};
|
||||
|
||||
struct pci_access* pacc = pci_alloc();
|
||||
pci_init(pacc);
|
||||
pci_scan_bus(pacc);
|
||||
|
||||
std::vector<std::string> gpuNames;
|
||||
|
||||
for (size_t i = 0; i < g.gl_pathc; i++) {
|
||||
const char* cardpath = g.gl_pathv[i];
|
||||
|
||||
std::filesystem::path vendorPath = std::filesystem::path{cardpath} / "device" / "vendor";
|
||||
std::filesystem::path devicePath = std::filesystem::path{cardpath} / "device" / "device";
|
||||
std::filesystem::path classPath = std::filesystem::path{cardpath} / "device" / "class";
|
||||
|
||||
uint32_t vendor = 0, device = 0;
|
||||
|
||||
if (auto ret = readU32Hex(vendorPath.string()); ret)
|
||||
vendor = *ret;
|
||||
else
|
||||
continue;
|
||||
|
||||
if (auto ret = readU32Hex(devicePath.string()); ret)
|
||||
device = *ret;
|
||||
else
|
||||
continue;
|
||||
|
||||
if (auto ret = readU32Hex(classPath.string()); ret) {
|
||||
if (((*ret) & 0xFF0000u) != 0x030000u) // only keep PCI class 0x03xxxx (display controller)
|
||||
continue;
|
||||
}
|
||||
|
||||
static char namebuf[512] = {0};
|
||||
pci_lookup_name(pacc, namebuf, sizeof(namebuf), PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE, vendor, device);
|
||||
|
||||
gpuNames.emplace_back(namebuf);
|
||||
}
|
||||
|
||||
pci_cleanup(pacc);
|
||||
globfree(&g);
|
||||
|
||||
return gpuNames;
|
||||
}
|
||||
|
||||
static uint64_t toBytes(uint64_t v, uint64_t unit) {
|
||||
return v * unit;
|
||||
}
|
||||
|
||||
static std::string formatBytes(uint64_t bytes) {
|
||||
// If you have more than 1TiB of RAM then holy fucking shit
|
||||
if (bytes > 1024 * 1024 * 1024 * 1024ULL)
|
||||
return std::format("{:.1f}TiB", bytes / sc<float>(1024 * 1024 * 1024 * 1024ULL));
|
||||
if (bytes > 1024 * 1024 * 1024)
|
||||
return std::format("{:.1f}GiB", bytes / sc<float>(1024 * 1024 * 1024));
|
||||
if (bytes > 1024 * 1024)
|
||||
return std::format("{:.1f}MiB", bytes / sc<float>(1024 * 1024));
|
||||
if (bytes > 1024)
|
||||
return std::format("{:.1f}KiB", bytes / sc<float>(1024));
|
||||
return std::format("{}B", bytes);
|
||||
}
|
||||
|
||||
std::string Info::mem() {
|
||||
struct sysinfo info{};
|
||||
if (sysinfo(&info) != 0)
|
||||
return "";
|
||||
|
||||
const auto TOTAL = toBytes(info.totalram, info.mem_unit);
|
||||
auto FREE = toBytes(info.freeram + info.bufferram, info.mem_unit);
|
||||
|
||||
if (const auto meminfo = cat("/proc/meminfo"); meminfo) {
|
||||
CVarList2 vl(std::string{*meminfo}, 0, '\n', true, true);
|
||||
for (const auto& l : vl) {
|
||||
if (!l.starts_with("MemAvailable"))
|
||||
continue;
|
||||
|
||||
CVarList2 ln(std::string{l}, 0, 's', true, false);
|
||||
|
||||
try {
|
||||
FREE = toBytes(std::stoull(std::string{ln[1]}), 1000);
|
||||
} catch (...) {}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return std::format("{} / {} ({:.0f}% used)", formatBytes(TOTAL - FREE), formatBytes(TOTAL), sc<float>((TOTAL - FREE) / sc<float>(TOTAL)) * 100.F);
|
||||
}
|
||||
|
||||
std::string Info::uptime() {
|
||||
struct sysinfo info{};
|
||||
if (::sysinfo(&info) != 0)
|
||||
return "";
|
||||
|
||||
uint64_t s = sc<uint64_t>(info.uptime);
|
||||
|
||||
const auto DAYS = s / 86400;
|
||||
s %= 86400;
|
||||
const auto HRS = s / 3600;
|
||||
s %= 3600;
|
||||
const auto MINS = s / 60;
|
||||
s %= 60;
|
||||
|
||||
return std::format("{} days, {} hours, {} minutes", DAYS, HRS, MINS);
|
||||
}
|
||||
|
||||
std::optional<std::span<const uint8_t>> Info::deLogo() {
|
||||
const auto DE = desktop();
|
||||
|
||||
if (DE == "Hyprland")
|
||||
return std::span<const uint8_t>{Icons::HYPRLAND_LOGO, sizeof(Icons::HYPRLAND_LOGO)};
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
24
src/utils/SystemInfo.hpp
Normal file
24
src/utils/SystemInfo.hpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <expected>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
namespace Info {
|
||||
std::expected<std::string, std::string> getDistroLogoName();
|
||||
std::optional<std::string> getFromEtcOsRelease(const std::string_view& sv);
|
||||
std::string kernel();
|
||||
std::string desktop();
|
||||
std::string desktopHome();
|
||||
std::string desktopVersion();
|
||||
std::string user();
|
||||
std::optional<std::string> cat(const std::filesystem::path& p);
|
||||
std::string model();
|
||||
std::string cpu();
|
||||
std::vector<std::string> gpus();
|
||||
std::string mem();
|
||||
std::string uptime();
|
||||
std::optional<std::span<const uint8_t>> deLogo();
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue