Skip to content

Commit 0f5a0c2

Browse files
authored
Port wayland launcher to coroutine (#1666)
1 parent 96cb0f6 commit 0f5a0c2

3 files changed

Lines changed: 156 additions & 55 deletions

File tree

src/lib/fcitx-utils/awaiter.h

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2026 CSSlayer <wengxt@gmail.com>
3+
*
4+
* SPDX-License-Identifier: LGPL-2.1-or-later
5+
*
6+
*/
7+
#ifndef _FCITX_UTILS_AWAITER_H_
8+
#define _FCITX_UTILS_AWAITER_H_
9+
10+
#include <cstdint>
11+
#include <memory>
12+
#include <coroutine>
13+
#include <fcitx-utils/event.h>
14+
#include <fcitx-utils/eventloopinterface.h>
15+
16+
namespace fcitx {
17+
18+
/**
19+
* @brief Await an event loop time event.
20+
*
21+
* The pending time event is cancelled if the awaiter is destroyed before it
22+
* fires. It must only be used from the same thread as the event loop.
23+
*
24+
* @since 5.1.23
25+
*/
26+
class TimeAwaiter {
27+
public:
28+
/**
29+
* Create an awaiter for a monotonic-clock relative delay.
30+
*
31+
* @param eventLoop event loop on which to create the time event.
32+
* @param offset delay in microseconds.
33+
* @param accuracy requested timer accuracy in microseconds.
34+
* @return an awaiter that resumes after the delay.
35+
*/
36+
static TimeAwaiter after(EventLoop &eventLoop, uint64_t offset,
37+
uint64_t accuracy = 0) {
38+
return {eventLoop, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + offset,
39+
accuracy};
40+
}
41+
42+
/**
43+
* Create an awaiter for an absolute time.
44+
*
45+
* @param eventLoop event loop on which to create the time event.
46+
* @param clock clock used for @p time.
47+
* @param time absolute time in microseconds.
48+
* @param accuracy requested timer accuracy in microseconds.
49+
* @return an awaiter that resumes at the requested time.
50+
*/
51+
static TimeAwaiter at(EventLoop &eventLoop, clockid_t clock, uint64_t time,
52+
uint64_t accuracy = 0) {
53+
return {eventLoop, clock, time, accuracy};
54+
}
55+
56+
bool await_ready() const noexcept { return false; }
57+
58+
void await_suspend(std::coroutine_handle<> continuation) {
59+
source_ = eventLoop_.addTimeEvent(
60+
clock_, time_, accuracy_,
61+
[this, continuation](EventSourceTime *, uint64_t time) {
62+
firedTime_ = time;
63+
continuation.resume();
64+
return true;
65+
});
66+
}
67+
68+
/**
69+
* @return the time at which the event fired, in microseconds.
70+
*/
71+
uint64_t await_resume() const noexcept { return firedTime_; }
72+
73+
private:
74+
TimeAwaiter(EventLoop &eventLoop, clockid_t clock, uint64_t time,
75+
uint64_t accuracy)
76+
: eventLoop_(eventLoop), clock_(clock), time_(time),
77+
accuracy_(accuracy) {}
78+
EventLoop &eventLoop_;
79+
clockid_t clock_;
80+
uint64_t time_;
81+
uint64_t accuracy_;
82+
uint64_t firedTime_ = 0;
83+
std::unique_ptr<EventSourceTime> source_;
84+
};
85+
86+
} // namespace fcitx
87+
88+
#endif // _FCITX_UTILS_AWAITER_H_

src/lib/fcitx-utils/dbus/coroutine.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,12 @@ class AsyncReturn : protected AsyncCall {
7070
reply_.errorMessage().c_str());
7171
}
7272
if (reply_.signature() !=
73-
DBusSignatureTraits<ReturnTypes...>::signature::str()) {
73+
DBusSignatureTraits<std::tuple<ReturnTypes...>>::signature::str()) {
7474
throw MethodReturnTypeMismatch();
7575
}
7676

7777
MetaStringToDBusTupleType<
78-
typename DBusSignatureTraits<ReturnTypes...>::signature>
78+
typename DBusSignatureTraits<std::tuple<ReturnTypes...>>::signature>
7979
ret;
8080
reply_ >> ret;
8181
return ret;

src/tools/wayland-launcher.cpp

Lines changed: 66 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* SPDX-License-Identifier: LGPL-2.1-or-later
55
*
66
*/
7+
#include <cassert>
78
#include <charconv>
89
#include <cstdint>
910
#include <cstdio>
@@ -16,7 +17,10 @@
1617
#include <string>
1718
#include <utility>
1819
#include <getopt.h>
20+
#include "fcitx-utils/awaiter.h"
21+
#include "fcitx-utils/coroutine.h"
1922
#include "fcitx-utils/dbus/bus.h"
23+
#include "fcitx-utils/dbus/coroutine.h"
2024
#include "fcitx-utils/dbus/message.h"
2125
#include "fcitx-utils/dbus/servicewatcher.h"
2226
#include "fcitx-utils/environ.h"
@@ -83,84 +87,93 @@ class Launcher {
8387

8488
if (oldOwner.empty() && newOwner.empty()) {
8589
// This is initial query, let's just start service.
86-
auto message = bus_.createMethodCall("org.freedesktop.DBus", "/",
87-
"org.freedesktop.DBus",
88-
"StartServiceByName");
89-
message << "org.fcitx.Fcitx5";
90-
message << 0U;
91-
message.send();
90+
startServiceTask_ =
91+
std::make_unique<CoroutineTask<void>>(startService());
92+
startServiceTask_->resume();
9293
return;
9394
}
9495

9596
if (!newOwner.empty() && connectedName_.empty()) {
96-
delayedConnection_ = loop_.addTimeEvent(
97-
CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 1000000, 0,
98-
[this, newOwner](EventSource *, uint64_t) {
99-
connectTo(newOwner);
100-
return true;
101-
});
97+
connectedName_ = newOwner;
98+
connectionTask_ =
99+
std::make_unique<CoroutineTask<void>>(connectTo());
100+
connectionTask_->resume();
102101
}
103102
}
104103

105-
void connectTo(const std::string &newOwner) {
106-
connectedName_ = newOwner;
107-
if (fd_.isValid()) {
108-
if (reopen_) {
109-
auto message =
110-
bus_.createMethodCall(newOwner.data(), "/controller",
111-
"org.fcitx.Fcitx.Controller1",
112-
"ReopenWaylandConnectionSocket");
113-
message << display_;
114-
message << fd_;
115-
reply_ = message.callAsync(0, [this](dbus::Message &message) {
116-
reply(message);
117-
return true;
118-
});
119-
fd_.release();
120-
} else {
121-
auto message =
122-
bus_.createMethodCall(newOwner.data(), "/controller",
123-
"org.fcitx.Fcitx.Controller1",
124-
"OpenWaylandConnectionSocket");
125-
message << fd_;
126-
reply_ = message.callAsync(0, [this](dbus::Message &message) {
127-
reply(message);
128-
return true;
129-
});
130-
fd_.release();
104+
Coroutine<void> startService() {
105+
try {
106+
auto message = bus_.createMethodCall("org.freedesktop.DBus", "/",
107+
"org.freedesktop.DBus",
108+
"StartServiceByName");
109+
message << "org.fcitx.Fcitx5";
110+
message << 0U;
111+
auto result =
112+
co_await dbus::AsyncReturn<uint32_t>(std::move(message));
113+
if (result != 1 && result != 2) {
114+
throw std::runtime_error(
115+
"Unexpected response from StartServiceByName: " +
116+
std::to_string(result));
131117
}
132-
} else {
133-
auto message = bus_.createMethodCall(newOwner.data(), "/controller",
134-
"org.fcitx.Fcitx.Controller1",
135-
"OpenWaylandConnection");
136-
message << display_;
137-
reply_ = message.callAsync(0, [this](dbus::Message &message) {
138-
reply(message);
139-
return true;
140-
});
118+
} catch (const std::exception &e) {
119+
done_ = true;
120+
error_ = true;
121+
FCITX_ERROR() << "Failed to start Fcitx service: " << e.what();
122+
loop_.exit();
141123
}
124+
assert(startServiceTask_);
125+
std::move(*startServiceTask_).detach_handle();
142126
}
143127

144-
void reply(dbus::Message &message) {
145-
if (message.isError()) {
128+
Coroutine<void> connectTo() {
129+
try {
130+
co_await TimeAwaiter::after(loop_, 1000000);
131+
if (fd_.isValid()) {
132+
if (reopen_) {
133+
auto message = bus_.createMethodCall(
134+
connectedName_.data(), "/controller",
135+
"org.fcitx.Fcitx.Controller1",
136+
"ReopenWaylandConnectionSocket");
137+
message << display_;
138+
message << fd_;
139+
fd_.release();
140+
co_await dbus::AsyncReturn<>(std::move(message));
141+
} else {
142+
auto message = bus_.createMethodCall(
143+
connectedName_.data(), "/controller",
144+
"org.fcitx.Fcitx.Controller1",
145+
"OpenWaylandConnectionSocket");
146+
message << fd_;
147+
fd_.release();
148+
co_await dbus::AsyncReturn<>(std::move(message));
149+
}
150+
} else {
151+
auto message = bus_.createMethodCall(
152+
connectedName_.data(), "/controller",
153+
"org.fcitx.Fcitx.Controller1", "OpenWaylandConnection");
154+
message << display_;
155+
co_await dbus::AsyncReturn<>(std::move(message));
156+
}
157+
} catch (const std::exception &e) {
146158
done_ = true;
147159
error_ = true;
148-
FCITX_ERROR() << "DBus call error: " << message.errorName()
149-
<< message.errorMessage();
160+
FCITX_ERROR() << "Failed to open Wayland connection: " << e.what();
150161
loop_.exit();
151162
}
163+
assert(connectionTask_);
164+
std::move(*connectionTask_).detach_handle();
152165
}
153166

154167
dbus::Bus bus_{dbus::BusType::Session};
155168
std::unique_ptr<dbus::ServiceWatcher> watcher_;
156169
EventLoop loop_;
157170
std::unique_ptr<dbus::ServiceWatcherEntry> slot_;
158-
std::unique_ptr<dbus::Slot> reply_;
171+
std::unique_ptr<CoroutineTask<void>> startServiceTask_;
172+
std::unique_ptr<CoroutineTask<void>> connectionTask_;
159173
std::string connectedName_;
160174
UnixFD fd_;
161175
std::string display_;
162176
bool done_ = false;
163-
std::unique_ptr<EventSource> delayedConnection_;
164177
bool error_ = false;
165178
bool reopen_ = false;
166179
};

0 commit comments

Comments
 (0)