mirror of
https://github.com/hyprwm/Hyprland
synced 2025-01-12 04:49:48 +01:00
core: Add a periodic donation request (#8981)
Will fire once in december and july. Disableable with `ecosystem:no_donation:nag`
This commit is contained in:
parent
da9252a23e
commit
b5fb6110ab
10 changed files with 278 additions and 115 deletions
|
@ -9,6 +9,7 @@
|
||||||
#include "managers/PointerManager.hpp"
|
#include "managers/PointerManager.hpp"
|
||||||
#include "managers/SeatManager.hpp"
|
#include "managers/SeatManager.hpp"
|
||||||
#include "managers/VersionKeeperManager.hpp"
|
#include "managers/VersionKeeperManager.hpp"
|
||||||
|
#include "managers/DonationNagManager.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>
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
#endif
|
#endif
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include "helpers/varlist/VarList.hpp"
|
#include "helpers/varlist/VarList.hpp"
|
||||||
|
#include "helpers/fs/FsUtils.hpp"
|
||||||
#include "protocols/FractionalScale.hpp"
|
#include "protocols/FractionalScale.hpp"
|
||||||
#include "protocols/PointerConstraints.hpp"
|
#include "protocols/PointerConstraints.hpp"
|
||||||
#include "protocols/LayerShell.hpp"
|
#include "protocols/LayerShell.hpp"
|
||||||
|
@ -544,6 +546,8 @@ void CCompositor::cleanup() {
|
||||||
g_pSeatManager.reset();
|
g_pSeatManager.reset();
|
||||||
g_pHyprCtl.reset();
|
g_pHyprCtl.reset();
|
||||||
g_pEventLoopManager.reset();
|
g_pEventLoopManager.reset();
|
||||||
|
g_pVersionKeeperMgr.reset();
|
||||||
|
g_pDonationNagManager.reset();
|
||||||
|
|
||||||
if (m_pAqBackend)
|
if (m_pAqBackend)
|
||||||
m_pAqBackend.reset();
|
m_pAqBackend.reset();
|
||||||
|
@ -645,6 +649,9 @@ void CCompositor::initManagers(eManagersInitStage stage) {
|
||||||
Debug::log(LOG, "Creating the VersionKeeper!");
|
Debug::log(LOG, "Creating the VersionKeeper!");
|
||||||
g_pVersionKeeperMgr = std::make_unique<CVersionKeeperManager>();
|
g_pVersionKeeperMgr = std::make_unique<CVersionKeeperManager>();
|
||||||
|
|
||||||
|
Debug::log(LOG, "Creating the DonationNag!");
|
||||||
|
g_pDonationNagManager = std::make_unique<CDonationNagManager>();
|
||||||
|
|
||||||
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;
|
||||||
|
@ -2645,7 +2652,7 @@ void CCompositor::performUserChecks() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!*PNOCHECKQTUTILS) {
|
if (!*PNOCHECKQTUTILS) {
|
||||||
if (!executableExistsInPath("hyprland-dialog")) {
|
if (!NFsUtils::executableExistsInPath("hyprland-dialog")) {
|
||||||
g_pHyprNotificationOverlay->addNotification(
|
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);
|
"Your system does not have hyprland-qtutils installed. This is a runtime dependency for some dialogs. Consider installing it.", CHyprColor{}, 15000, ICON_WARNING);
|
||||||
}
|
}
|
||||||
|
|
|
@ -621,6 +621,7 @@ CConfigManager::CConfigManager() {
|
||||||
m_pConfig->addConfigValue("render:ctm_animation", Hyprlang::INT{2});
|
m_pConfig->addConfigValue("render:ctm_animation", Hyprlang::INT{2});
|
||||||
|
|
||||||
m_pConfig->addConfigValue("ecosystem:no_update_news", Hyprlang::INT{0});
|
m_pConfig->addConfigValue("ecosystem:no_update_news", Hyprlang::INT{0});
|
||||||
|
m_pConfig->addConfigValue("ecosystem:no_donation_nag", Hyprlang::INT{0});
|
||||||
|
|
||||||
m_pConfig->addConfigValue("experimental:wide_color_gamut", Hyprlang::INT{0});
|
m_pConfig->addConfigValue("experimental:wide_color_gamut", Hyprlang::INT{0});
|
||||||
m_pConfig->addConfigValue("experimental:hdr", Hyprlang::INT{0});
|
m_pConfig->addConfigValue("experimental:hdr", Hyprlang::INT{0});
|
||||||
|
|
|
@ -911,30 +911,3 @@ float stringToPercentage(const std::string& VALUE, const float REL) {
|
||||||
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();
|
|
||||||
|
|
||||||
return std::filesystem::perms::none != (perms & std::filesystem::perms::others_exec);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
|
@ -40,7 +40,6 @@ 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) {
|
||||||
|
|
107
src/helpers/fs/FsUtils.cpp
Normal file
107
src/helpers/fs/FsUtils.cpp
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
#include "FsUtils.hpp"
|
||||||
|
#include "../../debug/Log.hpp"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include <hyprutils/string/String.hpp>
|
||||||
|
#include <hyprutils/string/VarList.hpp>
|
||||||
|
using namespace Hyprutils::String;
|
||||||
|
|
||||||
|
std::optional<std::string> NFsUtils::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, "FsUtils::getDataHome: 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, "FsUtils::getDataHome: can't get data home: inaccessible / missing");
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
dataRoot += "hyprland/";
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(dataRoot, ec) || ec) {
|
||||||
|
Debug::log(LOG, "FsUtils::getDataHome: no hyprland data home, creating.");
|
||||||
|
std::filesystem::create_directory(dataRoot, ec);
|
||||||
|
if (ec) {
|
||||||
|
Debug::log(ERR, "FsUtils::getDataHome: 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, "FsUtils::getDataHome: couldn't set perms on hyprland data store. Proceeding anyways.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(dataRoot, ec) || ec) {
|
||||||
|
Debug::log(ERR, "FsUtils::getDataHome: no hyprland data home, failed to create.");
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> NFsUtils::readFileAsString(const std::string& path) {
|
||||||
|
std::error_code ec;
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(path, ec) || ec)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
std::ifstream file(path);
|
||||||
|
if (!file.good())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return trim(std::string((std::istreambuf_iterator<char>(file)), (std::istreambuf_iterator<char>())));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NFsUtils::writeToFile(const std::string& path, const std::string& content) {
|
||||||
|
std::ofstream of(path, std::ios::trunc);
|
||||||
|
if (!of.good()) {
|
||||||
|
Debug::log(ERR, "CVersionKeeperManager: couldn't open an ofstream for writing the version file.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
of << content;
|
||||||
|
of.close();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NFsUtils::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();
|
||||||
|
|
||||||
|
return std::filesystem::perms::none != (perms & std::filesystem::perms::others_exec);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
15
src/helpers/fs/FsUtils.hpp
Normal file
15
src/helpers/fs/FsUtils.hpp
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace NFsUtils {
|
||||||
|
// Returns the path to the hyprland directory in data home.
|
||||||
|
std::optional<std::string> getDataHome();
|
||||||
|
|
||||||
|
std::optional<std::string> readFileAsString(const std::string& path);
|
||||||
|
|
||||||
|
// overwrites the file if exists
|
||||||
|
bool writeToFile(const std::string& path, const std::string& content);
|
||||||
|
|
||||||
|
bool executableExistsInPath(const std::string& exe);
|
||||||
|
};
|
114
src/managers/DonationNagManager.cpp
Normal file
114
src/managers/DonationNagManager.cpp
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
#include "DonationNagManager.hpp"
|
||||||
|
#include "../debug/Log.hpp"
|
||||||
|
#include "VersionKeeperManager.hpp"
|
||||||
|
#include "eventLoop/EventLoopManager.hpp"
|
||||||
|
#include "../config/ConfigValue.hpp"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <format>
|
||||||
|
|
||||||
|
#include "../helpers/fs/FsUtils.hpp"
|
||||||
|
|
||||||
|
#include <hyprutils/os/Process.hpp>
|
||||||
|
using namespace Hyprutils::OS;
|
||||||
|
|
||||||
|
constexpr const char* LAST_NAG_FILE_NAME = "lastNag";
|
||||||
|
constexpr uint64_t DAY_IN_SECONDS = 3600ULL * 24;
|
||||||
|
constexpr uint64_t MONTH_IN_SECONDS = DAY_IN_SECONDS * 30;
|
||||||
|
|
||||||
|
struct SNagDatePoint {
|
||||||
|
// Counted from 1, as in Jan 1st is 1, 1
|
||||||
|
// No month-boundaries because I am lazy
|
||||||
|
uint8_t month = 0, dayStart = 0, dayEnd = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
const std::vector<SNagDatePoint> NAG_DATE_POINTS = {
|
||||||
|
SNagDatePoint {
|
||||||
|
7, 20, 31,
|
||||||
|
},
|
||||||
|
SNagDatePoint {
|
||||||
|
12, 1, 28
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
CDonationNagManager::CDonationNagManager() {
|
||||||
|
static auto PNONAG = CConfigValue<Hyprlang::INT>("ecosystem:no_donation_nag");
|
||||||
|
|
||||||
|
if (g_pVersionKeeperMgr->fired() || *PNONAG)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto DATAROOT = NFsUtils::getDataHome();
|
||||||
|
|
||||||
|
if (!DATAROOT)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto EPOCH = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
|
|
||||||
|
const auto LASTNAGSTR = NFsUtils::readFileAsString(*DATAROOT + "/" + LAST_NAG_FILE_NAME);
|
||||||
|
|
||||||
|
if (!LASTNAGSTR) {
|
||||||
|
const auto EPOCHSTR = std::format("{}", EPOCH);
|
||||||
|
NFsUtils::writeToFile(*DATAROOT + "/" + LAST_NAG_FILE_NAME, EPOCHSTR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t LAST_EPOCH = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
LAST_EPOCH = std::stoull(*LASTNAGSTR);
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
Debug::log(ERR, "DonationNag: Last epoch invalid? Failed to parse \"{}\". Setting to today.", *LASTNAGSTR);
|
||||||
|
const auto EPOCHSTR = std::format("{}", EPOCH);
|
||||||
|
NFsUtils::writeToFile(*DATAROOT + "/" + LAST_NAG_FILE_NAME, EPOCHSTR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't nag if the last nag was less than a month ago. This is
|
||||||
|
// mostly for first-time nags, as other nags happen in specific time frames shorter than a month
|
||||||
|
if (EPOCH - LAST_EPOCH < MONTH_IN_SECONDS) {
|
||||||
|
Debug::log(LOG, "DonationNag: last nag was {} days ago, too early for a nag.", (int)std::round((EPOCH - LAST_EPOCH) / (double)MONTH_IN_SECONDS));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NFsUtils::executableExistsInPath("hyprland-donate-screen")) {
|
||||||
|
Debug::log(ERR, "DonationNag: executable doesn't exist, skipping.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||||
|
auto local = *localtime(&tt);
|
||||||
|
|
||||||
|
const auto MONTH = local.tm_mon + 1;
|
||||||
|
const auto DAY = local.tm_mday;
|
||||||
|
|
||||||
|
for (const auto& nagPoint : NAG_DATE_POINTS) {
|
||||||
|
if (MONTH != nagPoint.month)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (DAY < nagPoint.dayStart || DAY > nagPoint.dayEnd)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Debug::log(LOG, "DonationNag: hit nag month {} days {}-{}, it's {} today, nagging", MONTH, nagPoint.dayStart, nagPoint.dayEnd, DAY);
|
||||||
|
|
||||||
|
m_bFired = true;
|
||||||
|
|
||||||
|
const auto EPOCHSTR = std::format("{}", EPOCH);
|
||||||
|
NFsUtils::writeToFile(*DATAROOT + "/" + LAST_NAG_FILE_NAME, EPOCHSTR);
|
||||||
|
|
||||||
|
g_pEventLoopManager->doLater([] {
|
||||||
|
CProcess proc("hyprland-donate-screen", {});
|
||||||
|
proc.runAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_bFired)
|
||||||
|
Debug::log(LOG, "DonationNag: didn't hit any nagging periods");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDonationNagManager::fired() {
|
||||||
|
return m_bFired;
|
||||||
|
}
|
16
src/managers/DonationNagManager.hpp
Normal file
16
src/managers/DonationNagManager.hpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class CDonationNagManager {
|
||||||
|
public:
|
||||||
|
CDonationNagManager();
|
||||||
|
|
||||||
|
// whether the donation nag was shown this boot.
|
||||||
|
bool fired();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_bFired = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::unique_ptr<CDonationNagManager> g_pDonationNagManager;
|
|
@ -6,6 +6,7 @@
|
||||||
#include "../helpers/varlist/VarList.hpp"
|
#include "../helpers/varlist/VarList.hpp"
|
||||||
#include "eventLoop/EventLoopManager.hpp"
|
#include "eventLoop/EventLoopManager.hpp"
|
||||||
#include "../config/ConfigValue.hpp"
|
#include "../config/ConfigValue.hpp"
|
||||||
|
#include "../helpers/fs/FsUtils.hpp"
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -20,12 +21,12 @@ constexpr const char* VERSION_FILE_NAME = "lastVersion";
|
||||||
CVersionKeeperManager::CVersionKeeperManager() {
|
CVersionKeeperManager::CVersionKeeperManager() {
|
||||||
static auto PNONOTIFY = CConfigValue<Hyprlang::INT>("ecosystem:no_update_news");
|
static auto PNONOTIFY = CConfigValue<Hyprlang::INT>("ecosystem:no_update_news");
|
||||||
|
|
||||||
const auto DATAROOT = getDataHome();
|
const auto DATAROOT = NFsUtils::getDataHome();
|
||||||
|
|
||||||
if (!DATAROOT)
|
if (!DATAROOT)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto LASTVER = getDataLastVersion(*DATAROOT);
|
const auto LASTVER = NFsUtils::readFileAsString(*DATAROOT + "/" + VERSION_FILE_NAME);
|
||||||
|
|
||||||
if (!LASTVER)
|
if (!LASTVER)
|
||||||
return;
|
return;
|
||||||
|
@ -35,101 +36,26 @@ CVersionKeeperManager::CVersionKeeperManager() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeVersionToVersionFile(*DATAROOT);
|
NFsUtils::writeToFile(*DATAROOT + "/" + VERSION_FILE_NAME, HYPRLAND_VERSION);
|
||||||
|
|
||||||
if (*PNONOTIFY) {
|
if (*PNONOTIFY) {
|
||||||
Debug::log(LOG, "CVersionKeeperManager: updated, but update news is disabled in the config :(");
|
Debug::log(LOG, "CVersionKeeperManager: updated, but update news is disabled in the config :(");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!executableExistsInPath("hyprland-update-screen")) {
|
if (!NFsUtils::executableExistsInPath("hyprland-update-screen")) {
|
||||||
Debug::log(ERR, "CVersionKeeperManager: hyprland-update-screen doesn't seem to exist, skipping notif about update...");
|
Debug::log(ERR, "CVersionKeeperManager: hyprland-update-screen doesn't seem to exist, skipping notif about update...");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_bFired = true;
|
||||||
|
|
||||||
g_pEventLoopManager->doLater([]() {
|
g_pEventLoopManager->doLater([]() {
|
||||||
CProcess proc("hyprland-update-screen", {"--new-version", HYPRLAND_VERSION});
|
CProcess proc("hyprland-update-screen", {"--new-version", HYPRLAND_VERSION});
|
||||||
proc.runAsync();
|
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 "0.0.0";
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
bool CVersionKeeperManager::isVersionOlderThanRunning(const std::string& ver) {
|
||||||
const CVarList verStrings(ver, 0, '.', true);
|
const CVarList verStrings(ver, 0, '.', true);
|
||||||
|
|
||||||
|
@ -151,3 +77,7 @@ bool CVersionKeeperManager::isVersionOlderThanRunning(const std::string& ver) {
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CVersionKeeperManager::fired() {
|
||||||
|
return m_bFired;
|
||||||
|
}
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
class CVersionKeeperManager {
|
class CVersionKeeperManager {
|
||||||
public:
|
public:
|
||||||
CVersionKeeperManager();
|
CVersionKeeperManager();
|
||||||
|
|
||||||
|
// whether the update screen was shown this boot.
|
||||||
|
bool fired();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::optional<std::string> getDataHome();
|
bool isVersionOlderThanRunning(const std::string& ver);
|
||||||
std::optional<std::string> getDataLastVersion(const std::string& dataRoot);
|
|
||||||
void writeVersionToVersionFile(const std::string& dataRoot);
|
bool m_bFired = false;
|
||||||
bool isVersionOlderThanRunning(const std::string& ver);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::unique_ptr<CVersionKeeperManager> g_pVersionKeeperMgr;
|
inline std::unique_ptr<CVersionKeeperManager> g_pVersionKeeperMgr;
|
Loading…
Reference in a new issue