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