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 * \nativetype 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 }
134}
135
136void QWaylandXdgOutputManagerV1Private::unregisterXdgOutput(QWaylandOutput *output)
137{
138 xdgOutputs.remove(output);
139}
140
141QWaylandXdgOutputV1 *QWaylandXdgOutputManagerV1Private::xdgOutput(QWaylandOutput *output) const
142{
143 return xdgOutputs.value(output);
144}
145
146void QWaylandXdgOutputManagerV1Private::zxdg_output_manager_v1_get_xdg_output(Resource *resource,
147 uint32_t id,
148 wl_resource *outputResource)
149{
150 Q_Q(QWaylandXdgOutputManagerV1);
151
152 // Verify if the associated output exist
153 auto *output = QWaylandOutput::fromResource(resource: outputResource);
154 if (!output) {
155 qCWarning(qLcWaylandCompositor,
156 "The client is requesting a QWaylandXdgOutputV1 for a "
157 "QWaylandOutput that doesn't exist");
158 wl_resource_post_error(resource->handle, WL_DISPLAY_ERROR_INVALID_OBJECT, "output not found");
159 return;
160 }
161
162 // Do we have a QWaylandXdgOutputV1 for this output?
163 if (!xdgOutputs.contains(output)) {
164 qCWarning(qLcWaylandCompositor,
165 "The client is requesting a QWaylandXdgOutputV1 that the compositor "
166 "didn't create before");
167 wl_resource_post_error(resource->handle, WL_DISPLAY_ERROR_INVALID_OBJECT,
168 "compositor didn't create a QWaylandXdgOutputV1 for this zxdg_output_v1 object");
169 return;
170 }
171
172 // Bind QWaylandXdgOutputV1 and initialize
173 auto *xdgOutput = xdgOutputs[output];
174 auto *xdgOutputPrivate = QWaylandXdgOutputV1Private::get(xdgOutput: xdgOutput);
175 Q_ASSERT(xdgOutputPrivate);
176 xdgOutputPrivate->setManager(q);
177 xdgOutputPrivate->setOutput(output);
178 xdgOutputPrivate->add(resource->client(), id, qMin(resource->version(), QWaylandXdgOutputV1Private::interfaceVersion()));
179}
180
181// QWaylandXdgOutputV1
182
183QWaylandXdgOutputV1::QWaylandXdgOutputV1()
184 : QObject(*new QWaylandXdgOutputV1Private)
185{
186}
187
188QWaylandXdgOutputV1::QWaylandXdgOutputV1(QWaylandOutput *output, QWaylandXdgOutputManagerV1 *manager)
189 : QObject(*new QWaylandXdgOutputV1Private)
190{
191 Q_D(QWaylandXdgOutputV1);
192
193 // Set members before emitting changed signals so that handlers will
194 // see both already set and not nullptr, avoiding potential crashes
195 d->manager = manager;
196 d->output = output;
197
198 QWaylandXdgOutputManagerV1Private::get(manager: d->manager)->registerXdgOutput(output, xdgOutput: this);
199
200 emit managerChanged();
201 emit outputChanged();
202}
203
204QWaylandXdgOutputV1::~QWaylandXdgOutputV1()
205{
206 Q_D(QWaylandXdgOutputV1);
207
208 if (d->manager)
209 QWaylandXdgOutputManagerV1Private::get(manager: d->manager)->unregisterXdgOutput(output: d->output);
210}
211
212/*!
213 * \qmlproperty XdgOutputManagerV1 XdgOutputV1::manager
214 * \readonly
215 *
216 * This property holds the object that manages this XdgOutputV1.
217 */
218/*!
219 * \property QWaylandXdgOutputV1::manager
220 * \readonly
221 *
222 * This property holds the object that manages this QWaylandXdgOutputV1.
223 */
224QWaylandXdgOutputManagerV1 *QWaylandXdgOutputV1::manager() const
225{
226 Q_D(const QWaylandXdgOutputV1);
227 return d->manager;
228}
229
230/*!
231 * \qmlproperty WaylandOutput XdgOutputV1::output
232 * \readonly
233 *
234 * This property holds the WaylandOutput associated with this XdgOutputV1.
235 */
236/*!
237 * \property QWaylandXdgOutputV1::output
238 * \readonly
239 *
240 * This property holds the QWaylandOutput associated with this QWaylandXdgOutputV1.
241 */
242QWaylandOutput *QWaylandXdgOutputV1::output() const
243{
244 Q_D(const QWaylandXdgOutputV1);
245 return d->output;
246}
247
248/*!
249 * \qmlproperty string XdgOutputV1::name
250 *
251 * This property holds the name of this output.
252 *
253 * The naming convention is compositor defined, but limited to alphanumeric
254 * characters and dashes ("-"). Each name is unique and will also remain
255 * consistent across sessions with the same hardware and software configuration.
256 *
257 * Examples of names include "HDMI-A-1", "WL-1", "X11-1" etc...
258 * However don't assume the name reflects the underlying technology.
259 *
260 * Changing this property after initialization doesn't take effect.
261 */
262/*!
263 * \property QWaylandXdgOutputV1::name
264 *
265 * This property holds the name of this output.
266 *
267 * The naming convention is compositor defined, but limited to alphanumeric
268 * characters and dashes ("-"). Each name is unique and will also remain
269 * consistent across sessions with the same hardware and software configuration.
270 *
271 * Examples of names include "HDMI-A-1", "WL-1", "X11-1" etc...
272 * However don't assume the name reflects the underlying technology.
273 *
274 * Changing this property after initialization doesn't take effect.
275 */
276QString QWaylandXdgOutputV1::name() const
277{
278 Q_D(const QWaylandXdgOutputV1);
279 return d->name;
280}
281
282void QWaylandXdgOutputV1::setName(const QString &name)
283{
284 Q_D(QWaylandXdgOutputV1);
285
286 if (d->name == name)
287 return;
288
289 // Can't change after clients bound to xdg-output
290 if (d->initialized) {
291 qCWarning(qLcWaylandCompositor, "QWaylandXdgOutputV1::name cannot be changed after initialization");
292 return;
293 }
294
295 d->name = name;
296 emit nameChanged();
297}
298
299/*!
300 * \qmlproperty string XdgOutputV1::description
301 *
302 * This property holds the description of this output.
303 *
304 * No convention is defined for the description.
305 *
306 * Changing this property after initialization doesn't take effect.
307 */
308/*!
309 * \property QWaylandXdgOutputV1::description
310 *
311 * This property holds the description of this output.
312 *
313 * No convention is defined for the description.
314 *
315 * Changing this property after initialization doesn't take effect.
316 */
317QString QWaylandXdgOutputV1::description() const
318{
319 Q_D(const QWaylandXdgOutputV1);
320 return d->description;
321}
322
323void QWaylandXdgOutputV1::setDescription(const QString &description)
324{
325 Q_D(QWaylandXdgOutputV1);
326
327 if (d->description == description)
328 return;
329
330 // Can't change after clients bound to xdg-output
331 if (d->initialized) {
332 qCWarning(qLcWaylandCompositor, "QWaylandXdgOutputV1::description cannot be changed after initialization");
333 return;
334 }
335
336 d->description = description;
337 emit descriptionChanged();
338}
339
340/*!
341 * \qmlproperty point XdgOutputV1::logicalPosition
342 *
343 * This property holds the coordinates of the output within the global compositor space.
344 *
345 * The default value is 0,0.
346 */
347/*!
348 * \property QWaylandXdgOutputV1::logicalPosition
349 *
350 * This property holds the coordinates of the output within the global compositor space.
351 *
352 * The default value is 0,0.
353 */
354QPoint QWaylandXdgOutputV1::logicalPosition() const
355{
356 Q_D(const QWaylandXdgOutputV1);
357 return d->logicalPos;
358}
359
360void QWaylandXdgOutputV1::setLogicalPosition(const QPoint &position)
361{
362 Q_D(QWaylandXdgOutputV1);
363
364 if (d->logicalPos == position)
365 return;
366
367 d->logicalPos = position;
368 if (d->initialized) {
369 d->sendLogicalPosition(position);
370 d->sendDone();
371 }
372 emit logicalPositionChanged();
373 emit logicalGeometryChanged();
374}
375
376/*!
377 * \qmlproperty size XdgOutputV1::logicalSize
378 *
379 * This property holds the size of the output in the global compositor space.
380 *
381 * The default value is -1,-1 which is invalid.
382 *
383 * Please remember that this is the logical size, not the physical size.
384 * For example, for a WaylandOutput mode 3840x2160 and a scale factor 2:
385 * \list
386 * \li A compositor not scaling the surface buffers, will report a logical size of 3840x2160.
387 * \li A compositor automatically scaling the surface buffers, will report a logical size of 1920x1080.
388 * \li A compositor using a fractional scale of 1.5, will report a logical size of 2560x1620.
389 * \endlist
390 */
391/*!
392 * \property QWaylandXdgOutputV1::logicalSize
393 *
394 * This property holds the size of the output in the global compositor space.
395 *
396 * The default value is -1,-1 which is invalid.
397 *
398 * Please remember that this is the logical size, not the physical size.
399 * For example, for a WaylandOutput mode 3840x2160 and a scale factor 2:
400 * \list
401 * \li A compositor not scaling the surface buffers, will report a logical size of 3840x2160.
402 * \li A compositor automatically scaling the surface buffers, will report a logical size of 1920x1080.
403 * \li A compositor using a fractional scale of 1.5, will report a logical size of 2560x1620.
404 * \endlist
405 */
406QSize QWaylandXdgOutputV1::logicalSize() const
407{
408 Q_D(const QWaylandXdgOutputV1);
409 return d->logicalSize;
410}
411
412void QWaylandXdgOutputV1::setLogicalSize(const QSize &size)
413{
414 Q_D(QWaylandXdgOutputV1);
415
416 if (d->logicalSize == size)
417 return;
418
419 d->logicalSize = size;
420 if (d->initialized) {
421 d->sendLogicalSize(size);
422 d->sendDone();
423 }
424 emit logicalSizeChanged();
425 emit logicalGeometryChanged();
426}
427
428/*!
429 * \qmlproperty rect XdgOutputV1::logicalGeometry
430 * \readonly
431 *
432 * This property holds the position and size of the output in the global compositor space.
433 * It's the combination of the logical position and logical size.
434 *
435 * \sa XdgOutputV1::logicalPosition
436 * \sa XdgOutputV1::logicalSize
437 */
438/*!
439 * \property QWaylandXdgOutputV1::logicalGeometry
440 * \readonly
441 *
442 * This property holds the position and size of the output in the global compositor space.
443 * It's the combination of the logical position and logical size.
444 *
445 * \sa QWaylandXdgOutputV1::logicalPosition
446 * \sa QWaylandXdgOutputV1::logicalSize
447 */
448QRect QWaylandXdgOutputV1::logicalGeometry() const
449{
450 Q_D(const QWaylandXdgOutputV1);
451 return QRect(d->logicalPos, d->logicalSize);
452}
453
454// QWaylandXdgOutputV1Private
455
456void QWaylandXdgOutputV1Private::sendLogicalPosition(const QPoint &position)
457{
458 const auto values = resourceMap().values();
459 for (auto *resource : values)
460 send_logical_position(resource->handle, position.x(), position.y());
461}
462
463void QWaylandXdgOutputV1Private::sendLogicalSize(const QSize &size)
464{
465 const auto values = resourceMap().values();
466 for (auto *resource : values)
467 send_logical_size(resource->handle, size.width(), size.height());
468}
469
470void QWaylandXdgOutputV1Private::sendDone()
471{
472 const auto values = resourceMap().values();
473 for (auto *resource : values) {
474 if (resource->version() < 3)
475 send_done(resource->handle);
476 }
477
478 QWaylandOutputPrivate::get(output)->sendDone();
479}
480
481void QWaylandXdgOutputV1Private::setManager(QWaylandXdgOutputManagerV1 *_manager)
482{
483 Q_Q(QWaylandXdgOutputV1);
484
485 if (!_manager) {
486 qCWarning(qLcWaylandCompositor,
487 "Cannot associate a null QWaylandXdgOutputManagerV1 to QWaylandXdgOutputV1 %p", this);
488 return;
489 }
490
491 if (manager == _manager)
492 return;
493
494 if (manager) {
495 qCWarning(qLcWaylandCompositor,
496 "Cannot associate a different QWaylandXdgOutputManagerV1 to QWaylandXdgOutputV1 %p "
497 "after initialization", this);
498 return;
499 }
500
501 manager = _manager;
502 emit q->managerChanged();
503}
504
505void QWaylandXdgOutputV1Private::setOutput(QWaylandOutput *_output)
506{
507 Q_Q(QWaylandXdgOutputV1);
508
509 if (!_output) {
510 qCWarning(qLcWaylandCompositor,
511 "Cannot associate a null QWaylandOutput to QWaylandXdgOutputV1 %p", this);
512 return;
513 }
514
515 if (output == _output)
516 return;
517
518 if (output) {
519 qCWarning(qLcWaylandCompositor,
520 "Cannot associate a different QWaylandOutput to QWaylandXdgOutputV1 %p "
521 "after initialization", this);
522 return;
523 }
524
525 // Assign output above manager, to make both values valid in handlers
526 output = _output;
527
528 if (!manager) {
529 // Try to find the manager from the output parents
530 for (auto *p = output->parent(); p != nullptr; p = p->parent()) {
531 if (auto *m = qobject_cast<QWaylandXdgOutputManagerV1 *>(object: p)) {
532 manager = m;
533 emit q->managerChanged();
534 break;
535 }
536 }
537 }
538
539 emit q->outputChanged();
540
541 // Register the output
542 if (manager)
543 QWaylandXdgOutputManagerV1Private::get(manager)->registerXdgOutput(output, xdgOutput: q);
544}
545
546void QWaylandXdgOutputV1Private::zxdg_output_v1_bind_resource(Resource *resource)
547{
548 send_logical_position(resource->handle, logicalPos.x(), logicalPos.y());
549 send_logical_size(resource->handle, logicalSize.width(), logicalSize.height());
550 if (resource->version() >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION)
551 send_name(resource->handle, name);
552 if (resource->version() >= ZXDG_OUTPUT_V1_DESCRIPTION_SINCE_VERSION)
553 send_description(resource->handle, description);
554 send_done(resource->handle);
555
556 initialized = true;
557}
558
559void QWaylandXdgOutputV1Private::zxdg_output_v1_destroy(Resource *resource)
560{
561 wl_resource_destroy(resource->handle);
562}
563
564QT_END_NAMESPACE
565
566#include "moc_qwaylandxdgoutputv1.cpp"
567

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