-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
267 additions
and
114 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
#include "DonationNagManager.hpp" | ||
#include "../debug/Log.hpp" | ||
#include "VersionKeeperManager.hpp" | ||
#include "eventLoop/EventLoopManager.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() { | ||
if (g_pVersionKeeperMgr->fired()) | ||
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-donation-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-donation-screen", {}); | ||
proc.runAsync(); | ||
}); | ||
|
||
break; | ||
} | ||
|
||
if (!m_bFired) | ||
Debug::log(LOG, "DonationNag: didn't hit any nagging periods"); | ||
} | ||
|
||
bool CDonationNagManager::fired() { | ||
return m_bFired; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; |
Oops, something went wrong.