1 /*
2  * SPDX-FileCopyrightText: 2015-2015 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 
8 #include <exception>
9 #include <functional>
10 #include <mutex>
11 
12 #if defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__)
13 #define __INCLUDE_LEVEL__ 2
14 #endif
15 #include <systemd/sd-event.h>
16 #include "event.h"
17 #include "log.h"
18 
19 namespace fcitx {
20 
IOEventFlagsToEpollFlags(IOEventFlags flags)21 static uint32_t IOEventFlagsToEpollFlags(IOEventFlags flags) {
22     uint32_t result = 0;
23     if (flags & IOEventFlag::In) {
24         result |= EPOLLIN;
25     }
26     if (flags & IOEventFlag::Out) {
27         result |= EPOLLOUT;
28     }
29     if (flags & IOEventFlag::Err) {
30         result |= EPOLLERR;
31     }
32     if (flags & IOEventFlag::Hup) {
33         result |= EPOLLHUP;
34     }
35     if (flags & IOEventFlag::EdgeTrigger) {
36         result |= EPOLLET;
37     }
38     return result;
39 }
40 
EpollFlagsToIOEventFlags(uint32_t flags)41 static IOEventFlags EpollFlagsToIOEventFlags(uint32_t flags) {
42     return ((flags & EPOLLIN) ? IOEventFlag::In : IOEventFlags()) |
43            ((flags & EPOLLOUT) ? IOEventFlag::Out : IOEventFlags()) |
44            ((flags & EPOLLERR) ? IOEventFlag::Err : IOEventFlags()) |
45            ((flags & EPOLLHUP) ? IOEventFlag::Hup : IOEventFlags()) |
46            ((flags & EPOLLET) ? IOEventFlag::EdgeTrigger : IOEventFlags());
47 }
48 
49 template <typename Interface>
50 struct SDEventSourceBase : public Interface {
51 public:
~SDEventSourceBasefcitx::SDEventSourceBase52     ~SDEventSourceBase() {
53         if (eventSource_) {
54             sd_event_source_set_enabled(eventSource_, SD_EVENT_OFF);
55             sd_event_source_set_userdata(eventSource_, nullptr);
56             sd_event_source_unref(eventSource_);
57         }
58     }
59 
setEventSourcefcitx::SDEventSourceBase60     void setEventSource(sd_event_source *event) { eventSource_ = event; }
61 
isEnabledfcitx::SDEventSourceBase62     bool isEnabled() const override {
63         int result = 0, err;
64         if ((err = sd_event_source_get_enabled(eventSource_, &result)) < 0) {
65             throw EventLoopException(err);
66         }
67         return result != SD_EVENT_OFF;
68     }
69 
setEnabledfcitx::SDEventSourceBase70     void setEnabled(bool enabled) override {
71         sd_event_source_set_enabled(eventSource_,
72                                     enabled ? SD_EVENT_ON : SD_EVENT_OFF);
73     }
74 
isOneShotfcitx::SDEventSourceBase75     bool isOneShot() const override {
76         int result = 0, err;
77         if ((err = sd_event_source_get_enabled(eventSource_, &result)) < 0) {
78             throw EventLoopException(err);
79         }
80         return result == SD_EVENT_ONESHOT;
81     }
82 
setOneShotfcitx::SDEventSourceBase83     void setOneShot() override {
84         sd_event_source_set_enabled(eventSource_, SD_EVENT_ONESHOT);
85     }
86 
87 protected:
88     sd_event_source *eventSource_;
89 };
90 
91 struct SDEventSource : public SDEventSourceBase<EventSource> {
SDEventSourcefcitx::SDEventSource92     SDEventSource(EventCallback _callback) : callback_(std::move(_callback)) {}
93 
94     EventCallback callback_;
95 };
96 
97 struct SDEventSourceIO : public SDEventSourceBase<EventSourceIO> {
SDEventSourceIOfcitx::SDEventSourceIO98     SDEventSourceIO(IOCallback _callback) : callback_(std::move(_callback)) {}
99 
fdfcitx::SDEventSourceIO100     int fd() const override {
101         int ret = sd_event_source_get_io_fd(eventSource_);
102         if (ret < 0) {
103             throw EventLoopException(ret);
104         }
105         return ret;
106     }
107 
setFdfcitx::SDEventSourceIO108     void setFd(int fd) override {
109         int ret = sd_event_source_set_io_fd(eventSource_, fd);
110         if (ret < 0) {
111             throw EventLoopException(ret);
112         }
113     }
114 
eventsfcitx::SDEventSourceIO115     IOEventFlags events() const override {
116         uint32_t events;
117         int ret = sd_event_source_get_io_events(eventSource_, &events);
118         if (ret < 0) {
119             throw EventLoopException(ret);
120         }
121         return EpollFlagsToIOEventFlags(events);
122     }
123 
setEventsfcitx::SDEventSourceIO124     void setEvents(IOEventFlags flags) override {
125         int ret = sd_event_source_set_io_events(
126             eventSource_, IOEventFlagsToEpollFlags(flags));
127         if (ret < 0) {
128             throw EventLoopException(ret);
129         }
130     }
131 
reventsfcitx::SDEventSourceIO132     IOEventFlags revents() const override {
133         uint32_t revents;
134         int ret = sd_event_source_get_io_revents(eventSource_, &revents);
135         if (ret < 0) {
136             throw EventLoopException(ret);
137         }
138         return EpollFlagsToIOEventFlags(revents);
139     }
140 
141     IOCallback callback_;
142 };
143 
144 struct SDEventSourceTime : public SDEventSourceBase<EventSourceTime> {
SDEventSourceTimefcitx::SDEventSourceTime145     SDEventSourceTime(TimeCallback _callback)
146         : callback_(std::move(_callback)) {}
147 
timefcitx::SDEventSourceTime148     uint64_t time() const override {
149         uint64_t time;
150         int err = sd_event_source_get_time(eventSource_, &time);
151         if (err < 0) {
152             throw EventLoopException(err);
153         }
154         return time;
155     }
156 
setTimefcitx::SDEventSourceTime157     void setTime(uint64_t time) override {
158         int ret = sd_event_source_set_time(eventSource_, time);
159         if (ret < 0) {
160             throw EventLoopException(ret);
161         }
162     }
163 
accuracyfcitx::SDEventSourceTime164     uint64_t accuracy() const override {
165         uint64_t time;
166         int err = sd_event_source_get_time_accuracy(eventSource_, &time);
167         if (err < 0) {
168             throw EventLoopException(err);
169         }
170         return time;
171     }
172 
setAccuracyfcitx::SDEventSourceTime173     void setAccuracy(uint64_t time) override {
174         int ret = sd_event_source_set_time_accuracy(eventSource_, time);
175         if (ret < 0) {
176             throw EventLoopException(ret);
177         }
178     }
179 
clockfcitx::SDEventSourceTime180     clockid_t clock() const override {
181         clockid_t clock;
182         int err = sd_event_source_get_time_clock(eventSource_, &clock);
183         if (err < 0) {
184             throw EventLoopException(err);
185         }
186         return clock;
187     }
188 
189     TimeCallback callback_;
190 };
191 
192 class EventLoopPrivate {
193 public:
EventLoopPrivate()194     EventLoopPrivate() {
195         if (sd_event_new(&event_) < 0) {
196             throw std::runtime_error("Create sd_event failed.");
197         }
198     }
199 
~EventLoopPrivate()200     ~EventLoopPrivate() { sd_event_unref(event_); }
201 
202     std::mutex mutex_;
203     sd_event *event_;
204 };
205 
EventLoop()206 EventLoop::EventLoop() : d_ptr(std::make_unique<EventLoopPrivate>()) {}
207 
~EventLoop()208 EventLoop::~EventLoop() {}
209 
impl()210 const char *EventLoop::impl() { return "sd-event"; }
211 
nativeHandle()212 void *EventLoop::nativeHandle() {
213     FCITX_D();
214     return d->event_;
215 }
216 
exec()217 bool EventLoop::exec() {
218     FCITX_D();
219     int r = sd_event_loop(d->event_);
220     return r >= 0;
221 }
222 
exit()223 void EventLoop::exit() {
224     FCITX_D();
225     sd_event_exit(d->event_, 0);
226 }
227 
IOEventCallback(sd_event_source *,int fd,uint32_t revents,void * userdata)228 int IOEventCallback(sd_event_source *, int fd, uint32_t revents,
229                     void *userdata) {
230     auto *source = static_cast<SDEventSourceIO *>(userdata);
231     if (!source) {
232         return 0;
233     }
234     try {
235         auto result =
236             source->callback_(source, fd, EpollFlagsToIOEventFlags(revents));
237         return result ? 0 : -1;
238     } catch (const std::exception &e) {
239         FCITX_FATAL() << e.what();
240     }
241     return -1;
242 }
243 
addIOEvent(int fd,IOEventFlags flags,IOCallback callback)244 std::unique_ptr<EventSourceIO> EventLoop::addIOEvent(int fd, IOEventFlags flags,
245                                                      IOCallback callback) {
246     FCITX_D();
247     auto source = std::make_unique<SDEventSourceIO>(std::move(callback));
248     sd_event_source *sdEventSource;
249     int err;
250     if ((err = sd_event_add_io(d->event_, &sdEventSource, fd,
251                                IOEventFlagsToEpollFlags(flags), IOEventCallback,
252                                source.get())) < 0) {
253         throw EventLoopException(err);
254     }
255     source->setEventSource(sdEventSource);
256     return source;
257 }
258 
TimeEventCallback(sd_event_source *,uint64_t usec,void * userdata)259 int TimeEventCallback(sd_event_source *, uint64_t usec, void *userdata) {
260     auto *source = static_cast<SDEventSourceTime *>(userdata);
261     if (!source) {
262         return 0;
263     }
264     try {
265         auto result = source->callback_(source, usec);
266         return result ? 0 : -1;
267     } catch (const std::exception &e) {
268         // some abnormal things threw
269         FCITX_ERROR() << e.what();
270         abort();
271     }
272     return -1;
273 }
274 
275 std::unique_ptr<EventSourceTime>
addTimeEvent(clockid_t clock,uint64_t usec,uint64_t accuracy,TimeCallback callback)276 EventLoop::addTimeEvent(clockid_t clock, uint64_t usec, uint64_t accuracy,
277                         TimeCallback callback) {
278     FCITX_D();
279     auto source = std::make_unique<SDEventSourceTime>(std::move(callback));
280     sd_event_source *sdEventSource;
281     int err;
282     if ((err = sd_event_add_time(d->event_, &sdEventSource, clock, usec,
283                                  accuracy, TimeEventCallback, source.get())) <
284         0) {
285         throw EventLoopException(err);
286     }
287     source->setEventSource(sdEventSource);
288     return source;
289 }
290 
StaticEventCallback(sd_event_source *,void * userdata)291 int StaticEventCallback(sd_event_source *, void *userdata) {
292     auto *source = static_cast<SDEventSource *>(userdata);
293     if (!source) {
294         return 0;
295     }
296     try {
297         auto result = source->callback_(source);
298         return result ? 0 : -1;
299     } catch (const std::exception &e) {
300         // some abnormal things threw
301         FCITX_ERROR() << e.what();
302         abort();
303     }
304     return -1;
305 }
306 
addExitEvent(EventCallback callback)307 std::unique_ptr<EventSource> EventLoop::addExitEvent(EventCallback callback) {
308     FCITX_D();
309     auto source = std::make_unique<SDEventSource>(std::move(callback));
310     sd_event_source *sdEventSource;
311     int err;
312     if ((err = sd_event_add_exit(d->event_, &sdEventSource, StaticEventCallback,
313                                  source.get())) < 0) {
314         throw EventLoopException(err);
315     }
316     source->setEventSource(sdEventSource);
317     return source;
318 }
319 
addDeferEvent(EventCallback callback)320 std::unique_ptr<EventSource> EventLoop::addDeferEvent(EventCallback callback) {
321     FCITX_D();
322     auto source = std::make_unique<SDEventSource>(std::move(callback));
323     sd_event_source *sdEventSource;
324     int err;
325     if ((err = sd_event_add_defer(d->event_, &sdEventSource,
326                                   StaticEventCallback, source.get())) < 0) {
327         throw EventLoopException(err);
328     }
329     source->setEventSource(sdEventSource);
330     return source;
331 }
332 } // namespace fcitx
333