1 /*
2 * SPDX-FileCopyrightText: 2016-2016 CSSlayer <wengxt@gmail.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-or-later
5 *
6 */
7
8 #include "display.h"
9 #include <errno.h>
10 #include <poll.h>
11 #include <cassert>
12 #include <cstring>
13 #include <fcitx-utils/log.h>
14 #include "wl_callback.h"
15 #include "wl_output.h"
16 #include "wl_registry.h"
17
18 namespace fcitx::wayland {
19
createGlobalHelper(GlobalsFactoryBase * factory,std::pair<const uint32_t,std::tuple<std::string,uint32_t,uint32_t,std::shared_ptr<void>>> & globalsPair)20 void Display::createGlobalHelper(
21 GlobalsFactoryBase *factory,
22 std::pair<const uint32_t, std::tuple<std::string, uint32_t, uint32_t,
23 std::shared_ptr<void>>> &globalsPair) {
24 std::get<std::shared_ptr<void>>(globalsPair.second) = factory->create(
25 *registry(), globalsPair.first, std::get<2>(globalsPair.second));
26
27 globalCreatedSignal_(std::get<std::string>(globalsPair.second),
28 std::get<std::shared_ptr<void>>(globalsPair.second));
29 sync();
30 flush();
31 }
32
Display(wl_display * display)33 Display::Display(wl_display *display) : display_(display) {
34 wl_display_set_user_data(display, this);
35 auto *reg = registry();
36 reg->global().connect(
37 [this](uint32_t name, const char *interface, uint32_t version) {
38 auto result = globals_.emplace(std::make_pair(
39 name, std::make_tuple(interface, name, version, nullptr)));
40 auto iter = requestedGlobals_.find(interface);
41 if (iter != requestedGlobals_.end()) {
42 createGlobalHelper(iter->second.get(), *result.first);
43 }
44 });
45 reg->globalRemove().connect([this](uint32_t name) {
46 auto iter = globals_.find(name);
47 if (iter != globals_.end()) {
48 globalRemovedSignal_(std::get<std::string>(iter->second),
49 std::get<std::shared_ptr<void>>(iter->second));
50 const auto &interface = std::get<std::string>(iter->second);
51 auto localGlobalIter = requestedGlobals_.find(interface);
52 if (localGlobalIter != requestedGlobals_.end()) {
53 localGlobalIter->second->erase(name);
54 }
55 globals_.erase(iter);
56 }
57 });
58
59 requestGlobals<wayland::WlOutput>();
60 globalCreatedSignal_.connect([this](const std::string &interface,
61 const std::shared_ptr<void> &data) {
62 if (interface != wayland::WlOutput::interface) {
63 return;
64 }
65 auto *output = static_cast<wayland::WlOutput *>(data.get());
66 addOutput(output);
67 });
68 globalRemovedSignal_.connect([this](const std::string &interface,
69 const std::shared_ptr<void> &data) {
70 if (interface != wayland::WlOutput::interface) {
71 return;
72 }
73 auto *output = static_cast<wayland::WlOutput *>(data.get());
74 removeOutput(output);
75 });
76
77 sync();
78 flush();
79 }
80
~Display()81 Display::~Display() {}
82
roundtrip()83 void Display::roundtrip() { wl_display_roundtrip(*this); }
84
sync()85 void Display::sync() {
86 callbacks_.emplace_back(
87 std::make_unique<WlCallback>(wl_display_sync(*this)));
88 callbacks_.back()->done().connect(
89 [this, iter = std::prev(callbacks_.end())](uint32_t) {
90 callbacks_.erase(iter);
91 });
92 }
93
flush()94 void Display::flush() { wl_display_flush(*this); }
95
run()96 void Display::run() {
97 pollfd pfd;
98
99 pfd.fd = fd();
100 pfd.events = POLLIN | POLLERR | POLLHUP;
101
102 while (1) {
103 wl_display_dispatch_pending(*this);
104 auto ret = wl_display_flush(*this);
105 if (ret < 0 && errno != EAGAIN) {
106 break;
107 }
108
109 auto count = poll(&pfd, 1, -1);
110 if (count < 0 && errno != EINTR) {
111 break;
112 }
113
114 if (count == 1) {
115 auto event = pfd.revents;
116 // We can have cases where POLLIN and POLLHUP are both set for
117 // example. Don't break if both flags are set.
118 if ((event & POLLERR || event & POLLHUP) && !(event & POLLIN)) {
119 break;
120 }
121
122 if (event & POLLIN) {
123 ret = wl_display_dispatch(*this);
124 if (ret == -1) {
125 break;
126 }
127 }
128 }
129 }
130 }
131
registry()132 WlRegistry *Display::registry() {
133 if (!registry_) {
134 registry_ =
135 std::make_unique<WlRegistry>(wl_display_get_registry(*this));
136 }
137
138 return registry_.get();
139 }
140
141 const OutputInfomation *
outputInformation(wayland::WlOutput * output) const142 Display::outputInformation(wayland::WlOutput *output) const {
143 auto iter = outputInfo_.find(output);
144 if (iter == outputInfo_.end()) {
145 return nullptr;
146 }
147 return &iter->second;
148 }
149
addOutput(wayland::WlOutput * output)150 void Display::addOutput(wayland::WlOutput *output) {
151 outputInfo_.emplace(std::piecewise_construct, std::forward_as_tuple(output),
152 std::forward_as_tuple(output));
153 }
154
removeOutput(wayland::WlOutput * output)155 void Display::removeOutput(wayland::WlOutput *output) {
156 outputInfo_.erase(output);
157 }
158 } // namespace fcitx::wayland
159