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 "output_p.h"
21 
22 #include "output_device_v1_p.h"
23 #include "wl_output_p.h"
24 #include "xdg_output_p.h"
25 
26 #include "display.h"
27 
28 #include "wayland/client.h"
29 #include "wayland/display.h"
30 
31 #include <cassert>
32 #include <functional>
33 #include <wayland-server.h>
34 #include <cmath> // for std::ceil()
35 
36 namespace Wrapland::Server
37 {
38 
Private(Display * display,Output * q)39 Output::Private::Private(Display* display, Output* q)
40     : display_handle(display)
41     , device{new OutputDeviceV1(q, display)}
42     , q_ptr{q}
43 {
44 }
45 
done()46 void Output::Private::done()
47 {
48     if (published.enabled != pending.enabled) {
49         if (pending.enabled) {
50             wayland_output.reset(new WlOutput(q_ptr, display_handle));
51             xdg_output.reset(new XdgOutput(q_ptr, display_handle));
52         } else {
53             wayland_output.reset();
54             xdg_output.reset();
55         }
56     }
57     if (pending.enabled) {
58         auto wayland_change = wayland_output->d_ptr->broadcast();
59         auto xdg_change = xdg_output->d_ptr->broadcast();
60         if (wayland_change || xdg_change) {
61             wayland_output->d_ptr->done();
62             xdg_output->d_ptr->done();
63         }
64     }
65     if (device->d_ptr->broadcast()) {
66         device->d_ptr->done();
67     }
68     published = pending;
69 }
70 
done_wl(Client * client) const71 void Output::Private::done_wl(Client* client) const
72 {
73     if (!wayland_output) {
74         return;
75     }
76 
77     auto binds = wayland_output->d_ptr->getBinds(client);
78     for (auto bind : binds) {
79         wayland_output->d_ptr->done(bind);
80     }
81 }
82 
get_mode_flags(Output::Mode const & mode,OutputState const & state)83 int32_t Output::Private::get_mode_flags(Output::Mode const& mode, OutputState const& state)
84 {
85     int32_t flags = 0;
86 
87     if (state.mode == mode) {
88         flags |= WL_OUTPUT_MODE_CURRENT;
89     }
90     if (mode.preferred) {
91         flags |= WL_OUTPUT_MODE_PREFERRED;
92     }
93     return flags;
94 }
95 
to_transform(Output::Transform transform)96 int32_t Output::Private::to_transform(Output::Transform transform)
97 {
98     switch (transform) {
99     case Output::Transform::Normal:
100         return WL_OUTPUT_TRANSFORM_NORMAL;
101     case Output::Transform::Rotated90:
102         return WL_OUTPUT_TRANSFORM_90;
103     case Output::Transform::Rotated180:
104         return WL_OUTPUT_TRANSFORM_180;
105     case Output::Transform::Rotated270:
106         return WL_OUTPUT_TRANSFORM_270;
107     case Output::Transform::Flipped:
108         return WL_OUTPUT_TRANSFORM_FLIPPED;
109     case Output::Transform::Flipped90:
110         return WL_OUTPUT_TRANSFORM_FLIPPED_90;
111     case Output::Transform::Flipped180:
112         return WL_OUTPUT_TRANSFORM_FLIPPED_180;
113     case Output::Transform::Flipped270:
114         return WL_OUTPUT_TRANSFORM_FLIPPED_270;
115     }
116     abort();
117 }
118 
update_client_scale()119 void Output::Private::update_client_scale()
120 {
121     auto logical_size = pending.geometry.size();
122     auto mode_size = pending.mode.size;
123 
124     if (logical_size.width() <= 0 || logical_size.height() <= 0 || mode_size.width() <= 0
125         || mode_size.height() <= 0) {
126         pending.client_scale = 1;
127         return;
128     }
129 
130     auto width_ratio = mode_size.width() / logical_size.width();
131     auto height_ratio = mode_size.height() / logical_size.height();
132 
133     pending.client_scale = std::ceil(std::max(width_ratio, height_ratio));
134 }
135 
operator ==(Mode const & mode) const136 bool Output::Mode::operator==(Mode const& mode) const
137 {
138     return size == mode.size && refresh_rate == mode.refresh_rate && id == mode.id;
139 }
140 
operator !=(Mode const & mode) const141 bool Output::Mode::operator!=(Mode const& mode) const
142 {
143     return !(*this == mode);
144 }
145 
Output(Display * display,QObject * parent)146 Output::Output(Display* display, QObject* parent)
147     : QObject(parent)
148     , d_ptr(new Private(display, this))
149 {
150 }
151 
152 Output::~Output() = default;
153 
done()154 void Output::done()
155 {
156     d_ptr->done();
157 }
158 
subpixel() const159 Output::Subpixel Output::subpixel() const
160 {
161     return d_ptr->pending.subpixel;
162 }
163 
set_subpixel(Subpixel subpixel)164 void Output::set_subpixel(Subpixel subpixel)
165 {
166     d_ptr->pending.subpixel = subpixel;
167 }
168 
transform() const169 Output::Transform Output::transform() const
170 {
171     return d_ptr->pending.transform;
172 }
173 
name() const174 std::string Output::name() const
175 {
176     return d_ptr->pending.info.name;
177 }
178 
description() const179 std::string Output::description() const
180 {
181     return d_ptr->pending.info.description;
182 }
183 
serial_mumber() const184 std::string Output::serial_mumber() const
185 {
186     return d_ptr->pending.info.serial_number;
187 }
188 
make() const189 std::string Output::make() const
190 {
191     return d_ptr->pending.info.make;
192 }
193 
model() const194 std::string Output::model() const
195 {
196     return d_ptr->pending.info.model;
197 }
198 
set_name(std::string const & name)199 void Output::set_name(std::string const& name)
200 {
201     d_ptr->pending.info.name = name;
202 }
203 
set_description(std::string const & description)204 void Output::set_description(std::string const& description)
205 {
206     d_ptr->pending.info.description = description;
207 }
208 
set_make(std::string const & make)209 void Output::set_make(std::string const& make)
210 {
211     d_ptr->pending.info.make = make;
212 }
213 
set_model(std::string const & model)214 void Output::set_model(std::string const& model)
215 {
216     d_ptr->pending.info.model = model;
217 }
218 
set_serial_number(std::string const & serial_number)219 void Output::set_serial_number(std::string const& serial_number)
220 {
221     d_ptr->pending.info.serial_number = serial_number;
222 }
223 
set_physical_size(QSize const & size)224 void Output::set_physical_size(QSize const& size)
225 {
226     d_ptr->pending.info.physical_size = size;
227 }
228 
set_connector_id(int id)229 void Output::set_connector_id(int id)
230 {
231     d_ptr->connector_id = id;
232 }
233 
generate_description()234 void Output::generate_description()
235 {
236     auto& info = d_ptr->pending.info;
237     std::string descr;
238     if (!info.make.empty()) {
239         descr = info.make;
240     }
241     if (!info.model.empty()) {
242         descr = (descr.empty() ? "" : descr + " ") + info.model;
243     }
244     if (!info.name.empty()) {
245         if (descr.empty()) {
246             descr = info.name;
247         } else {
248             descr += " (" + info.name + ")";
249         }
250     }
251     info.description = descr;
252 }
253 
enabled() const254 bool Output::enabled() const
255 {
256     return d_ptr->pending.enabled;
257 }
258 
set_enabled(bool enabled)259 void Output::set_enabled(bool enabled)
260 {
261     d_ptr->pending.enabled = enabled;
262 }
263 
physical_size() const264 QSize Output::physical_size() const
265 {
266     return d_ptr->pending.info.physical_size;
267 }
268 
connector_id() const269 int Output::connector_id() const
270 {
271     return d_ptr->connector_id;
272 }
273 
modes() const274 std::vector<Output::Mode> Output::modes() const
275 {
276     return d_ptr->modes;
277 }
278 
mode_id() const279 int Output::mode_id() const
280 {
281     return d_ptr->pending.mode.id;
282 }
283 
mode_size() const284 QSize Output::mode_size() const
285 {
286     return d_ptr->pending.mode.size;
287 }
288 
refresh_rate() const289 int Output::refresh_rate() const
290 {
291     return d_ptr->pending.mode.refresh_rate;
292 }
293 
add_mode(Mode const & mode)294 void Output::add_mode(Mode const& mode)
295 {
296     d_ptr->pending.mode = mode;
297 
298     auto it = std::find(d_ptr->modes.begin(), d_ptr->modes.end(), mode);
299 
300     if (it != d_ptr->modes.end()) {
301         d_ptr->modes.at(it - d_ptr->modes.begin()) = mode;
302     } else {
303         d_ptr->modes.push_back(mode);
304     }
305 }
306 
set_mode(int id)307 bool Output::set_mode(int id)
308 {
309     for (auto const& mode : d_ptr->modes) {
310         if (mode.id == id) {
311             d_ptr->pending.mode = mode;
312             d_ptr->update_client_scale();
313             return true;
314         }
315     }
316     return false;
317 }
318 
set_mode(Mode const & mode)319 bool Output::set_mode(Mode const& mode)
320 {
321     for (auto const& cmp : d_ptr->modes) {
322         if (cmp == mode) {
323             d_ptr->pending.mode = cmp;
324             d_ptr->update_client_scale();
325             return true;
326         }
327     }
328     return false;
329 }
330 
set_mode(QSize const & size,int refresh_rate)331 bool Output::set_mode(QSize const& size, int refresh_rate)
332 {
333     for (auto const& mode : d_ptr->modes) {
334         if (mode.size == size && mode.refresh_rate == refresh_rate) {
335             d_ptr->pending.mode = mode;
336             d_ptr->update_client_scale();
337             return true;
338         }
339     }
340     return false;
341 }
342 
geometry() const343 QRectF Output::geometry() const
344 {
345     return d_ptr->pending.geometry;
346 }
347 
set_transform(Transform transform)348 void Output::set_transform(Transform transform)
349 {
350     d_ptr->pending.transform = transform;
351 }
352 
set_geometry(QRectF const & geometry)353 void Output::set_geometry(QRectF const& geometry)
354 {
355     d_ptr->pending.geometry = geometry;
356     d_ptr->update_client_scale();
357 }
358 
client_scale() const359 int Output::client_scale() const
360 {
361     return d_ptr->pending.client_scale;
362 }
363 
set_dpms_supported(bool supported)364 void Output::set_dpms_supported(bool supported)
365 {
366     if (d_ptr->dpms.supported == supported) {
367         return;
368     }
369     d_ptr->dpms.supported = supported;
370     Q_EMIT dpms_supported_changed();
371 }
372 
dpms_supported() const373 bool Output::dpms_supported() const
374 {
375     return d_ptr->dpms.supported;
376 }
377 
set_dpms_mode(Output::DpmsMode mode)378 void Output::set_dpms_mode(Output::DpmsMode mode)
379 {
380     if (d_ptr->dpms.mode == mode) {
381         return;
382     }
383     d_ptr->dpms.mode = mode;
384     Q_EMIT dpms_mode_changed();
385 }
386 
dpms_mode() const387 Output::DpmsMode Output::dpms_mode() const
388 {
389     return d_ptr->dpms.mode;
390 }
391 
output_device_v1() const392 OutputDeviceV1* Output::output_device_v1() const
393 {
394     return d_ptr->device.get();
395 }
396 
wayland_output() const397 WlOutput* Output::wayland_output() const
398 {
399     return d_ptr->wayland_output.get();
400 }
401 
xdg_output() const402 XdgOutput* Output::xdg_output() const
403 {
404     return d_ptr->xdg_output.get();
405 }
406 
407 }
408