core: Add support for hyprqtutils' update screen (#8651)

* Nix: add hyprland-qtutils to PATH

* flake.lock: update

---------

Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
This commit is contained in:
Vaxry 2024-12-06 15:45:02 +01:00 committed by GitHub
parent 5ff02902ee
commit b1e5cc66bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 270 additions and 13 deletions

View file

@ -141,6 +141,32 @@
"type": "github" "type": "github"
} }
}, },
"hyprland-qtutils": {
"inputs": {
"hyprutils": [
"hyprutils"
],
"nixpkgs": [
"nixpkgs"
],
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1733472316,
"narHash": "sha256-PvXiFLIExJEJj+goLbIuXLTN5CSDSAUsAfiYSdbbWg0=",
"owner": "hyprwm",
"repo": "hyprland-qtutils",
"rev": "969427419276c7ee170301ef1ebe0f68eb6eb2e2",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprland-qtutils",
"type": "github"
}
},
"hyprlang": { "hyprlang": {
"inputs": { "inputs": {
"hyprutils": [ "hyprutils": [
@ -215,11 +241,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1732758367, "lastModified": 1733392399,
"narHash": "sha256-RzaI1RO0UXqLjydtz3GAXSTzHkpb/lLD1JD8a0W4Wpo=", "narHash": "sha256-kEsTJTUQfQFIJOcLYFt/RvNxIK653ZkTBIs4DG+cBns=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "fa42b5a5f401aab8a32bd33c9a4de0738180dc59", "rev": "d0797a04b81caeae77bcff10a9dde78bc17f5661",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -255,11 +281,11 @@
"nixpkgs-stable": "nixpkgs-stable" "nixpkgs-stable": "nixpkgs-stable"
}, },
"locked": { "locked": {
"lastModified": 1732021966, "lastModified": 1733318908,
"narHash": "sha256-mnTbjpdqF0luOkou8ZFi2asa1N3AA2CchR/RqCNmsGE=", "narHash": "sha256-SVQVsbafSM1dJ4fpgyBqLZ+Lft+jcQuMtEL3lQWx2Sk=",
"owner": "cachix", "owner": "cachix",
"repo": "git-hooks.nix", "repo": "git-hooks.nix",
"rev": "3308484d1a443fc5bc92012435d79e80458fe43c", "rev": "6f4e2a2112050951a314d2733a994fbab94864c6",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -274,6 +300,7 @@
"hyprcursor": "hyprcursor", "hyprcursor": "hyprcursor",
"hyprgraphics": "hyprgraphics", "hyprgraphics": "hyprgraphics",
"hyprland-protocols": "hyprland-protocols", "hyprland-protocols": "hyprland-protocols",
"hyprland-qtutils": "hyprland-qtutils",
"hyprlang": "hyprlang", "hyprlang": "hyprlang",
"hyprutils": "hyprutils", "hyprutils": "hyprutils",
"hyprwayland-scanner": "hyprwayland-scanner", "hyprwayland-scanner": "hyprwayland-scanner",
@ -320,11 +347,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1731703417, "lastModified": 1733157064,
"narHash": "sha256-rheDc/7C+yI+QspYr9J2z9kQ5P9F4ATapI7qyFAe1XA=", "narHash": "sha256-NetqJHAN4bbZDQADvpep+wXk2AbMZ2bN6tINz8Kpz6M=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland", "repo": "xdg-desktop-portal-hyprland",
"rev": "8070f36deec723de71e7557441acb17e478204d3", "rev": "fd85ef39369f95eed67fdf3f025e86916edeea2f",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -35,6 +35,13 @@
inputs.systems.follows = "systems"; inputs.systems.follows = "systems";
}; };
hyprland-qtutils = {
url = "github:hyprwm/hyprland-qtutils";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
inputs.hyprutils.follows = "hyprutils";
};
hyprlang = { hyprlang = {
url = "github:hyprwm/hyprlang"; url = "github:hyprwm/hyprlang";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
@ -123,13 +130,11 @@
inherit inherit
(pkgsFor.${system}) (pkgsFor.${system})
# hyprland-packages # hyprland-packages
hyprland hyprland
hyprland-debug hyprland-debug
hyprland-legacy-renderer hyprland-legacy-renderer
hyprland-unwrapped hyprland-unwrapped
# hyprland-extras # hyprland-extras
xdg-desktop-portal-hyprland xdg-desktop-portal-hyprland
; ;
hyprland-cross = (pkgsCrossFor.${system} "aarch64-linux").hyprland; hyprland-cross = (pkgsCrossFor.${system} "aarch64-linux").hyprland;

View file

@ -14,6 +14,7 @@
hyprcursor, hyprcursor,
hyprgraphics, hyprgraphics,
hyprland-protocols, hyprland-protocols,
hyprland-qtutils,
hyprlang, hyprlang,
hyprutils, hyprutils,
hyprwayland-scanner, hyprwayland-scanner,
@ -168,6 +169,7 @@ in
wrapProgram $out/bin/Hyprland \ wrapProgram $out/bin/Hyprland \
--suffix PATH : ${makeBinPath [ --suffix PATH : ${makeBinPath [
binutils binutils
hyprland-qtutils
pciutils pciutils
pkgconf pkgconf
]} ]}

View file

@ -24,6 +24,7 @@ in {
inputs.hyprcursor.overlays.default inputs.hyprcursor.overlays.default
inputs.hyprgraphics.overlays.default inputs.hyprgraphics.overlays.default
inputs.hyprland-protocols.overlays.default inputs.hyprland-protocols.overlays.default
inputs.hyprland-qtutils.overlays.default
inputs.hyprlang.overlays.default inputs.hyprlang.overlays.default
inputs.hyprutils.overlays.default inputs.hyprutils.overlays.default
inputs.hyprwayland-scanner.overlays.default inputs.hyprwayland-scanner.overlays.default

View file

@ -6,6 +6,7 @@
#include "managers/TokenManager.hpp" #include "managers/TokenManager.hpp"
#include "managers/PointerManager.hpp" #include "managers/PointerManager.hpp"
#include "managers/SeatManager.hpp" #include "managers/SeatManager.hpp"
#include "managers/VersionKeeperManager.hpp"
#include "managers/eventLoop/EventLoopManager.hpp" #include "managers/eventLoop/EventLoopManager.hpp"
#include <aquamarine/output/Output.hpp> #include <aquamarine/output/Output.hpp>
#include <bit> #include <bit>
@ -645,6 +646,9 @@ void CCompositor::initManagers(eManagersInitStage stage) {
Debug::log(LOG, "Creating the CursorManager!"); Debug::log(LOG, "Creating the CursorManager!");
g_pCursorManager = std::make_unique<CCursorManager>(); g_pCursorManager = std::make_unique<CCursorManager>();
Debug::log(LOG, "Creating the VersionKeeper!");
g_pVersionKeeperMgr = std::make_unique<CVersionKeeperManager>();
Debug::log(LOG, "Starting XWayland"); Debug::log(LOG, "Starting XWayland");
g_pXWayland = std::make_unique<CXWayland>(g_pCompositor->m_bEnableXwayland); g_pXWayland = std::make_unique<CXWayland>(g_pCompositor->m_bEnableXwayland);
} break; } break;
@ -2611,6 +2615,7 @@ WORKSPACEID CCompositor::getNewSpecialID() {
void CCompositor::performUserChecks() { void CCompositor::performUserChecks() {
static auto PNOCHECKXDG = CConfigValue<Hyprlang::INT>("misc:disable_xdg_env_checks"); static auto PNOCHECKXDG = CConfigValue<Hyprlang::INT>("misc:disable_xdg_env_checks");
static auto PNOCHECKQTUTILS = CConfigValue<Hyprlang::INT>("misc:disable_hyprland_qtutils_check");
if (!*PNOCHECKXDG) { if (!*PNOCHECKXDG) {
const auto CURRENT_DESKTOP_ENV = getenv("XDG_CURRENT_DESKTOP"); const auto CURRENT_DESKTOP_ENV = getenv("XDG_CURRENT_DESKTOP");
@ -2622,6 +2627,13 @@ void CCompositor::performUserChecks() {
} }
} }
if (!*PNOCHECKQTUTILS) {
if (!executableExistsInPath("hyprland-dialog")) {
g_pHyprNotificationOverlay->addNotification(
"Your system does not have hyprland-qtutils installed. This is a runtime dependency for some dialogs. Consider installing it.", CHyprColor{}, 15000, ICON_WARNING);
}
}
if (g_pHyprOpenGL->failedAssetsNo > 0) { if (g_pHyprOpenGL->failedAssetsNo > 0) {
g_pHyprNotificationOverlay->addNotification(std::format("Hyprland failed to load {} essential asset{}, blame your distro's packager for doing a bad job at packaging!", g_pHyprNotificationOverlay->addNotification(std::format("Hyprland failed to load {} essential asset{}, blame your distro's packager for doing a bad job at packaging!",
g_pHyprOpenGL->failedAssetsNo, g_pHyprOpenGL->failedAssetsNo > 1 ? "s" : ""), g_pHyprOpenGL->failedAssetsNo, g_pHyprOpenGL->failedAssetsNo > 1 ? "s" : ""),

View file

@ -1133,6 +1133,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL, .type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false}, .data = SConfigOptionDescription::SBoolData{false},
}, },
SConfigOptionDescription{
.value = "misc:disable_hyprland_qtutils_check",
.description = "disable the warning if hyprland-qtutils is missing",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{ SConfigOptionDescription{
.value = "misc:lockdead_screen_delay", .value = "misc:lockdead_screen_delay",
.description = "the delay in ms after the lockdead screen appears if the lock screen did not appear after a lock event occurred.", .description = "the delay in ms after the lockdead screen appears if the lock screen did not appear after a lock event occurred.",

View file

@ -383,6 +383,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("misc:middle_click_paste", Hyprlang::INT{1}); m_pConfig->addConfigValue("misc:middle_click_paste", Hyprlang::INT{1});
m_pConfig->addConfigValue("misc:render_unfocused_fps", Hyprlang::INT{15}); m_pConfig->addConfigValue("misc:render_unfocused_fps", Hyprlang::INT{15});
m_pConfig->addConfigValue("misc:disable_xdg_env_checks", Hyprlang::INT{0}); m_pConfig->addConfigValue("misc:disable_xdg_env_checks", Hyprlang::INT{0});
m_pConfig->addConfigValue("misc:disable_hyprland_qtutils_check", Hyprlang::INT{0});
m_pConfig->addConfigValue("misc:lockdead_screen_delay", Hyprlang::INT{1000}); m_pConfig->addConfigValue("misc:lockdead_screen_delay", Hyprlang::INT{1000});
m_pConfig->addConfigValue("group:insert_after_current", Hyprlang::INT{1}); m_pConfig->addConfigValue("group:insert_after_current", Hyprlang::INT{1});
@ -606,6 +607,8 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("render:direct_scanout", Hyprlang::INT{0}); m_pConfig->addConfigValue("render:direct_scanout", Hyprlang::INT{0});
m_pConfig->addConfigValue("render:expand_undersized_textures", Hyprlang::INT{1}); m_pConfig->addConfigValue("render:expand_undersized_textures", Hyprlang::INT{1});
m_pConfig->addConfigValue("ecosystem:no_update_news", Hyprlang::INT{0});
// devices // devices
m_pConfig->addSpecialCategory("device", {"name"}); m_pConfig->addSpecialCategory("device", {"name"});
m_pConfig->addSpecialConfigValue("device", "sensitivity", {0.F}); m_pConfig->addSpecialConfigValue("device", "sensitivity", {0.F});

View file

@ -930,4 +930,34 @@ float stringToPercentage(const std::string& VALUE, const float REL) {
return (std::stof(VALUE.substr(0, VALUE.length() - 1)) * REL) / 100.f; return (std::stof(VALUE.substr(0, VALUE.length() - 1)) * REL) / 100.f;
else else
return std::stof(VALUE); return std::stof(VALUE);
}; }
bool executableExistsInPath(const std::string& exe) {
if (!getenv("PATH"))
return false;
static CVarList paths(getenv("PATH"), 0, ':', true);
for (auto& p : paths) {
std::string path = p + std::string{"/"} + exe;
std::error_code ec;
if (!std::filesystem::exists(path, ec) || ec)
continue;
if (!std::filesystem::is_regular_file(path, ec) || ec)
continue;
auto stat = std::filesystem::status(path, ec);
if (ec)
continue;
auto perms = stat.permissions();
if (std::filesystem::perms::none == (perms & std::filesystem::perms::others_exec))
return false;
return true;
}
return false;
}

View file

@ -42,6 +42,7 @@ bool envEnabled(const std::string& env);
int allocateSHMFile(size_t len); int allocateSHMFile(size_t len);
bool allocateSHMFilePair(size_t size, int* rw_fd_ptr, int* ro_fd_ptr); bool allocateSHMFilePair(size_t size, int* rw_fd_ptr, int* ro_fd_ptr);
float stringToPercentage(const std::string& VALUE, const float REL); float stringToPercentage(const std::string& VALUE, const float REL);
bool executableExistsInPath(const std::string& exe);
template <typename... Args> template <typename... Args>
[[deprecated("use std::format instead")]] std::string getFormat(std::format_string<Args...> fmt, Args&&... args) { [[deprecated("use std::format instead")]] std::string getFormat(std::format_string<Args...> fmt, Args&&... args) {

View file

@ -0,0 +1,153 @@
#include "VersionKeeperManager.hpp"
#include "../debug/Log.hpp"
#include "../macros.hpp"
#include "../version.h"
#include "../helpers/MiscFunctions.hpp"
#include "../helpers/varlist/VarList.hpp"
#include "eventLoop/EventLoopManager.hpp"
#include "../config/ConfigValue.hpp"
#include <filesystem>
#include <fstream>
#include <hyprutils/string/String.hpp>
#include <hyprutils/os/Process.hpp>
using namespace Hyprutils::String;
using namespace Hyprutils::OS;
constexpr const char* VERSION_FILE_NAME = "lastVersion";
CVersionKeeperManager::CVersionKeeperManager() {
static auto PNONOTIFY = CConfigValue<Hyprlang::INT>("ecosystem:no_update_news");
const auto DATAROOT = getDataHome();
if (!DATAROOT)
return;
const auto LASTVER = getDataLastVersion(*DATAROOT);
if (!LASTVER)
return;
if (!isVersionOlderThanRunning(*LASTVER)) {
Debug::log(LOG, "CVersionKeeperManager: Read version {} matches or is older than running.", *LASTVER);
return;
}
writeVersionToVersionFile(*DATAROOT);
if (*PNONOTIFY) {
Debug::log(LOG, "CVersionKeeperManager: updated, but update news is disabled in the config :(");
return;
}
if (!executableExistsInPath("hyprland-update-screen")) {
Debug::log(ERR, "CVersionKeeperManager: hyprland-update-screen doesn't seem to exist, skipping notif about update...");
return;
}
g_pEventLoopManager->doLater([]() {
CProcess proc("hyprland-update-screen", {"--new-version", HYPRLAND_VERSION});
proc.runAsync();
});
}
std::optional<std::string> CVersionKeeperManager::getDataHome() {
const auto DATA_HOME = getenv("XDG_DATA_HOME");
std::string dataRoot;
if (!DATA_HOME) {
const auto HOME = getenv("HOME");
if (!HOME) {
Debug::log(ERR, "CVersionKeeperManager: can't get data home: no $HOME or $XDG_DATA_HOME");
return std::nullopt;
}
dataRoot = HOME + std::string{"/.local/share/"};
} else
dataRoot = DATA_HOME + std::string{"/"};
std::error_code ec;
if (!std::filesystem::exists(dataRoot, ec) || ec) {
Debug::log(ERR, "CVersionKeeperManager: can't get data home: inaccessible / missing");
return std::nullopt;
}
dataRoot += "hyprland/";
if (!std::filesystem::exists(dataRoot, ec) || ec) {
Debug::log(LOG, "CVersionKeeperManager: no hyprland data home, creating.");
std::filesystem::create_directory(dataRoot, ec);
if (ec) {
Debug::log(ERR, "CVersionKeeperManager: can't create new data home for hyprland");
return std::nullopt;
}
std::filesystem::permissions(dataRoot, std::filesystem::perms::owner_read | std::filesystem::perms::owner_write | std::filesystem::perms::owner_exec, ec);
if (ec)
Debug::log(WARN, "CVersionKeeperManager: couldn't set perms on hyprland data store. Proceeding anyways.");
}
if (!std::filesystem::exists(dataRoot, ec) || ec) {
Debug::log(ERR, "CVersionKeeperManager: no hyprland data home, failed to create.");
return std::nullopt;
}
return dataRoot;
}
std::optional<std::string> CVersionKeeperManager::getDataLastVersion(const std::string& dataRoot) {
std::error_code ec;
std::string lastVerFile = dataRoot + "/" + VERSION_FILE_NAME;
if (!std::filesystem::exists(lastVerFile, ec) || ec) {
Debug::log(LOG, "CVersionKeeperManager: no hyprland last version file, creating.");
writeVersionToVersionFile(dataRoot);
return HYPRLAND_VERSION;
}
std::ifstream file(lastVerFile);
if (!file.good()) {
Debug::log(ERR, "CVersionKeeperManager: couldn't open an ifstream for reading the version file.");
return std::nullopt;
}
return trim(std::string((std::istreambuf_iterator<char>(file)), (std::istreambuf_iterator<char>())));
}
void CVersionKeeperManager::writeVersionToVersionFile(const std::string& dataRoot) {
std::string lastVerFile = dataRoot + "/" + VERSION_FILE_NAME;
std::ofstream of(lastVerFile, std::ios::trunc);
if (!of.good()) {
Debug::log(ERR, "CVersionKeeperManager: couldn't open an ofstream for writing the version file.");
return;
}
of << HYPRLAND_VERSION;
of.close();
}
bool CVersionKeeperManager::isVersionOlderThanRunning(const std::string& ver) {
const CVarList verStrings(ver, 0, '.', true);
const int V1 = configStringToInt(verStrings[0]).value_or(0);
const int V2 = configStringToInt(verStrings[1]).value_or(0);
const int V3 = configStringToInt(verStrings[2]).value_or(0);
static const CVarList runningStrings(HYPRLAND_VERSION, 0, '.', true);
static const int R1 = configStringToInt(runningStrings[0]).value_or(0);
static const int R2 = configStringToInt(runningStrings[1]).value_or(0);
static const int R3 = configStringToInt(runningStrings[2]).value_or(0);
if (R1 > V1)
return true;
if (R2 > V2)
return true;
if (R3 > V3)
return true;
return false;
}

View file

@ -0,0 +1,17 @@
#pragma once
#include <memory>
#include <optional>
class CVersionKeeperManager {
public:
CVersionKeeperManager();
private:
std::optional<std::string> getDataHome();
std::optional<std::string> getDataLastVersion(const std::string& dataRoot);
void writeVersionToVersionFile(const std::string& dataRoot);
bool isVersionOlderThanRunning(const std::string& ver);
};
inline std::unique_ptr<CVersionKeeperManager> g_pVersionKeeperMgr;