aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--flake.lock45
-rw-r--r--flake.nix9
-rw-r--r--nix/default.nix2
-rw-r--r--nix/overlays.nix1
-rw-r--r--src/Compositor.cpp14
-rw-r--r--src/config/ConfigDescriptions.hpp6
-rw-r--r--src/config/ConfigManager.cpp3
-rw-r--r--src/helpers/MiscFunctions.cpp32
-rw-r--r--src/helpers/MiscFunctions.hpp1
-rw-r--r--src/managers/VersionKeeperManager.cpp153
-rw-r--r--src/managers/VersionKeeperManager.hpp17
11 files changed, 270 insertions, 13 deletions
diff --git a/flake.lock b/flake.lock
index 23bd37a9..29f517b7 100644
--- a/flake.lock
+++ b/flake.lock
@@ -141,6 +141,32 @@
"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": {
"inputs": {
"hyprutils": [
@@ -215,11 +241,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1732758367,
- "narHash": "sha256-RzaI1RO0UXqLjydtz3GAXSTzHkpb/lLD1JD8a0W4Wpo=",
+ "lastModified": 1733392399,
+ "narHash": "sha256-kEsTJTUQfQFIJOcLYFt/RvNxIK653ZkTBIs4DG+cBns=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "fa42b5a5f401aab8a32bd33c9a4de0738180dc59",
+ "rev": "d0797a04b81caeae77bcff10a9dde78bc17f5661",
"type": "github"
},
"original": {
@@ -255,11 +281,11 @@
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
- "lastModified": 1732021966,
- "narHash": "sha256-mnTbjpdqF0luOkou8ZFi2asa1N3AA2CchR/RqCNmsGE=",
+ "lastModified": 1733318908,
+ "narHash": "sha256-SVQVsbafSM1dJ4fpgyBqLZ+Lft+jcQuMtEL3lQWx2Sk=",
"owner": "cachix",
"repo": "git-hooks.nix",
- "rev": "3308484d1a443fc5bc92012435d79e80458fe43c",
+ "rev": "6f4e2a2112050951a314d2733a994fbab94864c6",
"type": "github"
},
"original": {
@@ -274,6 +300,7 @@
"hyprcursor": "hyprcursor",
"hyprgraphics": "hyprgraphics",
"hyprland-protocols": "hyprland-protocols",
+ "hyprland-qtutils": "hyprland-qtutils",
"hyprlang": "hyprlang",
"hyprutils": "hyprutils",
"hyprwayland-scanner": "hyprwayland-scanner",
@@ -320,11 +347,11 @@
]
},
"locked": {
- "lastModified": 1731703417,
- "narHash": "sha256-rheDc/7C+yI+QspYr9J2z9kQ5P9F4ATapI7qyFAe1XA=",
+ "lastModified": 1733157064,
+ "narHash": "sha256-NetqJHAN4bbZDQADvpep+wXk2AbMZ2bN6tINz8Kpz6M=",
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
- "rev": "8070f36deec723de71e7557441acb17e478204d3",
+ "rev": "fd85ef39369f95eed67fdf3f025e86916edeea2f",
"type": "github"
},
"original": {
diff --git a/flake.nix b/flake.nix
index f26f1c31..821a5f90 100644
--- a/flake.nix
+++ b/flake.nix
@@ -35,6 +35,13 @@
inputs.systems.follows = "systems";
};
+ hyprland-qtutils = {
+ url = "github:hyprwm/hyprland-qtutils";
+ inputs.nixpkgs.follows = "nixpkgs";
+ inputs.systems.follows = "systems";
+ inputs.hyprutils.follows = "hyprutils";
+ };
+
hyprlang = {
url = "github:hyprwm/hyprlang";
inputs.nixpkgs.follows = "nixpkgs";
@@ -123,13 +130,11 @@
inherit
(pkgsFor.${system})
# hyprland-packages
-
hyprland
hyprland-debug
hyprland-legacy-renderer
hyprland-unwrapped
# hyprland-extras
-
xdg-desktop-portal-hyprland
;
hyprland-cross = (pkgsCrossFor.${system} "aarch64-linux").hyprland;
diff --git a/nix/default.nix b/nix/default.nix
index 3a84ccc8..aeb4a4bb 100644
--- a/nix/default.nix
+++ b/nix/default.nix
@@ -14,6 +14,7 @@
hyprcursor,
hyprgraphics,
hyprland-protocols,
+ hyprland-qtutils,
hyprlang,
hyprutils,
hyprwayland-scanner,
@@ -168,6 +169,7 @@ in
wrapProgram $out/bin/Hyprland \
--suffix PATH : ${makeBinPath [
binutils
+ hyprland-qtutils
pciutils
pkgconf
]}
diff --git a/nix/overlays.nix b/nix/overlays.nix
index c2103f31..b632d0b4 100644
--- a/nix/overlays.nix
+++ b/nix/overlays.nix
@@ -24,6 +24,7 @@ in {
inputs.hyprcursor.overlays.default
inputs.hyprgraphics.overlays.default
inputs.hyprland-protocols.overlays.default
+ inputs.hyprland-qtutils.overlays.default
inputs.hyprlang.overlays.default
inputs.hyprutils.overlays.default
inputs.hyprwayland-scanner.overlays.default
diff --git a/src/Compositor.cpp b/src/Compositor.cpp
index 1261d003..74a2c8d1 100644
--- a/src/Compositor.cpp
+++ b/src/Compositor.cpp
@@ -6,6 +6,7 @@
#include "managers/TokenManager.hpp"
#include "managers/PointerManager.hpp"
#include "managers/SeatManager.hpp"
+#include "managers/VersionKeeperManager.hpp"
#include "managers/eventLoop/EventLoopManager.hpp"
#include <aquamarine/output/Output.hpp>
#include <bit>
@@ -645,6 +646,9 @@ void CCompositor::initManagers(eManagersInitStage stage) {
Debug::log(LOG, "Creating the CursorManager!");
g_pCursorManager = std::make_unique<CCursorManager>();
+ Debug::log(LOG, "Creating the VersionKeeper!");
+ g_pVersionKeeperMgr = std::make_unique<CVersionKeeperManager>();
+
Debug::log(LOG, "Starting XWayland");
g_pXWayland = std::make_unique<CXWayland>(g_pCompositor->m_bEnableXwayland);
} break;
@@ -2610,7 +2614,8 @@ WORKSPACEID CCompositor::getNewSpecialID() {
}
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) {
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) {
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" : ""),
diff --git a/src/config/ConfigDescriptions.hpp b/src/config/ConfigDescriptions.hpp
index b95eeab1..a1d4858c 100644
--- a/src/config/ConfigDescriptions.hpp
+++ b/src/config/ConfigDescriptions.hpp
@@ -1134,6 +1134,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.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{
.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.",
.type = CONFIG_OPTION_INT,
diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp
index 820844eb..50fbe403 100644
--- a/src/config/ConfigManager.cpp
+++ b/src/config/ConfigManager.cpp
@@ -383,6 +383,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("misc:middle_click_paste", Hyprlang::INT{1});
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_hyprland_qtutils_check", Hyprlang::INT{0});
m_pConfig->addConfigValue("misc:lockdead_screen_delay", Hyprlang::INT{1000});
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:expand_undersized_textures", Hyprlang::INT{1});
+ m_pConfig->addConfigValue("ecosystem:no_update_news", Hyprlang::INT{0});
+
// devices
m_pConfig->addSpecialCategory("device", {"name"});
m_pConfig->addSpecialConfigValue("device", "sensitivity", {0.F});
diff --git a/src/helpers/MiscFunctions.cpp b/src/helpers/MiscFunctions.cpp
index dc181d4f..2da90eb7 100644
--- a/src/helpers/MiscFunctions.cpp
+++ b/src/helpers/MiscFunctions.cpp
@@ -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;
else
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;
+}
diff --git a/src/helpers/MiscFunctions.hpp b/src/helpers/MiscFunctions.hpp
index bb686df4..018efbed 100644
--- a/src/helpers/MiscFunctions.hpp
+++ b/src/helpers/MiscFunctions.hpp
@@ -42,6 +42,7 @@ bool envEnabled(const std::string& env);
int allocateSHMFile(size_t len);
bool allocateSHMFilePair(size_t size, int* rw_fd_ptr, int* ro_fd_ptr);
float stringToPercentage(const std::string& VALUE, const float REL);
+bool executableExistsInPath(const std::string& exe);
template <typename... Args>
[[deprecated("use std::format instead")]] std::string getFormat(std::format_string<Args...> fmt, Args&&... args) {
diff --git a/src/managers/VersionKeeperManager.cpp b/src/managers/VersionKeeperManager.cpp
new file mode 100644
index 00000000..89f7823d
--- /dev/null
+++ b/src/managers/VersionKeeperManager.cpp
@@ -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;
+}
diff --git a/src/managers/VersionKeeperManager.hpp b/src/managers/VersionKeeperManager.hpp
new file mode 100644
index 00000000..f0dc05ce
--- /dev/null
+++ b/src/managers/VersionKeeperManager.hpp
@@ -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; \ No newline at end of file