1// Copyright (C) 2019 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QWaylandCompositor>
5
6#include "qwaylandxdgoutputv1_p.h"
7#include "qwaylandoutput_p.h"
8
9#include <wayland-server.h>
10
11QT_BEGIN_NAMESPACE
12
13/*!
14 * \qmltype XdgOutputManagerV1
15 * \instantiates QWaylandXdgOutputManagerV1
16 * \inqmlmodule QtWayland.Compositor.XdgShell
17 * \since 5.14
18 * \brief Provides an extension for describing outputs in a desktop oriented fashion.
19 *
20 * The XdgOutputManagerV1 extension provides a way for a compositor to describe outputs in a way
21 * that is more in line with the concept of an output on desktop oriented systems.
22 *
23 * Some information may not make sense in other applications such as IVI systems.
24 *
25 * Typically the global compositor space on a desktop system is made of a
26 * contiguous or overlapping set of rectangular regions.
27 *
28 * XdgOutputManagerV1 corresponds to the Wayland interface, \c zxdg_output_manager_v1.
29 *
30 * To provide the functionality of the extension in a compositor, create an instance of the
31 * XdgOutputManagerV1 component and add it to the list of extensions supported by the compositor,
32 * and associated each XdgOutputV1 with its WaylandOutput:
33 *
34 * \qml
35 * import QtWayland.Compositor
36 *
37 * WaylandCompositor {
38 * XdgOutputManagerV1 {
39 * WaylandOutput {
40 * id: output1
41 *
42 * position: Qt.point(0, 0)
43 * window: Window {}
44 *
45 * XdgOutputV1 {
46 * name: "WL-1"
47 * logicalPosition: output1.position
48 * logicalSize: Qt.size(output1.geometry.width / output1.scaleFactor,
49 * output1.geometry.height / output1.scaleFactor)
50 * }
51 * }
52 *
53 * WaylandOutput {
54 * id: output2
55 *
56 * position: Qt.point(800, 0)
57 * window: Window {}
58 *
59 * XdgOutputV1 {
60 * name: "WL-2"
61 * logicalPosition: output2.position
62 * logicalSize: Qt.size(output2.geometry.width / output2.scaleFactor,
63 * output2.geometry.height / output2.scaleFactor)
64 * }
65 * }
66 * }
67 * }
68 * \endqml
69 */
70
71/*!
72 * \class QWaylandXdgOutputManagerV1
73 * \inmodule QtWaylandCompositor
74 * \since 5.14
75 * \brief Provides an extension for describing outputs in a desktop oriented fashion.
76 *
77 * The QWaylandXdgOutputManagerV1 extension provides a way for a compositor to describe outputs in a way
78 * that is more in line with the concept of an output on desktop oriented systems.
79 *
80 * Some information may not make sense in other applications such as IVI systems.
81 *
82 * QWaylandXdgOutputManagerV1 corresponds to the Wayland interface, \c zxdg_output_manager_v1.
83 */
84
85/*!
86 * Constructs a QWaylandXdgOutputManagerV1 object.
87 */
88QWaylandXdgOutputManagerV1::QWaylandXdgOutputManagerV1()
89 : QWaylandCompositorExtensionTemplate<QWaylandXdgOutputManagerV1>(*new QWaylandXdgOutputManagerV1Private())
90{
91}
92
93/*!
94 * Constructs a QWaylandXdgOutputManagerV1 object for the provided \a compositor.
95 */
96QWaylandXdgOutputManagerV1::QWaylandXdgOutputManagerV1(QWaylandCompositor *compositor)
97 : QWaylandCompositorExtensionTemplate<QWaylandXdgOutputManagerV1>(compositor, *new QWaylandXdgOutputManagerV1Private())
98{
99}
100
101// QWaylandXdgOutputManagerV1Private
102
103/*!
104 * Initializes the extension.
105 */
106void QWaylandXdgOutputManagerV1::initialize()
107{
108 Q_D(QWaylandXdgOutputManagerV1);
109
110 QWaylandCompositorExtensionTemplate::initialize();
111 QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer());
112 if (!compositor) {
113 qCWarning(qLcWaylandCompositor) << "Failed to find QWaylandCompositor when initializing QWaylandXdgOutputManagerV1";
114 return;
115 }
116 d->init(compositor->display(), d->interfaceVersion());
117}
118
119/*!
120 * Returns the Wayland interface for QWaylandXdgOutputManagerV1.
121 */
122const wl_interface *QWaylandXdgOutputManagerV1::interface()
123{
124 return QWaylandXdgOutputManagerV1Private::interface();
125}
126
127// QWaylandXdgOutputManagerV1Private
128
129void QWaylandXdgOutputManagerV1Private::registerXdgOutput(QWaylandOutput *output, QWaylandXdgOutputV1 *xdgOutput)
130{
131 if (!xdgOutputs.contains(output)) {
132 xdgOutputs[output] = xdgOutput;
133 QWaylandOutputPrivate::get(output)->xdgOutput = xdgOutput;
134 }
135}
136
137void QWaylandXdgOutputManagerV1Private::unregisterXdgOutput(QWaylandOutput *output)
138{
139 xdgOutputs.remove(output);
140}
141
142void QWaylandXdgOutputManagerV1Private::zxdg_output_manager_v1_get_xdg_output(Resource *resource,
143 uint32_t id,
144 wl_resource *outputResource)
145{
146 Q_Q(QWaylandXdgOutputManagerV1);
147
148 // Verify if the associated output exist
149 auto *output = QWaylandOutput::fromResource(resource: outputResource);
150 if (!output) {
151 qCWarning(qLcWaylandCompositor,
152 "The client is requesting a QWaylandXdgOutputV1 for a "
153 "QWaylandOutput that doesn't exist");
154 wl_resource_post_error(resource->handle, WL_DISPLAY_ERROR_INVALID_OBJECT, "output not found");
155 return;
156 }
157
158 // Do we have a QWaylandXdgOutputV1 for this output?
159 if (!xdgOutputs.contains(output)) {
160 qCWarning(qLcWaylandCompositor,
161 "The client is requesting a QWaylandXdgOutputV1 that the compositor "
162 "didn't create before");
163 wl_resource_post_error(resource->handle, WL_DISPLAY_ERROR_INVALID_OBJECT,
164 "compositor didn't create a QWaylandXdgOutputV1 for this zxdg_output_v1 object");
165 return;
166 }
167
168 // Bind QWaylandXdgOutputV1 and initialize
169 auto *xdgOutput = xdgOutputs[output];
170 auto *xdgOutputPrivate = QWaylandXdgOutputV1Private::get(xdgOutput: xdgOutput);
171 Q_ASSERT(xdgOutputPrivate);
172 xdgOutputPrivate->setManager(q);
173 xdgOutputPrivate->setOutput(output);
174 xdgOutputPrivate->add(resource->client(), id, qMin(resource->version(), QWaylandXdgOutputV1Private::interfaceVersion()));
175}
176
177// QWaylandXdgOutputV1
178
179QWaylandXdgOutputV1::QWaylandXdgOutputV1()
180 : QObject(*new QWaylandXdgOutputV1Private)
181{
182}
183
184QWaylandXdgOutputV1::QWaylandXdgOutputV1(QWaylandOutput *output, QWaylandXdgOutputManagerV1 *manager)
185 : QObject(*new QWaylandXdgOutputV1Private)
186{
187 Q_D(QWaylandXdgOutputV1);
188
189 // Set members before emitting changed signals so that handlers will
190 // see both already set and not nullptr, avoiding potential crashes
191 d->manager = manager;
192 d->output = output;
193
194 QWaylandXdgOutputManagerV1Private::get(manager: d->manager)->registerXdgOutput(output, xdgOutput: this);
195
196 emit managerChanged();
197 emit outputChanged();
198}
199
200QWaylandXdgOutputV1::~QWaylandXdgOutputV1()
201{
202 Q_D(QWaylandXdgOutputV1);
203
204 if (d->manager)
205 QWaylandXdgOutputManagerV1Private::get(manager: d->manager)->unregisterXdgOutput(output: d->output);
206}
207
208/*!
209 * \qmlproperty XdgOutputManagerV1 XdgOutputV1::manager
210 * \readonly
211 *
212 * This property holds the object that manages this XdgOutputV1.
213 */
214/*!
215 * \property QWaylandXdgOutputV1::manager
216 * \readonly
217 *
218 * This property holds the object that manages this QWaylandXdgOutputV1.
219 */
220QWaylandXdgOutputManagerV1 *QWaylandXdgOutputV1::manager() const
221{
222 Q_D(const QWaylandXdgOutputV1);
223 return d->manager;
224}
225
226/*!
227 * \qmlproperty WaylandOutput XdgOutputV1::output
228 * \readonly
229 *
230 * This property holds the WaylandOutput associated with this XdgOutputV1.
231 */
232/*!
233 * \property QWaylandXdgOutputV1::output
234 * \readonly
235 *
236 * This property holds the QWaylandOutput associated with this QWaylandXdgOutputV1.
237 */
238QWaylandOutput *QWaylandXdgOutputV1::output() const
239{
240 Q_D(const QWaylandXdgOutputV1);
241 return d->output;
242}
243
244/*!
245 * \qmlproperty string XdgOutputV1::name
246 *
247 * This property holds the name of this output.
248 *
249 * The naming convention is compositor defined, but limited to alphanumeric
250 * characters and dashes ("-"). Each name is unique and will also remain
251 * consistent across sessions with the same hardware and software configuration.
252 *
253 * Examples of names include "HDMI-A-1", "WL-1", "X11-1" etc...
254 * However don't assume the name reflects the underlying technology.
255 *
256 * Changing this property after initialization doesn't take effect.
257 */
258/*!
259 * \property QWaylandXdgOutputV1::name
260 *
261 * This property holds the name of this output.
262 *
263 * The naming convention is compositor defined, but limited to alphanumeric
264 * characters and dashes ("-"). Each name is unique and will also remain
265 * consistent across sessions with the same hardware and software configuration.
266 *
267 * Examples of names include "HDMI-A-1", "WL-1", "X11-1" etc...
268 * However don't assume the name reflects the underlying technology.
269 *
270 * Changing this property after initialization doesn't take effect.
271 */
272QString QWaylandXdgOutputV1::name() const
273{
274 Q_D(const QWaylandXdgOutputV1);
275 return d->name;
276}
277
278void QWaylandXdgOutputV1::setName(const QString &name)
279{
280 Q_D(QWaylandXdgOutputV1);
281
282 if (d->name == name)
283 return;
284
285 // Can't change after clients bound to xdg-output
286 if (d->initialized) {
287 qCWarning(qLcWaylandCompositor, "QWaylandXdgOutputV1::name cannot be changed after initialization");
288 return;
289 }
290
291 d->name = name;
292 emit nameChanged();
293}
294
295/*!
296 * \qmlproperty string XdgOutputV1::description
297 *
298 * This property holds the description of this output.
299 *
300 * No convention is defined for the description.
301 *
302 * Changing this property after initialization doesn't take effect.
303 */
304/*!
305 * \property QWaylandXdgOutputV1::description
306 *
307 * This property holds the description of this output.
308 *
309 * No convention is defined for the description.
310 *
311 * Changing this property after initialization doesn't take effect.
312 */
313QString QWaylandXdgOutputV1::description() const
314{
315 Q_D(const QWaylandXdgOutputV1);
316 return d->description;
317}
318
319void QWaylandXdgOutputV1::setDescription(const QString &description)
320{
321 Q_D(QWaylandXdgOutputV1);
322
323 if (d->description == description)
324 return;
325
326 // Can't change after clients bound to xdg-output
327 if (d->initialized) {
328 qCWarning(qLcWaylandCompositor, "QWaylandXdgOutputV1::description cannot be changed after initialization");
329 return;
330 }
331
332 d->description = description;
333 emit descriptionChanged();
334}
335
336/*!
337 * \qmlproperty point XdgOutputV1::logicalPosition
338 *
339 * This property holds the coordinates of the output within the global compositor space.
340 *
341 * The default value is 0,0.
342 */
343/*!
344 * \property QWaylandXdgOutputV1::logicalPosition
345 *
346 * This property holds the coordinates of the output within the global compositor space.
347 *
348 * The default value is 0,0.
349 */
350QPoint QWaylandXdgOutputV1::logicalPosition() const
351{
352 Q_D(const QWaylandXdgOutputV1);
353 return d->logicalPos;
354}
355
356void QWaylandXdgOutputV1::setLogicalPosition(const QPoint &position)
357{
358 Q_D(QWaylandXdgOutputV1);
359
360 if (d->logicalPos == position)
361 return;
362
363 d->logicalPos = position;
364 if (d->initialized) {
365 d->sendLogicalPosition(position);
366 d->sendDone();
367 }
368 emit logicalPositionChanged();
369 emit logicalGeometryChanged();
370}
371
372/*!
373 * \qmlproperty size XdgOutputV1::logicalSize
374 *
375 * This property holds the size of the output in the global compositor space.
376 *
377 * The default value is -1,-1 which is invalid.
378 *
379 * Please remember that this is the logical size, not the physical size.
380 * For example, for a WaylandOutput mode 3840x2160 and a scale factor 2:
381 * \list
382 * \li A compositor not scaling the surface buffers, will report a logical size of 3840x2160.
383 * \li A compositor automatically scaling the surface buffers, will report a logical size of 1920x1080.
384 * \li A compositor using a fractional scale of 1.5, will report a logical size of 2560x1620.
385 * \endlist
386 */
387/*!
388 * \property QWaylandXdgOutputV1::logicalSize
389 *
390 * This property holds the size of the output in the global compositor space.
391 *
392 * The default value is -1,-1 which is invalid.
393 *
394 * Please remember that this is the logical size, not the physical size.
395 * For example, for a WaylandOutput mode 3840x2160 and a scale factor 2:
396 * \list
397 * \li A compositor not scaling the surface buffers, will report a logical size of 3840x2160.
398 * \li A compositor automatically scaling the surface buffers, will report a logical size of 1920x1080.
399 * \li A compositor using a fractional scale of 1.5, will report a logical size of 2560x1620.
400 * \endlist
401 */
402QSize QWaylandXdgOutputV1::logicalSize() const
403{
404 Q_D(const QWaylandXdgOutputV1);
405 return d->logicalSize;
406}
407
408void QWaylandXdgOutputV1::setLogicalSize(const QSize &size)
409{
410 Q_D(QWaylandXdgOutputV1);
411
412 if (d->logicalSize == size)
413 return;
414
415 d->logicalSize = size;
416 if (d->initialized) {
417 d->sendLogicalSize(size);
418 d->sendDone();
419 }
420 emit logicalSizeChanged();
421 emit logicalGeometryChanged();
422}
423
424/*!
425 * \qmlproperty rect XdgOutputV1::logicalGeometry
426 * \readonly
427 *
428 * This property holds the position and size of the output in the global compositor space.
429 * It's the combination of the logical position and logical size.
430 *
431 * \sa XdgOutputV1::logicalPosition
432 * \sa XdgOutputV1::logicalSize
433 */
434/*!
435 * \property QWaylandXdgOutputV1::logicalGeometry
436 * \readonly
437 *
438 * This property holds the position and size of the output in the global compositor space.
439 * It's the combination of the logical position and logical size.
440 *
441 * \sa QWaylandXdgOutputV1::logicalPosition
442 * \sa QWaylandXdgOutputV1::logicalSize
443 */
444QRect QWaylandXdgOutputV1::logicalGeometry() const
445{
446 Q_D(const QWaylandXdgOutputV1);
447 return QRect(d->logicalPos, d->logicalSize);
448}
449
450// QWaylandXdgOutputV1Private
451
452void QWaylandXdgOutputV1Private::sendLogicalPosition(const QPoint &position)
453{
454 const auto values = resourceMap().values();
455 for (auto *resource : values)
456 send_logical_position(resource->handle, position.x(), position.y());
457 needToSendDone = true;
458}
459
460void QWaylandXdgOutputV1Private::sendLogicalSize(const QSize &size)
461{
462 const auto values = resourceMap().values();
463 for (auto *resource : values)
464 send_logical_size(resource->handle, size.width(), size.height());
465 needToSendDone = true;
466}
467
468void QWaylandXdgOutputV1Private::sendDone()
469{
470 if (needToSendDone) {
471 const auto values = resourceMap().values();
472 for (auto *resource : values) {
473 if (resource->version() < 3)
474 send_done(resource->handle);
475 }
476 needToSendDone = false;
477 }
478}
479
480void QWaylandXdgOutputV1Private::setManager(QWaylandXdgOutputManagerV1 *_manager)
481{
482 Q_Q(QWaylandXdgOutputV1);
483
484 if (!_manager) {
485 qCWarning(qLcWaylandCompositor,
486 "Cannot associate a null QWaylandXdgOutputManagerV1 to QWaylandXdgOutputV1 %p", this);
487 return;
488 }
489
490 if (manager == _manager)
491 return;
492
493 if (manager) {
494 qCWarning(qLcWaylandCompositor,
495 "Cannot associate a different QWaylandXdgOutputManagerV1 to QWaylandXdgOutputV1 %p "
496 "after initialization", this);
497 return;
498 }
499
500 manager = _manager;
501 emit q->managerChanged();
502}
503
504void QWaylandXdgOutputV1Private::setOutput(QWaylandOutput *_output)
505{
506 Q_Q(QWaylandXdgOutputV1);
507
508 if (!_output) {
509 qCWarning(qLcWaylandCompositor,
510 "Cannot associate a null QWaylandOutput to QWaylandXdgOutputV1 %p", this);
511 return;
512 }
513
514 if (output == _output)
515 return;
516
517 if (output) {
518 qCWarning(qLcWaylandCompositor,
519 "Cannot associate a different QWaylandOutput to QWaylandXdgOutputV1 %p "
520 "after initialization", this);
521 return;
522 }
523
524 // Assign output above manager, to make both values valid in handlers
525 output = _output;
526
527 if (!manager) {
528 // Try to find the manager from the output parents
529 for (auto *p = output->parent(); p != nullptr; p = p->parent()) {
530 if (auto *m = qobject_cast<QWaylandXdgOutputManagerV1 *>(object: p)) {
531 manager = m;
532 emit q->managerChanged();
533 break;
534 }
535 }
536 }
537
538 emit q->outputChanged();
539
540 // Register the output
541 if (manager)
542 QWaylandXdgOutputManagerV1Private::get(manager)->registerXdgOutput(output, xdgOutput: q);
543}
544
545void QWaylandXdgOutputV1Private::zxdg_output_v1_bind_resource(Resource *resource)
546{
547 send_logical_position(resource->handle, logicalPos.x(), logicalPos.y());
548 send_logical_size(resource->handle, logicalSize.width(), logicalSize.height());
549 if (resource->version() >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION)
550 send_name(resource->handle, name);
551 if (resource->version() >= ZXDG_OUTPUT_V1_DESCRIPTION_SINCE_VERSION)
552 send_description(resource->handle, description);
553 send_done(resource->handle);
554
555 initialized = true;
556}
557
558void QWaylandXdgOutputV1Private::zxdg_output_v1_destroy(Resource *resource)
559{
560 wl_resource_destroy(resource->handle);
561}
562
563QT_END_NAMESPACE
564
565#include "moc_qwaylandxdgoutputv1.cpp"
566

source code of qtwayland/src/compositor/extensions/qwaylandxdgoutputv1.cpp