summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVaxry <vaxry@vaxry.net>2025-04-27 00:03:32 +0100
committerVaxry <vaxry@vaxry.net>2025-04-27 13:28:06 +0100
commit0302bfdc2207f9b5482fb07aa6052e7f6cb237ca (patch)
treec2039f2d4f01327b23b39493bc03b90d195fa6d3 /src
parent4f868a1f3c1edfc1f5998f7d21e46e7c23aeb02f (diff)
async: add Promise and use it for AsyncDialogBox
Diffstat (limited to 'src')
-rw-r--r--src/config/ConfigManager.cpp2
-rw-r--r--src/helpers/AsyncDialogBox.cpp17
-rw-r--r--src/helpers/AsyncDialogBox.hpp34
-rw-r--r--src/helpers/defer/Promise.hpp119
-rw-r--r--src/managers/ANRManager.cpp9
-rw-r--r--src/managers/permissions/DynamicPermissionManager.cpp36
-rw-r--r--src/managers/permissions/DynamicPermissionManager.hpp27
7 files changed, 208 insertions, 36 deletions
diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp
index 872456a6..9d177798 100644
--- a/src/config/ConfigManager.cpp
+++ b/src/config/ConfigManager.cpp
@@ -2906,6 +2906,8 @@ std::optional<std::string> CConfigManager::handlePermission(const std::string& c
if (data[1] == "screencopy")
type = PERMISSION_TYPE_SCREENCOPY;
+ else if (data[1] == "plugin")
+ type = PERMISSION_TYPE_PLUGIN;
if (data[2] == "ask")
mode = PERMISSION_RULE_ALLOW_MODE_ASK;
diff --git a/src/helpers/AsyncDialogBox.cpp b/src/helpers/AsyncDialogBox.cpp
index 73bae8f6..86eb9e1f 100644
--- a/src/helpers/AsyncDialogBox.cpp
+++ b/src/helpers/AsyncDialogBox.cpp
@@ -59,8 +59,7 @@ void CAsyncDialogBox::onWrite(int fd, uint32_t mask) {
if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
Debug::log(LOG, "CAsyncDialogBox: dialog {:x} hung up, closed.");
- if (m_onResolution)
- m_onResolution(m_stdout);
+ m_promiseResolver->resolve(m_stdout);
wl_event_source_remove(m_readEventSource);
m_selfReference.reset();
@@ -68,9 +67,7 @@ void CAsyncDialogBox::onWrite(int fd, uint32_t mask) {
}
}
-void CAsyncDialogBox::open(std::function<void(std::string)> onResolution) {
- m_onResolution = onResolution;
-
+SP<CPromise<std::string>> CAsyncDialogBox::open() {
std::string buttonsString = "";
for (auto& b : m_buttons) {
buttonsString += b + ";";
@@ -83,7 +80,7 @@ void CAsyncDialogBox::open(std::function<void(std::string)> onResolution) {
int outPipe[2];
if (pipe(outPipe)) {
Debug::log(ERR, "CAsyncDialogBox::open: failed to pipe()");
- return;
+ return nullptr;
}
m_pipeReadFd = CFileDescriptor(outPipe[0]);
@@ -94,7 +91,7 @@ void CAsyncDialogBox::open(std::function<void(std::string)> onResolution) {
if (!m_readEventSource) {
Debug::log(ERR, "CAsyncDialogBox::open: failed to add read fd to loop");
- return;
+ return nullptr;
}
m_selfReference = m_selfWeakReference.lock();
@@ -102,13 +99,17 @@ void CAsyncDialogBox::open(std::function<void(std::string)> onResolution) {
if (!proc.runAsync()) {
Debug::log(ERR, "CAsyncDialogBox::open: failed to run async");
wl_event_source_remove(m_readEventSource);
- return;
+ return nullptr;
}
m_dialogPid = proc.pid();
// close the write fd, only the dialog owns it now
close(outPipe[1]);
+
+ auto promise = CPromise<std::string>::make([this](SP<CPromiseResolver<std::string>> r) { m_promiseResolver = r; });
+
+ return promise;
}
void CAsyncDialogBox::kill() {
diff --git a/src/helpers/AsyncDialogBox.hpp b/src/helpers/AsyncDialogBox.hpp
index d3cc242c..0cb95846 100644
--- a/src/helpers/AsyncDialogBox.hpp
+++ b/src/helpers/AsyncDialogBox.hpp
@@ -2,6 +2,7 @@
#include "../macros.hpp"
#include "./memory/Memory.hpp"
+#include "./defer/Promise.hpp"
#include <vector>
#include <functional>
@@ -15,29 +16,30 @@ class CAsyncDialogBox {
public:
static SP<CAsyncDialogBox> create(const std::string& title, const std::string& description, std::vector<std::string> buttons);
- CAsyncDialogBox(const CAsyncDialogBox&) = delete;
- CAsyncDialogBox(CAsyncDialogBox&&) = delete;
- CAsyncDialogBox& operator=(const CAsyncDialogBox&) = delete;
- CAsyncDialogBox& operator=(CAsyncDialogBox&&) = delete;
+ CAsyncDialogBox(const CAsyncDialogBox&) = delete;
+ CAsyncDialogBox(CAsyncDialogBox&&) = delete;
+ CAsyncDialogBox& operator=(const CAsyncDialogBox&) = delete;
+ CAsyncDialogBox& operator=(CAsyncDialogBox&&) = delete;
- void open(std::function<void(std::string)> onResolution);
- void kill();
- bool isRunning() const;
+ SP<CPromise<std::string>> open();
+ void kill();
+ bool isRunning() const;
- void onWrite(int fd, uint32_t mask);
+ void onWrite(int fd, uint32_t mask);
private:
CAsyncDialogBox(const std::string& title, const std::string& description, std::vector<std::string> buttons);
- pid_t m_dialogPid = 0;
- wl_event_source* m_readEventSource = nullptr;
- std::function<void(std::string)> m_onResolution;
- Hyprutils::OS::CFileDescriptor m_pipeReadFd;
- std::string m_stdout = "";
+ pid_t m_dialogPid = 0;
+ wl_event_source* m_readEventSource = nullptr;
+ Hyprutils::OS::CFileDescriptor m_pipeReadFd;
+ std::string m_stdout = "";
- const std::string m_title;
- const std::string m_description;
- const std::vector<std::string> m_buttons;
+ const std::string m_title;
+ const std::string m_description;
+ const std::vector<std::string> m_buttons;
+
+ SP<CPromiseResolver<std::string>> m_promiseResolver;
// WARNING: cyclic reference. This will be removed once the event source is removed to avoid dangling pointers
SP<CAsyncDialogBox> m_selfReference;
diff --git a/src/helpers/defer/Promise.hpp b/src/helpers/defer/Promise.hpp
new file mode 100644
index 00000000..53399bc2
--- /dev/null
+++ b/src/helpers/defer/Promise.hpp
@@ -0,0 +1,119 @@
+#pragma once
+
+#include <functional>
+#include <string>
+#include "../memory/Memory.hpp"
+
+// TODO: move into hyprutils
+
+template <typename T>
+class CPromise;
+template <typename T>
+class CPromiseResult;
+
+template <typename T>
+class CPromiseResolver {
+ public:
+ CPromiseResolver(const CPromiseResolver&) = delete;
+ CPromiseResolver(CPromiseResolver&&) = delete;
+ CPromiseResolver& operator=(const CPromiseResolver&) = delete;
+ CPromiseResolver& operator=(CPromiseResolver&&) = delete;
+
+ void resolve(T value) {
+ if (m_promise->m_result)
+ return;
+
+ m_promise->m_result = CPromiseResult<T>::result(value);
+
+ if (!m_promise->m_then)
+ return;
+
+ m_promise->m_then(m_promise->m_result);
+ }
+
+ void reject(const std::string& reason) {
+ if (m_promise->m_result)
+ return;
+
+ m_promise->m_result = CPromiseResult<T>::err(reason);
+
+ if (!m_promise->m_then)
+ return;
+
+ m_promise->m_then(m_promise->m_result);
+ }
+
+ private:
+ CPromiseResolver(SP<CPromise<T>> promise) : m_promise(promise) {}
+
+ SP<CPromise<T>> m_promise;
+
+ friend class CPromise<T>;
+};
+
+template <typename T>
+class CPromiseResult {
+ public:
+ bool hasError() {
+ return m_hasError;
+ }
+
+ T result() {
+ return m_result;
+ }
+
+ std::string error() {
+ return m_error;
+ }
+
+ private:
+ static SP<CPromiseResult<T>> result(T result) {
+ auto p = SP<CPromiseResult<T>>(new CPromiseResult<T>());
+ p->m_result = result;
+ return p;
+ }
+
+ static SP<CPromiseResult<T>> err(std::string reason) {
+ auto p = SP<CPromiseResult<T>>(new CPromiseResult<T>());
+ p->m_error = reason;
+ p->m_hasError = true;
+ return p;
+ }
+
+ T m_result = {};
+ std::string m_error = {};
+ bool m_hasError = false;
+
+ friend class CPromiseResolver<T>;
+};
+
+template <typename T>
+class CPromise {
+ public:
+ CPromise(const CPromise&) = delete;
+ CPromise(CPromise&&) = delete;
+ CPromise& operator=(const CPromise&) = delete;
+ CPromise& operator=(CPromise&&) = delete;
+
+ static SP<CPromise> make(const std::function<void(SP<CPromiseResolver<T>>)>& fn) {
+ auto sp = SP<CPromise<T>>(new CPromise<T>());
+ fn(SP<CPromiseResolver<T>>(new CPromiseResolver<T>(sp)));
+ return sp;
+ }
+
+ void then(std::function<void(SP<CPromiseResult<T>>)>&& fn) {
+ m_then = std::move(fn);
+ if (m_result)
+ m_then(m_result);
+ }
+
+ private:
+ CPromise() = default;
+
+ const std::function<void(SP<CPromiseResult<T>>)> m_fn;
+ std::function<void(SP<CPromiseResult<T>>)> m_then;
+ SP<CPromiseResult<T>> m_result;
+
+ friend class CPromiseResult<T>;
+ friend class CPromiseResolver<T>;
+}; \ No newline at end of file
diff --git a/src/managers/ANRManager.cpp b/src/managers/ANRManager.cpp
index 69d127a7..48b9a6db 100644
--- a/src/managers/ANRManager.cpp
+++ b/src/managers/ANRManager.cpp
@@ -169,7 +169,14 @@ void CANRManager::SANRData::runDialog(const std::string& title, const std::strin
appClass.empty() ? "unknown" : appClass),
std::vector<std::string>{"Terminate", "Wait"});
- dialogBox->open([dialogWmPID, this](std::string result) {
+ dialogBox->open()->then([dialogWmPID, this](SP<CPromiseResult<std::string>> r) {
+ if (r->hasError()) {
+ Debug::log(ERR, "CANRManager::SANRData::runDialog: error spawning dialog");
+ return;
+ }
+
+ const auto& result = r->result();
+
if (result.starts_with("Terminate"))
::kill(dialogWmPID, SIGKILL);
else if (result.starts_with("Wait"))
diff --git a/src/managers/permissions/DynamicPermissionManager.cpp b/src/managers/permissions/DynamicPermissionManager.cpp
index cf1fd1a0..c23f1fdb 100644
--- a/src/managers/permissions/DynamicPermissionManager.cpp
+++ b/src/managers/permissions/DynamicPermissionManager.cpp
@@ -48,6 +48,7 @@ static const char* permissionToString(eDynamicPermissionType type) {
switch (type) {
case PERMISSION_TYPE_UNKNOWN: return "PERMISSION_TYPE_UNKNOWN";
case PERMISSION_TYPE_SCREENCOPY: return "PERMISSION_TYPE_SCREENCOPY";
+ case PERMISSION_TYPE_PLUGIN: return "PERMISSION_TYPE_PLUGIN";
}
return "error";
@@ -57,6 +58,7 @@ static const char* permissionToHumanString(eDynamicPermissionType type) {
switch (type) {
case PERMISSION_TYPE_UNKNOWN: return "requesting an unknown permission";
case PERMISSION_TYPE_SCREENCOPY: return "trying to capture your screen";
+ case PERMISSION_TYPE_PLUGIN: return "trying to load a plugin";
}
return "error";
@@ -210,10 +212,22 @@ void CDynamicPermissionManager::askForPermission(wl_client* client, const std::s
return;
}
- rule->m_dialogBox->open([r = WP<CDynamicPermissionRule>(rule), binaryPath](std::string result) {
+ rule->m_promise = rule->m_dialogBox->open();
+ rule->m_promise->then([r = WP<CDynamicPermissionRule>(rule), binaryPath](SP<CPromiseResult<std::string>> pr) {
if (!r)
return;
+ if (pr->hasError()) {
+ // not reachable for now
+ Debug::log(TRACE, "CDynamicPermissionRule: error spawning dialog box");
+ if (r->m_promiseResolverForExternal)
+ r->m_promiseResolverForExternal->reject("error spawning dialog box");
+ r->m_promiseResolverForExternal.reset();
+ return;
+ }
+
+ const std::string& result = pr->result();
+
Debug::log(TRACE, "CDynamicPermissionRule: user returned {}", result);
if (result.starts_with("Allow once"))
@@ -226,9 +240,29 @@ void CDynamicPermissionManager::askForPermission(wl_client* client, const std::s
r->m_binaryPath = binaryPath;
} else if (result.starts_with("Allow"))
r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_ALLOW;
+
+ if (r->m_promiseResolverForExternal)
+ r->m_promiseResolverForExternal->resolve(r->m_allowMode);
+
+ r->m_promise.reset();
+ r->m_promiseResolverForExternal.reset();
});
}
+SP<CPromise<eDynamicPermissionAllowMode>> CDynamicPermissionManager::promiseFor(wl_client* client, eDynamicPermissionType permission) {
+ auto rule = std::ranges::find_if(m_rules, [client, permission](const auto& e) { return e->m_client == client && e->m_type == permission; });
+ if (rule == m_rules.end())
+ return nullptr;
+
+ if (!(*rule)->m_promise)
+ return nullptr;
+
+ if ((*rule)->m_promiseResolverForExternal)
+ return nullptr;
+
+ return CPromise<eDynamicPermissionAllowMode>::make([rule](SP<CPromiseResolver<eDynamicPermissionAllowMode>> r) { (*rule)->m_promiseResolverForExternal = r; });
+}
+
void CDynamicPermissionManager::removeRulesForClient(wl_client* client) {
std::erase_if(m_rules, [client](const auto& e) { return e->m_client == client; });
}
diff --git a/src/managers/permissions/DynamicPermissionManager.hpp b/src/managers/permissions/DynamicPermissionManager.hpp
index 681cbc1c..79dc56a6 100644
--- a/src/managers/permissions/DynamicPermissionManager.hpp
+++ b/src/managers/permissions/DynamicPermissionManager.hpp
@@ -5,7 +5,7 @@
#include "../../helpers/AsyncDialogBox.hpp"
#include <vector>
#include <wayland-server-core.h>
-#include <optional>
+#include "../../helpers/defer/Promise.hpp"
// NOLINTNEXTLINE
namespace re2 {
@@ -15,6 +15,7 @@ namespace re2 {
enum eDynamicPermissionType : uint8_t {
PERMISSION_TYPE_UNKNOWN = 0,
PERMISSION_TYPE_SCREENCOPY,
+ PERMISSION_TYPE_PLUGIN,
};
enum eDynamicPermissionRuleSource : uint8_t {
@@ -50,16 +51,18 @@ class CDynamicPermissionRule {
// user rule
CDynamicPermissionRule(wl_client* const client, eDynamicPermissionType type, eDynamicPermissionAllowMode defaultAllowMode = PERMISSION_RULE_ALLOW_MODE_ASK);
- const eDynamicPermissionType m_type = PERMISSION_TYPE_UNKNOWN;
- const eDynamicPermissionRuleSource m_source = PERMISSION_RULE_SOURCE_UNKNOWN;
- wl_client* const m_client = nullptr;
- std::string m_binaryPath = "";
- UP<re2::RE2> m_binaryRegex;
+ const eDynamicPermissionType m_type = PERMISSION_TYPE_UNKNOWN;
+ const eDynamicPermissionRuleSource m_source = PERMISSION_RULE_SOURCE_UNKNOWN;
+ wl_client* const m_client = nullptr;
+ std::string m_binaryPath = "";
+ UP<re2::RE2> m_binaryRegex;
- eDynamicPermissionAllowMode m_allowMode = PERMISSION_RULE_ALLOW_MODE_ASK;
- SP<CAsyncDialogBox> m_dialogBox; // for pending
+ eDynamicPermissionAllowMode m_allowMode = PERMISSION_RULE_ALLOW_MODE_ASK;
+ SP<CAsyncDialogBox> m_dialogBox; // for pending
+ SP<CPromise<std::string>> m_promise; // for pending
+ SP<CPromiseResolver<eDynamicPermissionAllowMode>> m_promiseResolverForExternal; // for external promise
- SDynamicPermissionRuleDestroyWrapper m_destroyWrapper;
+ SDynamicPermissionRuleDestroyWrapper m_destroyWrapper;
friend class CDynamicPermissionManager;
};
@@ -73,7 +76,11 @@ class CDynamicPermissionManager {
// (will continue returning false if the user does not agree, of course.)
eDynamicPermissionAllowMode clientPermissionMode(wl_client* client, eDynamicPermissionType permission);
- void removeRulesForClient(wl_client* client);
+ // get a promise for the result. Returns null if there already was one requested for the client.
+ // Returns null if state is not pending
+ SP<CPromise<eDynamicPermissionAllowMode>> promiseFor(wl_client* client, eDynamicPermissionType permission);
+
+ void removeRulesForClient(wl_client* client);
private:
void askForPermission(wl_client* client, const std::string& binaryName, eDynamicPermissionType type);