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 | |
15 | namespace KWayland |
16 | { |
17 | namespace Client |
18 | { |
19 | namespace |
20 | { |
21 | typedef QList<Output::Mode> Modes; |
22 | } |
23 | |
24 | class Q_DECL_HIDDEN Output::Private |
25 | { |
26 | public: |
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 | |
47 | private: |
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 | |
78 | QList<Output::Private *> Output::Private::s_allOutputs; |
79 | |
80 | Output::Private::Private(Output *q) |
81 | : q(q) |
82 | { |
83 | s_allOutputs << this; |
84 | } |
85 | |
86 | Output::Private::~Private() |
87 | { |
88 | s_allOutputs.removeOne(t: this); |
89 | } |
90 | |
91 | Output *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 | |
103 | void 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 | |
111 | bool 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 | |
116 | Output::Output(QObject *parent) |
117 | : QObject(parent) |
118 | , d(new Private(this)) |
119 | { |
120 | } |
121 | |
122 | Output::~Output() |
123 | { |
124 | d->output.release(); |
125 | } |
126 | |
127 | wl_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 | |
137 | void 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 | |
197 | void 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 | |
204 | void 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 | |
242 | void 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 | |
249 | void 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 | |
256 | void 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 | |
263 | void 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 | |
270 | void Output::setup(wl_output *output) |
271 | { |
272 | d->setup(output); |
273 | } |
274 | |
275 | EventQueue *Output::eventQueue() const |
276 | { |
277 | return d->queue; |
278 | } |
279 | |
280 | void Output::setEventQueue(EventQueue *queue) |
281 | { |
282 | d->queue = queue; |
283 | } |
284 | |
285 | void Output::Private::setGlobalPosition(const QPoint &pos) |
286 | { |
287 | globalPosition = pos; |
288 | } |
289 | |
290 | void Output::Private::setManufacturer(const QString &m) |
291 | { |
292 | manufacturer = m; |
293 | } |
294 | |
295 | void Output::Private::setModel(const QString &m) |
296 | { |
297 | model = m; |
298 | } |
299 | |
300 | void Output::Private::setPhysicalSize(const QSize &size) |
301 | { |
302 | physicalSize = size; |
303 | } |
304 | |
305 | void Output::Private::setScale(int s) |
306 | { |
307 | scale = s; |
308 | } |
309 | |
310 | QRect Output::geometry() const |
311 | { |
312 | if (d->currentMode == d->modes.end()) { |
313 | return QRect(); |
314 | } |
315 | return QRect(d->globalPosition, pixelSize()); |
316 | } |
317 | |
318 | void Output::Private::setSubPixel(Output::SubPixel s) |
319 | { |
320 | subPixel = s; |
321 | } |
322 | |
323 | void Output::Private::setTransform(Output::Transform t) |
324 | { |
325 | transform = t; |
326 | } |
327 | |
328 | QPoint Output::globalPosition() const |
329 | { |
330 | return d->globalPosition; |
331 | } |
332 | |
333 | QString Output::manufacturer() const |
334 | { |
335 | return d->manufacturer; |
336 | } |
337 | |
338 | QString Output::model() const |
339 | { |
340 | return d->model; |
341 | } |
342 | |
343 | wl_output *Output::output() |
344 | { |
345 | return d->output; |
346 | } |
347 | |
348 | QSize Output::physicalSize() const |
349 | { |
350 | return d->physicalSize; |
351 | } |
352 | |
353 | QSize Output::pixelSize() const |
354 | { |
355 | if (d->currentMode == d->modes.end()) { |
356 | return QSize(); |
357 | } |
358 | return (*d->currentMode).size; |
359 | } |
360 | |
361 | int Output::refreshRate() const |
362 | { |
363 | if (d->currentMode == d->modes.end()) { |
364 | return 0; |
365 | } |
366 | return (*d->currentMode).refreshRate; |
367 | } |
368 | |
369 | int Output::scale() const |
370 | { |
371 | return d->scale; |
372 | } |
373 | |
374 | bool Output::isValid() const |
375 | { |
376 | return d->output.isValid(); |
377 | } |
378 | |
379 | Output::SubPixel Output::subPixel() const |
380 | { |
381 | return d->subPixel; |
382 | } |
383 | |
384 | Output::Transform Output::transform() const |
385 | { |
386 | return d->transform; |
387 | } |
388 | |
389 | QList<Output::Mode> Output::modes() const |
390 | { |
391 | return d->modes; |
392 | } |
393 | |
394 | QString Output::name() const |
395 | { |
396 | return d->name; |
397 | } |
398 | |
399 | QString Output::description() const |
400 | { |
401 | return d->description; |
402 | } |
403 | |
404 | Output::operator wl_output *() |
405 | { |
406 | return d->output; |
407 | } |
408 | |
409 | Output::operator wl_output *() const |
410 | { |
411 | return d->output; |
412 | } |
413 | |
414 | Output *Output::get(wl_output *o) |
415 | { |
416 | return Private::get(o); |
417 | } |
418 | |
419 | void Output::destroy() |
420 | { |
421 | d->output.destroy(); |
422 | } |
423 | |
424 | } |
425 | } |
426 | |
427 | #include "moc_output.cpp" |
428 | |