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