Skip to content

Commit 0302bfd

Browse files
committed
async: add Promise and use it for AsyncDialogBox
1 parent 4f868a1 commit 0302bfd

File tree

7 files changed

+208
-36
lines changed

7 files changed

+208
-36
lines changed

src/config/ConfigManager.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2906,6 +2906,8 @@ std::optional<std::string> CConfigManager::handlePermission(const std::string& c
29062906

29072907
if (data[1] == "screencopy")
29082908
type = PERMISSION_TYPE_SCREENCOPY;
2909+
else if (data[1] == "plugin")
2910+
type = PERMISSION_TYPE_PLUGIN;
29092911

29102912
if (data[2] == "ask")
29112913
mode = PERMISSION_RULE_ALLOW_MODE_ASK;

src/helpers/AsyncDialogBox.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,15 @@ void CAsyncDialogBox::onWrite(int fd, uint32_t mask) {
5959
if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
6060
Debug::log(LOG, "CAsyncDialogBox: dialog {:x} hung up, closed.");
6161

62-
if (m_onResolution)
63-
m_onResolution(m_stdout);
62+
m_promiseResolver->resolve(m_stdout);
6463

6564
wl_event_source_remove(m_readEventSource);
6665
m_selfReference.reset();
6766
return;
6867
}
6968
}
7069

71-
void CAsyncDialogBox::open(std::function<void(std::string)> onResolution) {
72-
m_onResolution = onResolution;
73-
70+
SP<CPromise<std::string>> CAsyncDialogBox::open() {
7471
std::string buttonsString = "";
7572
for (auto& b : m_buttons) {
7673
buttonsString += b + ";";
@@ -83,7 +80,7 @@ void CAsyncDialogBox::open(std::function<void(std::string)> onResolution) {
8380
int outPipe[2];
8481
if (pipe(outPipe)) {
8582
Debug::log(ERR, "CAsyncDialogBox::open: failed to pipe()");
86-
return;
83+
return nullptr;
8784
}
8885

8986
m_pipeReadFd = CFileDescriptor(outPipe[0]);
@@ -94,21 +91,25 @@ void CAsyncDialogBox::open(std::function<void(std::string)> onResolution) {
9491

9592
if (!m_readEventSource) {
9693
Debug::log(ERR, "CAsyncDialogBox::open: failed to add read fd to loop");
97-
return;
94+
return nullptr;
9895
}
9996

10097
m_selfReference = m_selfWeakReference.lock();
10198

10299
if (!proc.runAsync()) {
103100
Debug::log(ERR, "CAsyncDialogBox::open: failed to run async");
104101
wl_event_source_remove(m_readEventSource);
105-
return;
102+
return nullptr;
106103
}
107104

108105
m_dialogPid = proc.pid();
109106

110107
// close the write fd, only the dialog owns it now
111108
close(outPipe[1]);
109+
110+
auto promise = CPromise<std::string>::make([this](SP<CPromiseResolver<std::string>> r) { m_promiseResolver = r; });
111+
112+
return promise;
112113
}
113114

114115
void CAsyncDialogBox::kill() {

src/helpers/AsyncDialogBox.hpp

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "../macros.hpp"
44
#include "./memory/Memory.hpp"
5+
#include "./defer/Promise.hpp"
56

67
#include <vector>
78
#include <functional>
@@ -15,29 +16,30 @@ class CAsyncDialogBox {
1516
public:
1617
static SP<CAsyncDialogBox> create(const std::string& title, const std::string& description, std::vector<std::string> buttons);
1718

18-
CAsyncDialogBox(const CAsyncDialogBox&) = delete;
19-
CAsyncDialogBox(CAsyncDialogBox&&) = delete;
20-
CAsyncDialogBox& operator=(const CAsyncDialogBox&) = delete;
21-
CAsyncDialogBox& operator=(CAsyncDialogBox&&) = delete;
19+
CAsyncDialogBox(const CAsyncDialogBox&) = delete;
20+
CAsyncDialogBox(CAsyncDialogBox&&) = delete;
21+
CAsyncDialogBox& operator=(const CAsyncDialogBox&) = delete;
22+
CAsyncDialogBox& operator=(CAsyncDialogBox&&) = delete;
2223

23-
void open(std::function<void(std::string)> onResolution);
24-
void kill();
25-
bool isRunning() const;
24+
SP<CPromise<std::string>> open();
25+
void kill();
26+
bool isRunning() const;
2627

27-
void onWrite(int fd, uint32_t mask);
28+
void onWrite(int fd, uint32_t mask);
2829

2930
private:
3031
CAsyncDialogBox(const std::string& title, const std::string& description, std::vector<std::string> buttons);
3132

32-
pid_t m_dialogPid = 0;
33-
wl_event_source* m_readEventSource = nullptr;
34-
std::function<void(std::string)> m_onResolution;
35-
Hyprutils::OS::CFileDescriptor m_pipeReadFd;
36-
std::string m_stdout = "";
33+
pid_t m_dialogPid = 0;
34+
wl_event_source* m_readEventSource = nullptr;
35+
Hyprutils::OS::CFileDescriptor m_pipeReadFd;
36+
std::string m_stdout = "";
3737

38-
const std::string m_title;
39-
const std::string m_description;
40-
const std::vector<std::string> m_buttons;
38+
const std::string m_title;
39+
const std::string m_description;
40+
const std::vector<std::string> m_buttons;
41+
42+
SP<CPromiseResolver<std::string>> m_promiseResolver;
4143

4244
// WARNING: cyclic reference. This will be removed once the event source is removed to avoid dangling pointers
4345
SP<CAsyncDialogBox> m_selfReference;

src/helpers/defer/Promise.hpp

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#pragma once
2+
3+
#include <functional>
4+
#include <string>
5+
#include "../memory/Memory.hpp"
6+
7+
// TODO: move into hyprutils
8+
9+
template <typename T>
10+
class CPromise;
11+
template <typename T>
12+
class CPromiseResult;
13+
14+
template <typename T>
15+
class CPromiseResolver {
16+
public:
17+
CPromiseResolver(const CPromiseResolver&) = delete;
18+
CPromiseResolver(CPromiseResolver&&) = delete;
19+
CPromiseResolver& operator=(const CPromiseResolver&) = delete;
20+
CPromiseResolver& operator=(CPromiseResolver&&) = delete;
21+
22+
void resolve(T value) {
23+
if (m_promise->m_result)
24+
return;
25+
26+
m_promise->m_result = CPromiseResult<T>::result(value);
27+
28+
if (!m_promise->m_then)
29+
return;
30+
31+
m_promise->m_then(m_promise->m_result);
32+
}
33+
34+
void reject(const std::string& reason) {
35+
if (m_promise->m_result)
36+
return;
37+
38+
m_promise->m_result = CPromiseResult<T>::err(reason);
39+
40+
if (!m_promise->m_then)
41+
return;
42+
43+
m_promise->m_then(m_promise->m_result);
44+
}
45+
46+
private:
47+
CPromiseResolver(SP<CPromise<T>> promise) : m_promise(promise) {}
48+
49+
SP<CPromise<T>> m_promise;
50+
51+
friend class CPromise<T>;
52+
};
53+
54+
template <typename T>
55+
class CPromiseResult {
56+
public:
57+
bool hasError() {
58+
return m_hasError;
59+
}
60+
61+
T result() {
62+
return m_result;
63+
}
64+
65+
std::string error() {
66+
return m_error;
67+
}
68+
69+
private:
70+
static SP<CPromiseResult<T>> result(T result) {
71+
auto p = SP<CPromiseResult<T>>(new CPromiseResult<T>());
72+
p->m_result = result;
73+
return p;
74+
}
75+
76+
static SP<CPromiseResult<T>> err(std::string reason) {
77+
auto p = SP<CPromiseResult<T>>(new CPromiseResult<T>());
78+
p->m_error = reason;
79+
p->m_hasError = true;
80+
return p;
81+
}
82+
83+
T m_result = {};
84+
std::string m_error = {};
85+
bool m_hasError = false;
86+
87+
friend class CPromiseResolver<T>;
88+
};
89+
90+
template <typename T>
91+
class CPromise {
92+
public:
93+
CPromise(const CPromise&) = delete;
94+
CPromise(CPromise&&) = delete;
95+
CPromise& operator=(const CPromise&) = delete;
96+
CPromise& operator=(CPromise&&) = delete;
97+
98+
static SP<CPromise> make(const std::function<void(SP<CPromiseResolver<T>>)>& fn) {
99+
auto sp = SP<CPromise<T>>(new CPromise<T>());
100+
fn(SP<CPromiseResolver<T>>(new CPromiseResolver<T>(sp)));
101+
return sp;
102+
}
103+
104+
void then(std::function<void(SP<CPromiseResult<T>>)>&& fn) {
105+
m_then = std::move(fn);
106+
if (m_result)
107+
m_then(m_result);
108+
}
109+
110+
private:
111+
CPromise() = default;
112+
113+
const std::function<void(SP<CPromiseResult<T>>)> m_fn;
114+
std::function<void(SP<CPromiseResult<T>>)> m_then;
115+
SP<CPromiseResult<T>> m_result;
116+
117+
friend class CPromiseResult<T>;
118+
friend class CPromiseResolver<T>;
119+
};

src/managers/ANRManager.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,14 @@ void CANRManager::SANRData::runDialog(const std::string& title, const std::strin
169169
appClass.empty() ? "unknown" : appClass),
170170
std::vector<std::string>{"Terminate", "Wait"});
171171

172-
dialogBox->open([dialogWmPID, this](std::string result) {
172+
dialogBox->open()->then([dialogWmPID, this](SP<CPromiseResult<std::string>> r) {
173+
if (r->hasError()) {
174+
Debug::log(ERR, "CANRManager::SANRData::runDialog: error spawning dialog");
175+
return;
176+
}
177+
178+
const auto& result = r->result();
179+
173180
if (result.starts_with("Terminate"))
174181
::kill(dialogWmPID, SIGKILL);
175182
else if (result.starts_with("Wait"))

src/managers/permissions/DynamicPermissionManager.cpp

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ static const char* permissionToString(eDynamicPermissionType type) {
4848
switch (type) {
4949
case PERMISSION_TYPE_UNKNOWN: return "PERMISSION_TYPE_UNKNOWN";
5050
case PERMISSION_TYPE_SCREENCOPY: return "PERMISSION_TYPE_SCREENCOPY";
51+
case PERMISSION_TYPE_PLUGIN: return "PERMISSION_TYPE_PLUGIN";
5152
}
5253

5354
return "error";
@@ -57,6 +58,7 @@ static const char* permissionToHumanString(eDynamicPermissionType type) {
5758
switch (type) {
5859
case PERMISSION_TYPE_UNKNOWN: return "requesting an unknown permission";
5960
case PERMISSION_TYPE_SCREENCOPY: return "trying to capture your screen";
61+
case PERMISSION_TYPE_PLUGIN: return "trying to load a plugin";
6062
}
6163

6264
return "error";
@@ -210,10 +212,22 @@ void CDynamicPermissionManager::askForPermission(wl_client* client, const std::s
210212
return;
211213
}
212214

213-
rule->m_dialogBox->open([r = WP<CDynamicPermissionRule>(rule), binaryPath](std::string result) {
215+
rule->m_promise = rule->m_dialogBox->open();
216+
rule->m_promise->then([r = WP<CDynamicPermissionRule>(rule), binaryPath](SP<CPromiseResult<std::string>> pr) {
214217
if (!r)
215218
return;
216219

220+
if (pr->hasError()) {
221+
// not reachable for now
222+
Debug::log(TRACE, "CDynamicPermissionRule: error spawning dialog box");
223+
if (r->m_promiseResolverForExternal)
224+
r->m_promiseResolverForExternal->reject("error spawning dialog box");
225+
r->m_promiseResolverForExternal.reset();
226+
return;
227+
}
228+
229+
const std::string& result = pr->result();
230+
217231
Debug::log(TRACE, "CDynamicPermissionRule: user returned {}", result);
218232

219233
if (result.starts_with("Allow once"))
@@ -226,9 +240,29 @@ void CDynamicPermissionManager::askForPermission(wl_client* client, const std::s
226240
r->m_binaryPath = binaryPath;
227241
} else if (result.starts_with("Allow"))
228242
r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_ALLOW;
243+
244+
if (r->m_promiseResolverForExternal)
245+
r->m_promiseResolverForExternal->resolve(r->m_allowMode);
246+
247+
r->m_promise.reset();
248+
r->m_promiseResolverForExternal.reset();
229249
});
230250
}
231251

252+
SP<CPromise<eDynamicPermissionAllowMode>> CDynamicPermissionManager::promiseFor(wl_client* client, eDynamicPermissionType permission) {
253+
auto rule = std::ranges::find_if(m_rules, [client, permission](const auto& e) { return e->m_client == client && e->m_type == permission; });
254+
if (rule == m_rules.end())
255+
return nullptr;
256+
257+
if (!(*rule)->m_promise)
258+
return nullptr;
259+
260+
if ((*rule)->m_promiseResolverForExternal)
261+
return nullptr;
262+
263+
return CPromise<eDynamicPermissionAllowMode>::make([rule](SP<CPromiseResolver<eDynamicPermissionAllowMode>> r) { (*rule)->m_promiseResolverForExternal = r; });
264+
}
265+
232266
void CDynamicPermissionManager::removeRulesForClient(wl_client* client) {
233267
std::erase_if(m_rules, [client](const auto& e) { return e->m_client == client; });
234268
}

src/managers/permissions/DynamicPermissionManager.hpp

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include "../../helpers/AsyncDialogBox.hpp"
66
#include <vector>
77
#include <wayland-server-core.h>
8-
#include <optional>
8+
#include "../../helpers/defer/Promise.hpp"
99

1010
// NOLINTNEXTLINE
1111
namespace re2 {
@@ -15,6 +15,7 @@ namespace re2 {
1515
enum eDynamicPermissionType : uint8_t {
1616
PERMISSION_TYPE_UNKNOWN = 0,
1717
PERMISSION_TYPE_SCREENCOPY,
18+
PERMISSION_TYPE_PLUGIN,
1819
};
1920

2021
enum eDynamicPermissionRuleSource : uint8_t {
@@ -50,16 +51,18 @@ class CDynamicPermissionRule {
5051
// user rule
5152
CDynamicPermissionRule(wl_client* const client, eDynamicPermissionType type, eDynamicPermissionAllowMode defaultAllowMode = PERMISSION_RULE_ALLOW_MODE_ASK);
5253

53-
const eDynamicPermissionType m_type = PERMISSION_TYPE_UNKNOWN;
54-
const eDynamicPermissionRuleSource m_source = PERMISSION_RULE_SOURCE_UNKNOWN;
55-
wl_client* const m_client = nullptr;
56-
std::string m_binaryPath = "";
57-
UP<re2::RE2> m_binaryRegex;
54+
const eDynamicPermissionType m_type = PERMISSION_TYPE_UNKNOWN;
55+
const eDynamicPermissionRuleSource m_source = PERMISSION_RULE_SOURCE_UNKNOWN;
56+
wl_client* const m_client = nullptr;
57+
std::string m_binaryPath = "";
58+
UP<re2::RE2> m_binaryRegex;
5859

59-
eDynamicPermissionAllowMode m_allowMode = PERMISSION_RULE_ALLOW_MODE_ASK;
60-
SP<CAsyncDialogBox> m_dialogBox; // for pending
60+
eDynamicPermissionAllowMode m_allowMode = PERMISSION_RULE_ALLOW_MODE_ASK;
61+
SP<CAsyncDialogBox> m_dialogBox; // for pending
62+
SP<CPromise<std::string>> m_promise; // for pending
63+
SP<CPromiseResolver<eDynamicPermissionAllowMode>> m_promiseResolverForExternal; // for external promise
6164

62-
SDynamicPermissionRuleDestroyWrapper m_destroyWrapper;
65+
SDynamicPermissionRuleDestroyWrapper m_destroyWrapper;
6366

6467
friend class CDynamicPermissionManager;
6568
};
@@ -73,7 +76,11 @@ class CDynamicPermissionManager {
7376
// (will continue returning false if the user does not agree, of course.)
7477
eDynamicPermissionAllowMode clientPermissionMode(wl_client* client, eDynamicPermissionType permission);
7578

76-
void removeRulesForClient(wl_client* client);
79+
// get a promise for the result. Returns null if there already was one requested for the client.
80+
// Returns null if state is not pending
81+
SP<CPromise<eDynamicPermissionAllowMode>> promiseFor(wl_client* client, eDynamicPermissionType permission);
82+
83+
void removeRulesForClient(wl_client* client);
7784

7885
private:
7986
void askForPermission(wl_client* client, const std::string& binaryName, eDynamicPermissionType type);

0 commit comments

Comments
 (0)