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