1 /********************************************************************
2 Copyright © 2020 Roman Gilg <subdiff@gmail.com>
3 
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) version 3, or any
8 later version accepted by the membership of KDE e.V. (or its
9 successor approved by the membership of KDE e.V.), which shall
10 act as a proxy defined in Section 6 of version 3 of the license.
11 
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 Lesser General Public License for more details.
16 
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library.  If not, see <http://www.gnu.org/licenses/>.
19 *********************************************************************/
20 #include "wlr_output_configuration_v1.h"
21 
22 #include "event_queue.h"
23 #include "wayland_pointer_p.h"
24 #include "wlr_output_manager_v1.h"
25 
26 #include "wayland-wlr-output-management-v1-client-protocol.h"
27 
28 #include <memory>
29 #include <vector>
30 
31 namespace Wrapland
32 {
33 namespace Client
34 {
35 
36 struct ConfigurationHead {
37     WlrOutputHeadV1* head = nullptr;
38     zwlr_output_configuration_head_v1* native = nullptr;
39 
40     WlrOutputModeV1* mode = nullptr;
41 
42     struct {
43         QSize size;
44         int refresh = -1;
45     } customMode;
46 
47     QPoint position;
48     bool positionSet = false;
49     WlrOutputHeadV1::Transform transform = WlrOutputHeadV1::Transform::Normal;
50     bool transformSet = false;
51     double scale = 1.;
52     bool scaleSet = false;
53 };
54 
55 class Q_DECL_HIDDEN WlrOutputConfigurationV1::Private
56 {
57 public:
58     Private() = default;
59 
60     void setup(zwlr_output_configuration_v1* outputConfiguration);
61 
62     WaylandPointer<zwlr_output_configuration_v1, zwlr_output_configuration_v1_destroy>
63         outputConfiguration;
64     static const struct zwlr_output_configuration_v1_listener s_listener;
65     EventQueue* queue = nullptr;
66 
67     void send();
68     ConfigurationHead* getConfigurationHead(WlrOutputHeadV1* head);
69 
70     std::vector<std::unique_ptr<ConfigurationHead>> heads;
71     WlrOutputConfigurationV1* q;
72 
73 private:
74     static void succeededCallback(void* data, zwlr_output_configuration_v1* config);
75     static void failedCallback(void* data, zwlr_output_configuration_v1* config);
76     static void cancelledCallback(void* data, zwlr_output_configuration_v1* config);
77 };
78 
79 const zwlr_output_configuration_v1_listener WlrOutputConfigurationV1::Private::s_listener = {
80     succeededCallback,
81     failedCallback,
82     cancelledCallback,
83 };
84 
succeededCallback(void * data,zwlr_output_configuration_v1 * config)85 void WlrOutputConfigurationV1::Private::succeededCallback(void* data,
86                                                           zwlr_output_configuration_v1* config)
87 {
88     Q_UNUSED(config);
89     auto priv = reinterpret_cast<WlrOutputConfigurationV1::Private*>(data);
90     Q_EMIT priv->q->succeeded();
91 }
92 
failedCallback(void * data,zwlr_output_configuration_v1 * config)93 void WlrOutputConfigurationV1::Private::failedCallback(void* data,
94                                                        zwlr_output_configuration_v1* config)
95 {
96     Q_UNUSED(config);
97     auto priv = reinterpret_cast<WlrOutputConfigurationV1::Private*>(data);
98     Q_EMIT priv->q->failed();
99 }
100 
cancelledCallback(void * data,zwlr_output_configuration_v1 * config)101 void WlrOutputConfigurationV1::Private::cancelledCallback(void* data,
102                                                           zwlr_output_configuration_v1* config)
103 {
104     Q_UNUSED(config);
105     auto priv = reinterpret_cast<WlrOutputConfigurationV1::Private*>(data);
106     Q_EMIT priv->q->cancelled();
107 }
108 
WlrOutputConfigurationV1(QObject * parent)109 WlrOutputConfigurationV1::WlrOutputConfigurationV1(QObject* parent)
110     : QObject(parent)
111     , d(new Private)
112 {
113     d->q = this;
114 }
115 
~WlrOutputConfigurationV1()116 WlrOutputConfigurationV1::~WlrOutputConfigurationV1()
117 {
118     release();
119 }
120 
setup(zwlr_output_configuration_v1 * outputConfiguration)121 void WlrOutputConfigurationV1::setup(zwlr_output_configuration_v1* outputConfiguration)
122 {
123     Q_ASSERT(outputConfiguration);
124     Q_ASSERT(!d->outputConfiguration);
125 
126     d->outputConfiguration.setup(outputConfiguration);
127     d->setup(outputConfiguration);
128 }
129 
setup(zwlr_output_configuration_v1 * outputConfiguration)130 void WlrOutputConfigurationV1::Private::setup(zwlr_output_configuration_v1* outputConfiguration)
131 {
132     zwlr_output_configuration_v1_add_listener(outputConfiguration, &s_listener, this);
133 }
134 
getConfigurationHead(WlrOutputHeadV1 * head)135 ConfigurationHead* WlrOutputConfigurationV1::Private::getConfigurationHead(WlrOutputHeadV1* head)
136 {
137     for (auto& configurationHead : heads) {
138         if (configurationHead->head == head) {
139             return configurationHead.get();
140         }
141     }
142 
143     // Create a new configuration head struct and hand a reference back by calling this function
144     // again.
145     std::unique_ptr<ConfigurationHead> configurationHead(new ConfigurationHead);
146     configurationHead->head = head;
147     heads.push_back(std::move(configurationHead));
148 
149     return getConfigurationHead(head);
150 }
151 
send()152 void WlrOutputConfigurationV1::Private::send()
153 {
154     for (auto& head : heads) {
155         if (!head->native) {
156             continue;
157         }
158 
159         if (head->mode) {
160             zwlr_output_configuration_head_v1_set_mode(head->native, *head->mode);
161         } else if (head->customMode.refresh >= 0 && head->customMode.size.isValid()) {
162             zwlr_output_configuration_head_v1_set_custom_mode(head->native,
163                                                               head->customMode.size.width(),
164                                                               head->customMode.size.height(),
165                                                               head->customMode.refresh);
166         }
167 
168         if (head->positionSet) {
169             zwlr_output_configuration_head_v1_set_position(
170                 head->native, head->position.x(), head->position.y());
171         }
172 
173         if (head->transformSet) {
174             auto toNative = [](WlrOutputHeadV1::Transform transform) {
175                 switch (transform) {
176                 case WlrOutputHeadV1::Transform::Normal:
177                     return WL_OUTPUT_TRANSFORM_NORMAL;
178                 case WlrOutputHeadV1::Transform::Rotated90:
179                     return WL_OUTPUT_TRANSFORM_90;
180                 case WlrOutputHeadV1::Transform::Rotated180:
181                     return WL_OUTPUT_TRANSFORM_180;
182                 case WlrOutputHeadV1::Transform::Rotated270:
183                     return WL_OUTPUT_TRANSFORM_270;
184                 case WlrOutputHeadV1::Transform::Flipped:
185                     return WL_OUTPUT_TRANSFORM_FLIPPED;
186                 case WlrOutputHeadV1::Transform::Flipped90:
187                     return WL_OUTPUT_TRANSFORM_FLIPPED_90;
188                 case WlrOutputHeadV1::Transform::Flipped180:
189                     return WL_OUTPUT_TRANSFORM_FLIPPED_180;
190                 case WlrOutputHeadV1::Transform::Flipped270:
191                     return WL_OUTPUT_TRANSFORM_FLIPPED_270;
192                 }
193                 abort();
194             };
195             zwlr_output_configuration_head_v1_set_transform(head->native,
196                                                             toNative(head->transform));
197         }
198 
199         if (head->scaleSet) {
200             zwlr_output_configuration_head_v1_set_scale(head->native,
201                                                         wl_fixed_from_double(head->scale));
202         }
203     }
204 }
205 
release()206 void WlrOutputConfigurationV1::release()
207 {
208     d->outputConfiguration.release();
209 }
210 
setEventQueue(EventQueue * queue)211 void WlrOutputConfigurationV1::setEventQueue(EventQueue* queue)
212 {
213     d->queue = queue;
214 }
215 
eventQueue()216 EventQueue* WlrOutputConfigurationV1::eventQueue()
217 {
218     return d->queue;
219 }
220 
operator zwlr_output_configuration_v1*()221 WlrOutputConfigurationV1::operator zwlr_output_configuration_v1*()
222 {
223     return d->outputConfiguration;
224 }
225 
operator zwlr_output_configuration_v1*() const226 WlrOutputConfigurationV1::operator zwlr_output_configuration_v1*() const
227 {
228     return d->outputConfiguration;
229 }
230 
isValid() const231 bool WlrOutputConfigurationV1::isValid() const
232 {
233     return d->outputConfiguration.isValid();
234 }
235 
setEnabled(WlrOutputHeadV1 * head,bool enable)236 void WlrOutputConfigurationV1::setEnabled(WlrOutputHeadV1* head, bool enable)
237 {
238     auto configurationHead = d->getConfigurationHead(head);
239 
240     if (enable) {
241         if (!configurationHead->native) {
242             configurationHead->native
243                 = zwlr_output_configuration_v1_enable_head(d->outputConfiguration, *head);
244         }
245     } else {
246         zwlr_output_configuration_v1_disable_head(d->outputConfiguration, *head);
247     }
248 }
249 
setMode(WlrOutputHeadV1 * head,WlrOutputModeV1 * mode)250 void WlrOutputConfigurationV1::setMode(WlrOutputHeadV1* head, WlrOutputModeV1* mode)
251 {
252     d->getConfigurationHead(head)->mode = mode;
253 }
254 
setTransform(WlrOutputHeadV1 * head,WlrOutputHeadV1::Transform transform)255 void WlrOutputConfigurationV1::setTransform(WlrOutputHeadV1* head,
256                                             WlrOutputHeadV1::Transform transform)
257 {
258     auto configurationHead = d->getConfigurationHead(head);
259 
260     configurationHead->transform = transform;
261     configurationHead->transformSet = true;
262 }
263 
setPosition(WlrOutputHeadV1 * head,const QPoint & pos)264 void WlrOutputConfigurationV1::setPosition(WlrOutputHeadV1* head, const QPoint& pos)
265 {
266     auto configurationHead = d->getConfigurationHead(head);
267 
268     configurationHead->position = pos;
269     configurationHead->positionSet = true;
270 }
271 
setScale(WlrOutputHeadV1 * head,double scale)272 void WlrOutputConfigurationV1::setScale(WlrOutputHeadV1* head, double scale)
273 {
274     auto configurationHead = d->getConfigurationHead(head);
275 
276     configurationHead->scale = scale;
277     configurationHead->scaleSet = true;
278 }
279 
test()280 void WlrOutputConfigurationV1::test()
281 {
282     d->send();
283     zwlr_output_configuration_v1_test(d->outputConfiguration);
284 }
285 
apply()286 void WlrOutputConfigurationV1::apply()
287 {
288     d->send();
289     zwlr_output_configuration_v1_apply(d->outputConfiguration);
290 }
291 
292 }
293 }
294