1 /*
2 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6 #include "output_interface.h"
7 #include "display.h"
8 #include "global_p.h"
9
10 #include <QVector>
11
12 #include <wayland-server.h>
13
14 namespace KWayland
15 {
16 namespace Server
17 {
18 class OutputInterface::Private : public Global::Private
19 {
20 public:
21 struct ResourceData {
22 wl_resource *resource;
23 uint32_t version;
24 };
25 Private(OutputInterface *q, Display *d);
26 ~Private() override;
27 void sendMode(wl_resource *resource, const Mode &mode);
28 void sendDone(const ResourceData &data);
29 void updateGeometry();
30 void updateScale();
31
32 QSize physicalSize;
33 QPoint globalPosition;
34 QString manufacturer = QStringLiteral("org.kde.kwin");
35 QString model = QStringLiteral("none");
36 int scale = 1;
37 SubPixel subPixel = SubPixel::Unknown;
38 Transform transform = Transform::Normal;
39 QList<Mode> modes;
40 QList<ResourceData> resources;
41 struct {
42 DpmsMode mode = DpmsMode::On;
43 bool supported = false;
44 } dpms;
45
46 static OutputInterface *get(wl_resource *native);
47
48 private:
49 static Private *cast(wl_resource *native);
50 static void releaseCallback(wl_client *client, wl_resource *resource);
51 static void unbind(wl_resource *resource);
52 void bind(wl_client *client, uint32_t version, uint32_t id) override;
53 int32_t toTransform() const;
54 int32_t toSubPixel() const;
55 void sendGeometry(wl_resource *resource);
56 void sendScale(const ResourceData &data);
57
58 OutputInterface *q;
59 static QVector<Private *> s_privates;
60 static const struct wl_output_interface s_interface;
61 static const quint32 s_version;
62 };
63
64 QVector<OutputInterface::Private *> OutputInterface::Private::s_privates;
65 const quint32 OutputInterface::Private::s_version = 3;
66
Private(OutputInterface * q,Display * d)67 OutputInterface::Private::Private(OutputInterface *q, Display *d)
68 : Global::Private(d, &wl_output_interface, s_version)
69 , q(q)
70 {
71 s_privates << this;
72 }
73
~Private()74 OutputInterface::Private::~Private()
75 {
76 s_privates.removeAll(this);
77 }
78
79 #ifndef K_DOXYGEN
80 const struct wl_output_interface OutputInterface::Private::s_interface = {releaseCallback};
81 #endif
82
releaseCallback(wl_client * client,wl_resource * resource)83 void OutputInterface::Private::releaseCallback(wl_client *client, wl_resource *resource)
84 {
85 Q_UNUSED(client);
86 unbind(resource);
87 }
88
get(wl_resource * native)89 OutputInterface *OutputInterface::Private::get(wl_resource *native)
90 {
91 if (Private *p = cast(native)) {
92 return p->q;
93 }
94 return nullptr;
95 }
96
cast(wl_resource * native)97 OutputInterface::Private *OutputInterface::Private::cast(wl_resource *native)
98 {
99 for (auto it = s_privates.constBegin(); it != s_privates.constEnd(); ++it) {
100 const auto &resources = (*it)->resources;
101 auto rit = std::find_if(resources.constBegin(), resources.constEnd(), [native](const ResourceData &data) {
102 return data.resource == native;
103 });
104 if (rit != resources.constEnd()) {
105 return (*it);
106 }
107 }
108 return nullptr;
109 }
110
OutputInterface(Display * display,QObject * parent)111 OutputInterface::OutputInterface(Display *display, QObject *parent)
112 : Global(new Private(this, display), parent)
113 {
114 Q_D();
115 connect(this, &OutputInterface::currentModeChanged, this, [this] {
116 Q_D();
117 auto currentModeIt = std::find_if(d->modes.constBegin(), d->modes.constEnd(), [](const Mode &mode) {
118 return mode.flags.testFlag(ModeFlag::Current);
119 });
120 if (currentModeIt == d->modes.constEnd()) {
121 return;
122 }
123 for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) {
124 d->sendMode((*it).resource, *currentModeIt);
125 d->sendDone(*it);
126 }
127 wl_display_flush_clients(*(d->display));
128 });
129 connect(this, &OutputInterface::subPixelChanged, this, [d] {
130 d->updateGeometry();
131 });
132 connect(this, &OutputInterface::transformChanged, this, [d] {
133 d->updateGeometry();
134 });
135 connect(this, &OutputInterface::globalPositionChanged, this, [d] {
136 d->updateGeometry();
137 });
138 connect(this, &OutputInterface::modelChanged, this, [d] {
139 d->updateGeometry();
140 });
141 connect(this, &OutputInterface::manufacturerChanged, this, [d] {
142 d->updateGeometry();
143 });
144 connect(this, &OutputInterface::scaleChanged, this, [d] {
145 d->updateScale();
146 });
147 }
148
149 OutputInterface::~OutputInterface() = default;
150
pixelSize() const151 QSize OutputInterface::pixelSize() const
152 {
153 Q_D();
154 auto it = std::find_if(d->modes.constBegin(), d->modes.constEnd(), [](const Mode &mode) {
155 return mode.flags.testFlag(ModeFlag::Current);
156 });
157 if (it == d->modes.constEnd()) {
158 return QSize();
159 }
160 return (*it).size;
161 }
162
refreshRate() const163 int OutputInterface::refreshRate() const
164 {
165 Q_D();
166 auto it = std::find_if(d->modes.constBegin(), d->modes.constEnd(), [](const Mode &mode) {
167 return mode.flags.testFlag(ModeFlag::Current);
168 });
169 if (it == d->modes.constEnd()) {
170 return 60000;
171 }
172 return (*it).refreshRate;
173 }
174
addMode(const QSize & size,OutputInterface::ModeFlags flags,int refreshRate)175 void OutputInterface::addMode(const QSize &size, OutputInterface::ModeFlags flags, int refreshRate)
176 {
177 Q_ASSERT(!isValid());
178 Q_D();
179
180 auto currentModeIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) {
181 return mode.flags.testFlag(ModeFlag::Current);
182 });
183 if (currentModeIt == d->modes.end() && !flags.testFlag(ModeFlag::Current)) {
184 // no mode with current flag - enforce
185 flags |= ModeFlag::Current;
186 }
187 if (currentModeIt != d->modes.end() && flags.testFlag(ModeFlag::Current)) {
188 // another mode has the current flag - remove
189 (*currentModeIt).flags &= ~uint(ModeFlag::Current);
190 }
191
192 if (flags.testFlag(ModeFlag::Preferred)) {
193 // remove from existing Preferred mode
194 auto preferredIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) {
195 return mode.flags.testFlag(ModeFlag::Preferred);
196 });
197 if (preferredIt != d->modes.end()) {
198 (*preferredIt).flags &= ~uint(ModeFlag::Preferred);
199 }
200 }
201
202 auto existingModeIt = std::find_if(d->modes.begin(), d->modes.end(), [size, refreshRate](const Mode &mode) {
203 return mode.size == size && mode.refreshRate == refreshRate;
204 });
205 auto emitChanges = [this, flags, size, refreshRate] {
206 Q_EMIT modesChanged();
207 if (flags.testFlag(ModeFlag::Current)) {
208 Q_EMIT refreshRateChanged(refreshRate);
209 Q_EMIT pixelSizeChanged(size);
210 Q_EMIT currentModeChanged();
211 }
212 };
213 if (existingModeIt != d->modes.end()) {
214 if ((*existingModeIt).flags == flags) {
215 // nothing to do
216 return;
217 }
218 (*existingModeIt).flags = flags;
219 emitChanges();
220 return;
221 }
222 Mode mode;
223 mode.size = size;
224 mode.refreshRate = refreshRate;
225 mode.flags = flags;
226 d->modes << mode;
227 emitChanges();
228 }
229
setCurrentMode(const QSize & size,int refreshRate)230 void OutputInterface::setCurrentMode(const QSize &size, int refreshRate)
231 {
232 Q_D();
233 auto currentModeIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) {
234 return mode.flags.testFlag(ModeFlag::Current);
235 });
236 if (currentModeIt != d->modes.end()) {
237 // another mode has the current flag - remove
238 (*currentModeIt).flags &= ~uint(ModeFlag::Current);
239 }
240
241 auto existingModeIt = std::find_if(d->modes.begin(), d->modes.end(), [size, refreshRate](const Mode &mode) {
242 return mode.size == size && mode.refreshRate == refreshRate;
243 });
244
245 Q_ASSERT(existingModeIt != d->modes.end());
246 (*existingModeIt).flags |= ModeFlag::Current;
247 Q_EMIT modesChanged();
248 Q_EMIT refreshRateChanged((*existingModeIt).refreshRate);
249 Q_EMIT pixelSizeChanged((*existingModeIt).size);
250 Q_EMIT currentModeChanged();
251 }
252
toTransform() const253 int32_t OutputInterface::Private::toTransform() const
254 {
255 switch (transform) {
256 case Transform::Normal:
257 return WL_OUTPUT_TRANSFORM_NORMAL;
258 case Transform::Rotated90:
259 return WL_OUTPUT_TRANSFORM_90;
260 case Transform::Rotated180:
261 return WL_OUTPUT_TRANSFORM_180;
262 case Transform::Rotated270:
263 return WL_OUTPUT_TRANSFORM_270;
264 case Transform::Flipped:
265 return WL_OUTPUT_TRANSFORM_FLIPPED;
266 case Transform::Flipped90:
267 return WL_OUTPUT_TRANSFORM_FLIPPED_90;
268 case Transform::Flipped180:
269 return WL_OUTPUT_TRANSFORM_FLIPPED_180;
270 case Transform::Flipped270:
271 return WL_OUTPUT_TRANSFORM_FLIPPED_270;
272 }
273 abort();
274 }
275
toSubPixel() const276 int32_t OutputInterface::Private::toSubPixel() const
277 {
278 switch (subPixel) {
279 case SubPixel::Unknown:
280 return WL_OUTPUT_SUBPIXEL_UNKNOWN;
281 case SubPixel::None:
282 return WL_OUTPUT_SUBPIXEL_NONE;
283 case SubPixel::HorizontalRGB:
284 return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
285 case SubPixel::HorizontalBGR:
286 return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;
287 case SubPixel::VerticalRGB:
288 return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;
289 case SubPixel::VerticalBGR:
290 return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;
291 }
292 abort();
293 }
294
bind(wl_client * client,uint32_t version,uint32_t id)295 void OutputInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id)
296 {
297 auto c = display->getConnection(client);
298 wl_resource *resource = c->createResource(&wl_output_interface, qMin(version, s_version), id);
299 if (!resource) {
300 wl_client_post_no_memory(client);
301 return;
302 }
303 wl_resource_set_user_data(resource, this);
304 wl_resource_set_implementation(resource, &s_interface, this, unbind);
305 ResourceData r;
306 r.resource = resource;
307 r.version = version;
308 resources << r;
309
310 sendGeometry(resource);
311 sendScale(r);
312
313 auto currentModeIt = modes.constEnd();
314 for (auto it = modes.constBegin(); it != modes.constEnd(); ++it) {
315 const Mode &mode = *it;
316 if (mode.flags.testFlag(ModeFlag::Current)) {
317 // needs to be sent as last mode
318 currentModeIt = it;
319 continue;
320 }
321 sendMode(resource, mode);
322 }
323
324 if (currentModeIt != modes.constEnd()) {
325 sendMode(resource, *currentModeIt);
326 }
327
328 sendDone(r);
329 c->flush();
330 }
331
unbind(wl_resource * resource)332 void OutputInterface::Private::unbind(wl_resource *resource)
333 {
334 Private *o = cast(resource);
335 if (!o) {
336 return;
337 }
338 auto it = std::find_if(o->resources.begin(), o->resources.end(), [resource](const ResourceData &r) {
339 return r.resource == resource;
340 });
341 if (it != o->resources.end()) {
342 o->resources.erase(it);
343 }
344 }
345
sendMode(wl_resource * resource,const Mode & mode)346 void OutputInterface::Private::sendMode(wl_resource *resource, const Mode &mode)
347 {
348 int32_t flags = 0;
349 if (mode.flags.testFlag(ModeFlag::Current)) {
350 flags |= WL_OUTPUT_MODE_CURRENT;
351 }
352 if (mode.flags.testFlag(ModeFlag::Preferred)) {
353 flags |= WL_OUTPUT_MODE_PREFERRED;
354 }
355 wl_output_send_mode(resource, flags, mode.size.width(), mode.size.height(), mode.refreshRate);
356 }
357
sendGeometry(wl_resource * resource)358 void OutputInterface::Private::sendGeometry(wl_resource *resource)
359 {
360 wl_output_send_geometry(resource,
361 globalPosition.x(),
362 globalPosition.y(),
363 physicalSize.width(),
364 physicalSize.height(),
365 toSubPixel(),
366 qPrintable(manufacturer),
367 qPrintable(model),
368 toTransform());
369 }
370
sendScale(const ResourceData & data)371 void OutputInterface::Private::sendScale(const ResourceData &data)
372 {
373 if (data.version < 2) {
374 return;
375 }
376 wl_output_send_scale(data.resource, scale);
377 }
378
sendDone(const ResourceData & data)379 void OutputInterface::Private::sendDone(const ResourceData &data)
380 {
381 if (data.version < 2) {
382 return;
383 }
384 wl_output_send_done(data.resource);
385 }
386
updateGeometry()387 void OutputInterface::Private::updateGeometry()
388 {
389 for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
390 sendGeometry((*it).resource);
391 sendDone(*it);
392 }
393 }
394
updateScale()395 void OutputInterface::Private::updateScale()
396 {
397 for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
398 sendScale(*it);
399 sendDone(*it);
400 }
401 }
402
403 // clang-format off
404 #define SETTER(setterName, type, argumentName) \
405 void OutputInterface::setterName(type arg) \
406 { \
407 Q_D(); \
408 if (d->argumentName == arg) { \
409 return; \
410 } \
411 d->argumentName = arg; \
412 Q_EMIT argumentName##Changed(d->argumentName); \
413 }
414 // clang-format on
415
SETTER(setPhysicalSize,const QSize &,physicalSize)416 SETTER(setPhysicalSize, const QSize &, physicalSize)
417 SETTER(setGlobalPosition, const QPoint &, globalPosition)
418 SETTER(setManufacturer, const QString &, manufacturer)
419 SETTER(setModel, const QString &, model)
420 SETTER(setScale, int, scale)
421 SETTER(setSubPixel, SubPixel, subPixel)
422 SETTER(setTransform, Transform, transform)
423
424 #undef SETTER
425
426 QSize OutputInterface::physicalSize() const
427 {
428 Q_D();
429 return d->physicalSize;
430 }
431
globalPosition() const432 QPoint OutputInterface::globalPosition() const
433 {
434 Q_D();
435 return d->globalPosition;
436 }
437
manufacturer() const438 QString OutputInterface::manufacturer() const
439 {
440 Q_D();
441 return d->manufacturer;
442 }
443
model() const444 QString OutputInterface::model() const
445 {
446 Q_D();
447 return d->model;
448 }
449
scale() const450 int OutputInterface::scale() const
451 {
452 Q_D();
453 return d->scale;
454 }
455
subPixel() const456 OutputInterface::SubPixel OutputInterface::subPixel() const
457 {
458 Q_D();
459 return d->subPixel;
460 }
461
transform() const462 OutputInterface::Transform OutputInterface::transform() const
463 {
464 Q_D();
465 return d->transform;
466 }
467
modes() const468 QList<OutputInterface::Mode> OutputInterface::modes() const
469 {
470 Q_D();
471 return d->modes;
472 }
473
setDpmsMode(OutputInterface::DpmsMode mode)474 void OutputInterface::setDpmsMode(OutputInterface::DpmsMode mode)
475 {
476 Q_D();
477 if (d->dpms.mode == mode) {
478 return;
479 }
480 d->dpms.mode = mode;
481 Q_EMIT dpmsModeChanged();
482 }
483
setDpmsSupported(bool supported)484 void OutputInterface::setDpmsSupported(bool supported)
485 {
486 Q_D();
487 if (d->dpms.supported == supported) {
488 return;
489 }
490 d->dpms.supported = supported;
491 Q_EMIT dpmsSupportedChanged();
492 }
493
dpmsMode() const494 OutputInterface::DpmsMode OutputInterface::dpmsMode() const
495 {
496 Q_D();
497 return d->dpms.mode;
498 }
499
isDpmsSupported() const500 bool OutputInterface::isDpmsSupported() const
501 {
502 Q_D();
503 return d->dpms.supported;
504 }
505
clientResources(ClientConnection * client) const506 QVector<wl_resource *> OutputInterface::clientResources(ClientConnection *client) const
507 {
508 Q_D();
509 QVector<wl_resource *> ret;
510 for (auto it = d->resources.constBegin(), end = d->resources.constEnd(); it != end; ++it) {
511 if (wl_resource_get_client((*it).resource) == client->client()) {
512 ret << (*it).resource;
513 }
514 }
515 return ret;
516 }
517
get(wl_resource * native)518 OutputInterface *OutputInterface::get(wl_resource *native)
519 {
520 return Private::get(native);
521 }
522
d_func() const523 OutputInterface::Private *OutputInterface::d_func() const
524 {
525 return reinterpret_cast<Private *>(d.data());
526 }
527
528 }
529 }
530