1/*
2 SPDX-FileCopyrightText: 2013 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.h"
7#include "wayland_pointer_p.h"
8// Qt
9#include <QList>
10#include <QPoint>
11#include <QRect>
12// wayland
13#include <wayland-client-protocol.h>
14
15namespace KWayland
16{
17namespace Client
18{
19namespace
20{
21typedef QList<Output::Mode> Modes;
22}
23
24class Q_DECL_HIDDEN Output::Private
25{
26public:
27 Private(Output *q);
28 ~Private();
29 void setup(wl_output *o);
30
31 WaylandPointer<wl_output, wl_output_release> output;
32 EventQueue *queue = nullptr;
33 QSize physicalSize;
34 QPoint globalPosition;
35 QString manufacturer;
36 QString model;
37 int scale = 1;
38 SubPixel subPixel = SubPixel::Unknown;
39 Transform transform = Transform::Normal;
40 Modes modes;
41 Modes::iterator currentMode = modes.end();
42 QString name;
43 QString description;
44
45 static Output *get(wl_output *o);
46
47private:
48 static void geometryCallback(void *data,
49 wl_output *output,
50 int32_t x,
51 int32_t y,
52 int32_t physicalWidth,
53 int32_t physicalHeight,
54 int32_t subPixel,
55 const char *make,
56 const char *model,
57 int32_t transform);
58 static void modeCallback(void *data, wl_output *output, uint32_t flags, int32_t width, int32_t height, int32_t refresh);
59 static void doneCallback(void *data, wl_output *output);
60 static void scaleCallback(void *data, wl_output *output, int32_t scale);
61 static void nameCallback(void *data, struct wl_output *wl_output, const char *name);
62 static void descriptionCallback(void *data, struct wl_output *wl_output, const char *description);
63 void setPhysicalSize(const QSize &size);
64 void setGlobalPosition(const QPoint &pos);
65 void setManufacturer(const QString &manufacturer);
66 void setModel(const QString &model);
67 void setScale(int scale);
68 void setSubPixel(SubPixel subPixel);
69 void setTransform(Transform transform);
70 void addMode(uint32_t flags, int32_t width, int32_t height, int32_t refresh);
71
72 Output *q;
73 static struct wl_output_listener s_outputListener;
74
75 static QList<Private *> s_allOutputs;
76};
77
78QList<Output::Private *> Output::Private::s_allOutputs;
79
80Output::Private::Private(Output *q)
81 : q(q)
82{
83 s_allOutputs << this;
84}
85
86Output::Private::~Private()
87{
88 s_allOutputs.removeOne(t: this);
89}
90
91Output *Output::Private::get(wl_output *o)
92{
93 auto it = std::find_if(first: s_allOutputs.constBegin(), last: s_allOutputs.constEnd(), pred: [o](Private *p) {
94 const wl_output *reference = p->output;
95 return reference == o;
96 });
97 if (it != s_allOutputs.constEnd()) {
98 return (*it)->q;
99 }
100 return nullptr;
101}
102
103void Output::Private::setup(wl_output *o)
104{
105 Q_ASSERT(o);
106 Q_ASSERT(!output);
107 output.setup(pointer: o);
108 wl_output_add_listener(wl_output: output, listener: &s_outputListener, data: this);
109}
110
111bool Output::Mode::operator==(const Output::Mode &m) const
112{
113 return size == m.size && refreshRate == m.refreshRate && flags == m.flags && output == m.output;
114}
115
116Output::Output(QObject *parent)
117 : QObject(parent)
118 , d(new Private(this))
119{
120}
121
122Output::~Output()
123{
124 d->output.release();
125}
126
127wl_output_listener Output::Private::s_outputListener = {
128 .geometry: geometryCallback, .mode: modeCallback, .done: doneCallback, .scale: scaleCallback,
129#ifdef WL_OUTPUT_NAME_SINCE_VERSION
130 .name: nameCallback,
131#endif
132#ifdef WL_OUTPUT_DESCRIPTION_SINCE_VERSION
133 .description: descriptionCallback,
134#endif
135};
136
137void Output::Private::geometryCallback(void *data,
138 wl_output *output,
139 int32_t x,
140 int32_t y,
141 int32_t physicalWidth,
142 int32_t physicalHeight,
143 int32_t subPixel,
144 const char *make,
145 const char *model,
146 int32_t transform)
147{
148 Q_UNUSED(transform)
149 auto o = reinterpret_cast<Output::Private *>(data);
150 Q_ASSERT(o->output == output);
151 o->setGlobalPosition(QPoint(x, y));
152 o->setManufacturer(make);
153 o->setModel(model);
154 o->setPhysicalSize(QSize(physicalWidth, physicalHeight));
155 auto toSubPixel = [subPixel]() {
156 switch (subPixel) {
157 case WL_OUTPUT_SUBPIXEL_NONE:
158 return SubPixel::None;
159 case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB:
160 return SubPixel::HorizontalRGB;
161 case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR:
162 return SubPixel::HorizontalBGR;
163 case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB:
164 return SubPixel::VerticalRGB;
165 case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR:
166 return SubPixel::VerticalBGR;
167 case WL_OUTPUT_SUBPIXEL_UNKNOWN:
168 default:
169 return SubPixel::Unknown;
170 }
171 };
172 o->setSubPixel(toSubPixel());
173 auto toTransform = [transform]() {
174 switch (transform) {
175 case WL_OUTPUT_TRANSFORM_90:
176 return Transform::Rotated90;
177 case WL_OUTPUT_TRANSFORM_180:
178 return Transform::Rotated180;
179 case WL_OUTPUT_TRANSFORM_270:
180 return Transform::Rotated270;
181 case WL_OUTPUT_TRANSFORM_FLIPPED:
182 return Transform::Flipped;
183 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
184 return Transform::Flipped90;
185 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
186 return Transform::Flipped180;
187 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
188 return Transform::Flipped270;
189 case WL_OUTPUT_TRANSFORM_NORMAL:
190 default:
191 return Transform::Normal;
192 }
193 };
194 o->setTransform(toTransform());
195}
196
197void Output::Private::modeCallback(void *data, wl_output *output, uint32_t flags, int32_t width, int32_t height, int32_t refresh)
198{
199 auto o = reinterpret_cast<Output::Private *>(data);
200 Q_ASSERT(o->output == output);
201 o->addMode(flags, width, height, refresh);
202}
203
204void Output::Private::addMode(uint32_t flags, int32_t width, int32_t height, int32_t refresh)
205{
206 Mode mode;
207 mode.output = QPointer<Output>(q);
208 mode.refreshRate = refresh;
209 mode.size = QSize(width, height);
210 if (flags & WL_OUTPUT_MODE_CURRENT) {
211 mode.flags |= Mode::Flag::Current;
212 }
213 if (flags & WL_OUTPUT_MODE_PREFERRED) {
214 mode.flags |= Mode::Flag::Preferred;
215 }
216 auto currentIt = modes.insert(before: modes.end(), t: mode);
217 bool existing = false;
218 if (flags & WL_OUTPUT_MODE_CURRENT) {
219 auto it = modes.begin();
220 while (it != currentIt) {
221 auto &m = (*it);
222 if (m.flags.testFlag(flag: Mode::Flag::Current)) {
223 m.flags &= ~Mode::Flags(Mode::Flag::Current);
224 Q_EMIT q->modeChanged(mode: m);
225 }
226 if (m.refreshRate == mode.refreshRate && m.size == mode.size) {
227 it = modes.erase(pos: it);
228 existing = true;
229 } else {
230 it++;
231 }
232 }
233 currentMode = currentIt;
234 }
235 if (existing) {
236 Q_EMIT q->modeChanged(mode);
237 } else {
238 Q_EMIT q->modeAdded(mode);
239 }
240}
241
242void Output::Private::scaleCallback(void *data, wl_output *output, int32_t scale)
243{
244 auto o = reinterpret_cast<Output::Private *>(data);
245 Q_ASSERT(o->output == output);
246 o->setScale(scale);
247}
248
249void Output::Private::nameCallback(void *data, struct wl_output *wl_output, const char *name)
250{
251 auto o = reinterpret_cast<Output::Private *>(data);
252 Q_ASSERT(o->output == wl_output);
253 o->name = QString::fromUtf8(utf8: name);
254}
255
256void Output::Private::descriptionCallback(void *data, struct wl_output *wl_output, const char *description)
257{
258 auto o = reinterpret_cast<Output::Private *>(data);
259 Q_ASSERT(o->output == wl_output);
260 o->description = QString::fromUtf8(utf8: description);
261}
262
263void Output::Private::doneCallback(void *data, wl_output *output)
264{
265 auto o = reinterpret_cast<Output::Private *>(data);
266 Q_ASSERT(o->output == output);
267 Q_EMIT o->q->changed();
268}
269
270void Output::setup(wl_output *output)
271{
272 d->setup(output);
273}
274
275EventQueue *Output::eventQueue() const
276{
277 return d->queue;
278}
279
280void Output::setEventQueue(EventQueue *queue)
281{
282 d->queue = queue;
283}
284
285void Output::Private::setGlobalPosition(const QPoint &pos)
286{
287 globalPosition = pos;
288}
289
290void Output::Private::setManufacturer(const QString &m)
291{
292 manufacturer = m;
293}
294
295void Output::Private::setModel(const QString &m)
296{
297 model = m;
298}
299
300void Output::Private::setPhysicalSize(const QSize &size)
301{
302 physicalSize = size;
303}
304
305void Output::Private::setScale(int s)
306{
307 scale = s;
308}
309
310QRect Output::geometry() const
311{
312 if (d->currentMode == d->modes.end()) {
313 return QRect();
314 }
315 return QRect(d->globalPosition, pixelSize());
316}
317
318void Output::Private::setSubPixel(Output::SubPixel s)
319{
320 subPixel = s;
321}
322
323void Output::Private::setTransform(Output::Transform t)
324{
325 transform = t;
326}
327
328QPoint Output::globalPosition() const
329{
330 return d->globalPosition;
331}
332
333QString Output::manufacturer() const
334{
335 return d->manufacturer;
336}
337
338QString Output::model() const
339{
340 return d->model;
341}
342
343wl_output *Output::output()
344{
345 return d->output;
346}
347
348QSize Output::physicalSize() const
349{
350 return d->physicalSize;
351}
352
353QSize Output::pixelSize() const
354{
355 if (d->currentMode == d->modes.end()) {
356 return QSize();
357 }
358 return (*d->currentMode).size;
359}
360
361int Output::refreshRate() const
362{
363 if (d->currentMode == d->modes.end()) {
364 return 0;
365 }
366 return (*d->currentMode).refreshRate;
367}
368
369int Output::scale() const
370{
371 return d->scale;
372}
373
374bool Output::isValid() const
375{
376 return d->output.isValid();
377}
378
379Output::SubPixel Output::subPixel() const
380{
381 return d->subPixel;
382}
383
384Output::Transform Output::transform() const
385{
386 return d->transform;
387}
388
389QList<Output::Mode> Output::modes() const
390{
391 return d->modes;
392}
393
394QString Output::name() const
395{
396 return d->name;
397}
398
399QString Output::description() const
400{
401 return d->description;
402}
403
404Output::operator wl_output *()
405{
406 return d->output;
407}
408
409Output::operator wl_output *() const
410{
411 return d->output;
412}
413
414Output *Output::get(wl_output *o)
415{
416 return Private::get(o);
417}
418
419void Output::destroy()
420{
421 d->output.destroy();
422}
423
424}
425}
426
427#include "moc_output.cpp"
428

source code of kwayland/src/client/output.cpp