1// Copyright (C) 2017-2016 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
2// Copyright (C) 2017 Klarälvdalens Datakonsult AB (KDAB).
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4
5#include "qwaylandoutput.h"
6#include "qwaylandoutput_p.h"
7
8#include <QtWaylandCompositor/QWaylandCompositor>
9#include <QtWaylandCompositor/QWaylandView>
10
11#include <QtWaylandCompositor/private/qwaylandsurface_p.h>
12#include <QtWaylandCompositor/private/qwaylandcompositor_p.h>
13#include <QtWaylandCompositor/private/qwaylandview_p.h>
14#include <QtWaylandCompositor/private/qwaylandutils_p.h>
15
16#include <QtCore/QCoreApplication>
17#include <QtCore/QtMath>
18#include <QtGui/QWindow>
19#include <QtGui/QExposeEvent>
20#include <QtGui/QScreen>
21#include <private/qobject_p.h>
22
23QT_BEGIN_NAMESPACE
24
25static QtWaylandServer::wl_output::subpixel toWlSubpixel(const QWaylandOutput::Subpixel &value)
26{
27 switch (value) {
28 case QWaylandOutput::SubpixelUnknown:
29 return QtWaylandServer::wl_output::subpixel_unknown;
30 case QWaylandOutput::SubpixelNone:
31 return QtWaylandServer::wl_output::subpixel_none;
32 case QWaylandOutput::SubpixelHorizontalRgb:
33 return QtWaylandServer::wl_output::subpixel_horizontal_rgb;
34 case QWaylandOutput::SubpixelHorizontalBgr:
35 return QtWaylandServer::wl_output::subpixel_horizontal_bgr;
36 case QWaylandOutput::SubpixelVerticalRgb:
37 return QtWaylandServer::wl_output::subpixel_vertical_rgb;
38 case QWaylandOutput::SubpixelVerticalBgr:
39 return QtWaylandServer::wl_output::subpixel_vertical_bgr;
40 default:
41 break;
42 }
43
44 return QtWaylandServer::wl_output::subpixel_unknown;
45}
46
47static QtWaylandServer::wl_output::transform toWlTransform(const QWaylandOutput::Transform &value)
48{
49 switch (value) {
50 case QWaylandOutput::Transform90:
51 return QtWaylandServer::wl_output::transform_90;
52 case QWaylandOutput::Transform180:
53 return QtWaylandServer::wl_output::transform_180;
54 case QWaylandOutput::Transform270:
55 return QtWaylandServer::wl_output::transform_270;
56 case QWaylandOutput::TransformFlipped:
57 return QtWaylandServer::wl_output::transform_flipped;
58 case QWaylandOutput::TransformFlipped90:
59 return QtWaylandServer::wl_output::transform_flipped_90;
60 case QWaylandOutput::TransformFlipped180:
61 return QtWaylandServer::wl_output::transform_flipped_180;
62 case QWaylandOutput::TransformFlipped270:
63 return QtWaylandServer::wl_output::transform_flipped_270;
64 default:
65 break;
66 }
67
68 return QtWaylandServer::wl_output::transform_normal;
69}
70
71QWaylandOutputPrivate::QWaylandOutputPrivate()
72{
73}
74
75QWaylandOutputPrivate::~QWaylandOutputPrivate()
76{
77}
78
79void QWaylandOutputPrivate::output_bind_resource(Resource *resource)
80{
81 sendGeometry(resource);
82
83 for (const QWaylandOutputMode &mode : modes)
84 sendMode(resource, mode);
85
86 if (resource->version() >= 2) {
87 send_scale(resource->handle, scaleFactor);
88 send_done(resource->handle);
89 }
90}
91
92void QWaylandOutputPrivate::_q_handleMaybeWindowPixelSizeChanged()
93{
94 if (!window)
95 return;
96
97 const QSize pixelSize = window->size() * window->devicePixelRatio();
98
99 if (pixelSize != windowPixelSize) {
100 windowPixelSize = pixelSize;
101 handleWindowPixelSizeChanged();
102 }
103}
104
105void QWaylandOutputPrivate::_q_handleWindowDestroyed()
106{
107 Q_Q(QWaylandOutput);
108 window = nullptr;
109 emit q->windowChanged();
110 emit q->windowDestroyed();
111}
112
113void QWaylandOutputPrivate::sendGeometry(const Resource *resource)
114{
115 send_geometry(resource->handle,
116 position.x(), position.y(),
117 physicalSize.width(), physicalSize.height(),
118 toWlSubpixel(subpixel), manufacturer, model,
119 toWlTransform(transform));
120}
121
122void QWaylandOutputPrivate::sendGeometryInfo()
123{
124 for (const Resource *resource : resourceMap().values()) {
125 sendGeometry(resource);
126 if (resource->version() >= 2)
127 send_done(resource->handle);
128 }
129}
130
131void QWaylandOutputPrivate::sendMode(const Resource *resource, const QWaylandOutputMode &mode)
132{
133 quint32 flags = 0;
134 if (currentMode == modes.indexOf(mode))
135 flags |= QtWaylandServer::wl_output::mode_current;
136 if (preferredMode == modes.indexOf(mode))
137 flags |= QtWaylandServer::wl_output::mode_preferred;
138
139 send_mode(resource->handle, flags,
140 mode.size().width(), mode.size().height(),
141 mode.refreshRate());
142}
143
144void QWaylandOutputPrivate::sendModesInfo()
145{
146 for (const Resource *resource : resourceMap().values()) {
147 for (const QWaylandOutputMode &mode : modes)
148 sendMode(resource, mode);
149 if (resource->version() >= 2)
150 send_done(resource->handle);
151 }
152}
153
154void QWaylandOutputPrivate::sendDone()
155{
156 const auto values = resourceMap().values();
157 for (auto *resource : values) {
158 if (resource->version() >= 2)
159 send_done(resource->handle);
160 }
161}
162
163void QWaylandOutputPrivate::handleWindowPixelSizeChanged()
164{
165 Q_Q(QWaylandOutput);
166 Q_ASSERT(window);
167 if (sizeFollowsWindow && currentMode <= modes.size() - 1) {
168 if (currentMode >= 0) {
169 QWaylandOutputMode mode = modes.at(i: currentMode);
170 mode.setSize(windowPixelSize);
171 modes.replace(i: currentMode, t: mode);
172 emit q->geometryChanged();
173 if (!availableGeometry.isValid())
174 emit q->availableGeometryChanged();
175 sendModesInfo();
176 } else {
177 // We didn't add a mode during the initialization because the window
178 // size was invalid, let's add it now
179 int mHzRefreshRate = qFloor(v: window->screen()->refreshRate() * 1000);
180 QWaylandOutputMode mode(windowPixelSize, mHzRefreshRate);
181 if (mode.isValid()) {
182 modes.clear();
183 q->addMode(mode, preferred: true);
184 q->setCurrentMode(mode);
185 }
186 }
187 }
188}
189
190void QWaylandOutputPrivate::addView(QWaylandView *view, QWaylandSurface *surface)
191{
192 for (int i = 0; i < surfaceViews.size(); i++) {
193 if (surface == surfaceViews.at(i).surface) {
194 if (!surfaceViews.at(i).views.contains(t: view)) {
195 surfaceViews[i].views.append(t: view);
196 }
197 return;
198 }
199 }
200
201 surfaceViews.append(t: QWaylandSurfaceViewMapper(surface,view));
202}
203
204void QWaylandOutputPrivate::removeView(QWaylandView *view, QWaylandSurface *surface)
205{
206 Q_Q(QWaylandOutput);
207 for (int i = 0; i < surfaceViews.size(); i++) {
208 if (surface == surfaceViews.at(i).surface) {
209 bool removed = surfaceViews[i].views.removeOne(t: view);
210 if (surfaceViews.at(i).views.isEmpty() && removed) {
211 if (surfaceViews.at(i).has_entered)
212 q->surfaceLeave(surface);
213 surfaceViews.remove(i);
214 }
215 return;
216 }
217 }
218 qWarning(msg: "%s Could not find view %p for surface %p to remove. Possible invalid state", Q_FUNC_INFO, view, surface);
219}
220
221QWaylandOutput::QWaylandOutput()
222 : QWaylandObject(*new QWaylandOutputPrivate())
223{
224}
225
226/*!
227 \qmltype WaylandOutput
228 \nativetype QWaylandOutput
229 \inqmlmodule QtWayland.Compositor
230 \since 5.8
231 \brief Provides access to a displayable area managed by the compositor.
232
233 The WaylandOutput manages a rectangular area within bounds of the compositor's
234 geometry, to use it for displaying client content. This could, for instance, be
235 a screen managed by the WaylandCompositor.
236
237 The type corresponds to the \c wl_output interface in the Wayland protocol.
238
239 \note If the compositor has multiple Wayland outputs, the \l Qt::AA_ShareOpenGLContexts
240 attribute must be set before the \l QGuiApplication object is constructed.
241*/
242
243/*!
244 \class QWaylandOutput
245 \inmodule QtWaylandCompositor
246 \since 5.8
247 \brief The QWaylandOutput class represents a displayable area managed by the compositor.
248
249 The QWaylandOutput manages a rectangular area within bounds of the compositor's
250 geometry, to use it for displaying client content. This could, for instance, be
251 a screen managed by the WaylandCompositor.
252
253 The class corresponds to the \c wl_output interface in the Wayland protocol.
254*/
255
256/*!
257 * Constructs a QWaylandOutput in \a compositor and with the specified \a window. The
258 * \l{QWaylandCompositor::create()}{create()} function must be called on the
259 * \a compositor before constructing a QWaylandOutput for it.
260 *
261 * The QWaylandOutput object is initialized later, in reaction to an event.
262 * At this point it is added as an output for the \a compositor. If it is the
263 * first QWaylandOutput object created for this \a compositor, it becomes the
264 * \l{QWaylandCompositor::defaultOutput()}{default output}.
265 */
266QWaylandOutput::QWaylandOutput(QWaylandCompositor *compositor, QWindow *window)
267 : QWaylandObject(*new QWaylandOutputPrivate())
268{
269 Q_D(QWaylandOutput);
270 d->compositor = compositor;
271 d->window = window;
272 QWaylandCompositorPrivate::get(compositor)->addPolishObject(object: this);
273}
274
275/*!
276 * Destroys the QWaylandOutput.
277 */
278QWaylandOutput::~QWaylandOutput()
279{
280 Q_D(QWaylandOutput);
281 if (d->compositor)
282 QWaylandCompositorPrivate::get(compositor: d->compositor)->removeOutput(output: this);
283}
284
285/*!
286 * \internal
287 */
288void QWaylandOutput::initialize()
289{
290 Q_D(QWaylandOutput);
291
292 Q_ASSERT(!d->initialized);
293 Q_ASSERT(d->compositor);
294 Q_ASSERT(d->compositor->isCreated());
295
296 if (!d->window && d->sizeFollowsWindow) {
297 qWarning(msg: "Setting QWaylandOutput::sizeFollowsWindow without a window has no effect");
298 }
299
300 // Replace modes with one that follows the window size and refresh rate,
301 // but only if window size is valid
302 if (d->window && d->sizeFollowsWindow) {
303 QWaylandOutputMode mode(d->window->size() * d->window->devicePixelRatio(),
304 qFloor(v: d->window->screen()->refreshRate() * 1000));
305 if (mode.isValid()) {
306 d->modes.clear();
307 addMode(mode, preferred: true);
308 setCurrentMode(mode);
309 }
310 }
311
312 QWaylandCompositorPrivate::get(compositor: d->compositor)->addOutput(output: this);
313
314 if (d->window) {
315 QObjectPrivate::connect(sender: d->window, signal: &QWindow::widthChanged, receiverPrivate: d, slot: &QWaylandOutputPrivate::_q_handleMaybeWindowPixelSizeChanged);
316 QObjectPrivate::connect(sender: d->window, signal: &QWindow::heightChanged, receiverPrivate: d, slot: &QWaylandOutputPrivate::_q_handleMaybeWindowPixelSizeChanged);
317 QObjectPrivate::connect(sender: d->window, signal: &QWindow::screenChanged, receiverPrivate: d, slot: &QWaylandOutputPrivate::_q_handleMaybeWindowPixelSizeChanged);
318 QObjectPrivate::connect(sender: d->window, signal: &QObject::destroyed, receiverPrivate: d, slot: &QWaylandOutputPrivate::_q_handleWindowDestroyed);
319 }
320
321 d->init(d->compositor->display(), 2);
322
323 d->initialized = true;
324}
325
326/*!
327 * Returns the QWaylandOutput corresponding to \a resource.
328 */
329QWaylandOutput *QWaylandOutput::fromResource(wl_resource *resource)
330{
331 if (auto p = QtWayland::fromResource<QWaylandOutputPrivate *>(resource))
332 return p->q_func();
333 return nullptr;
334}
335
336/*!
337 * \internal
338 */
339struct ::wl_resource *QWaylandOutput::resourceForClient(QWaylandClient *client) const
340{
341 Q_D(const QWaylandOutput);
342 QWaylandOutputPrivate::Resource *r = d->resourceMap().value(client->client());
343 if (r)
344 return r->handle;
345
346 return nullptr;
347}
348
349/*!
350 * Schedules a QEvent::UpdateRequest to be delivered to the QWaylandOutput's \l{window()}{window}.
351 *
352 * \sa QWindow::requestUpdate()
353 */
354void QWaylandOutput::update()
355{
356 Q_D(QWaylandOutput);
357 if (!d->window)
358 return;
359 d->window->requestUpdate();
360}
361
362/*!
363 * \qmlproperty WaylandCompositor QtWayland.Compositor::WaylandOutput::compositor
364 *
365 * This property holds the compositor displaying content on this WaylandOutput.
366 *
367 * \note This property can be set only once, before the WaylandOutput component
368 * is completed.
369 */
370
371/*!
372 * Returns the compositor for this QWaylandOutput.
373 */
374QWaylandCompositor *QWaylandOutput::compositor() const
375{
376 return d_func()->compositor;
377}
378
379/*!
380 * \internal
381 */
382void QWaylandOutput::setCompositor(QWaylandCompositor *compositor)
383{
384 Q_D(QWaylandOutput);
385
386 if (d->compositor == compositor)
387 return;
388
389 if (d->initialized) {
390 qWarning(msg: "Setting QWaylandCompositor %p on QWaylandOutput %p is not supported after QWaylandOutput has been initialized\n", compositor, this);
391 return;
392 }
393 if (d->compositor && d->compositor != compositor) {
394 qWarning(msg: "Possible initialization error. Moving QWaylandOutput %p between compositor instances.\n", this);
395 }
396
397 d->compositor = compositor;
398
399 QWaylandCompositorPrivate::get(compositor)->addPolishObject(object: this);
400}
401
402/*!
403 * \qmlproperty string QtWayland.Compositor::WaylandOutput::manufacturer
404 *
405 * This property holds a textual description of the manufacturer of the display
406 * managed by this WaylandOutput.
407 */
408
409/*!
410 * \property QWaylandOutput::manufacturer
411 *
412 * This property holds a textual description of the manufacturer of the display
413 * managed by this QWaylandOutput.
414 */
415QString QWaylandOutput::manufacturer() const
416{
417 return d_func()->manufacturer;
418}
419
420void QWaylandOutput::setManufacturer(const QString &manufacturer)
421{
422 Q_D(QWaylandOutput);
423
424 if (d->manufacturer == manufacturer)
425 return;
426
427 d->manufacturer = manufacturer;
428 d->sendGeometryInfo();
429 Q_EMIT manufacturerChanged();
430}
431
432/*!
433 * \qmlproperty string QtWayland.Compositor::WaylandOutput::model
434 *
435 * This property holds a textual description of the model of the display
436 * managed by this WaylandOutput.
437 */
438
439/*!
440 * \property QWaylandOutput::model
441 *
442 * This property holds a textual description of the model of the display
443 * managed by this QWaylandOutput.
444 */
445QString QWaylandOutput::model() const
446{
447 return d_func()->model;
448}
449
450void QWaylandOutput::setModel(const QString &model)
451{
452 Q_D(QWaylandOutput);
453
454 if (d->model == model)
455 return;
456
457 d->model = model;
458 d->sendGeometryInfo();
459 Q_EMIT modelChanged();
460}
461
462/*!
463 * \qmlproperty point QtWayland.Compositor::WaylandOutput::position
464 *
465 * This property holds the position of this WaylandOutput in the compositor's coordinate system.
466 */
467
468/*!
469 * \property QWaylandOutput::position
470 *
471 * This property holds the position of this QWaylandOutput in the compositor's coordinate system.
472 */
473QPoint QWaylandOutput::position() const
474{
475 return d_func()->position;
476}
477
478void QWaylandOutput::setPosition(const QPoint &pt)
479{
480 Q_D(QWaylandOutput);
481 if (d->position == pt)
482 return;
483
484 d->position = pt;
485
486 d->sendGeometryInfo();
487
488 Q_EMIT positionChanged();
489 Q_EMIT geometryChanged();
490}
491
492/*!
493 * Returns the list of modes.
494 */
495QList<QWaylandOutputMode> QWaylandOutput::modes() const
496{
497 Q_D(const QWaylandOutput);
498 return d->modes.toList();
499}
500
501/*!
502 * Adds the mode \a mode to the output and mark it as preferred
503 * if \a preferred is \c true.
504 * Please note there can only be one preferred mode.
505 */
506void QWaylandOutput::addMode(const QWaylandOutputMode &mode, bool preferred)
507{
508 Q_D(QWaylandOutput);
509
510 if (!mode.isValid()) {
511 qWarning(msg: "Cannot add an invalid mode");
512 return;
513 }
514
515 if (d->modes.indexOf(t: mode) < 0)
516 d->modes.append(t: mode);
517
518 if (preferred)
519 d->preferredMode = d->modes.indexOf(t: mode);
520
521 emit modeAdded();
522}
523
524/*!
525 * Returns the output's size in pixels and refresh rate in mHz.
526 * If the current mode is not set it will return an invalid mode.
527 *
528 * \sa QWaylandOutput::modes
529 * \sa QWaylandOutputMode
530 */
531QWaylandOutputMode QWaylandOutput::currentMode() const
532{
533 Q_D(const QWaylandOutput);
534
535 if (d->currentMode >= 0 && d->currentMode <= d->modes.size() - 1)
536 return d->modes.at(i: d->currentMode);
537 return QWaylandOutputMode();
538}
539
540/*!
541 * Sets the current mode.
542 * The mode \a mode must have been previously added.
543 *
544 * \sa QWaylandOutput::modes
545 * \sa QWaylandOutputMode
546 */
547void QWaylandOutput::setCurrentMode(const QWaylandOutputMode &mode)
548{
549 Q_D(QWaylandOutput);
550
551 int index = d->modes.indexOf(t: mode);
552 if (index < 0) {
553 qWarning(msg: "Cannot set an unknown QWaylandOutput mode as current");
554 return;
555 }
556
557 d->currentMode = index;
558
559 Q_EMIT currentModeChanged();
560 Q_EMIT geometryChanged();
561 if (!d->availableGeometry.isValid())
562 emit availableGeometryChanged();
563
564 d->sendModesInfo();
565}
566
567/*!
568 * \qmlproperty rect QtWayland.Compositor::WaylandOutput::geometry
569 *
570 * This property holds the geometry of the WaylandOutput.
571 */
572
573/*!
574 * \property QWaylandOutput::geometry
575 *
576 * This property holds the geometry of the QWaylandOutput.
577 *
578 * \sa QWaylandOutput::currentMode
579 */
580QRect QWaylandOutput::geometry() const
581{
582 Q_D(const QWaylandOutput);
583 return QRect(d->position, currentMode().size());
584}
585
586/*!
587 * \qmlproperty rect QtWayland.Compositor::WaylandOutput::availableGeometry
588 *
589 * This property holds the geometry of the WaylandOutput available for displaying content.
590 * The available geometry is in output coordinates space, starts from 0,0 and it's as big
591 * as the output by default.
592 *
593 * \sa QWaylandOutput::geometry
594 */
595
596/*!
597 * \property QWaylandOutput::availableGeometry
598 *
599 * This property holds the geometry of the QWaylandOutput available for displaying content.
600 * The available geometry is in output coordinates space, starts from 0,0 and it's as big
601 * as the output by default.
602 *
603 * \sa QWaylandOutput::currentMode, QWaylandOutput::geometry
604 */
605QRect QWaylandOutput::availableGeometry() const
606{
607 Q_D(const QWaylandOutput);
608
609 if (!d->availableGeometry.isValid())
610 return QRect(QPoint(0, 0), currentMode().size());
611
612 return d->availableGeometry;
613}
614
615void QWaylandOutput::setAvailableGeometry(const QRect &availableGeometry)
616{
617 Q_D(QWaylandOutput);
618 if (d->availableGeometry == availableGeometry)
619 return;
620
621 if (availableGeometry.topLeft().x() < 0 || availableGeometry.topLeft().y() < 0)
622 qWarning(msg: "Available geometry should be a portion of the output");
623
624 d->availableGeometry = availableGeometry;
625
626 Q_EMIT availableGeometryChanged();
627}
628
629/*!
630 * \qmlproperty size QtWayland.Compositor::WaylandOutput::physicalSize
631 *
632 * This property holds the physical size of the WaylandOutput in millimeters.
633 *
634 * \sa QWaylandOutput::geometry
635 */
636
637/*!
638 * \property QWaylandOutput::physicalSize
639 *
640 * This property holds the physical size of the QWaylandOutput in millimeters.
641 *
642 * \sa QWaylandOutput::geometry, QWaylandOutput::currentMode
643 */
644QSize QWaylandOutput::physicalSize() const
645{
646 return d_func()->physicalSize;
647}
648
649void QWaylandOutput::setPhysicalSize(const QSize &size)
650{
651 Q_D(QWaylandOutput);
652 if (d->physicalSize == size)
653 return;
654
655 d->physicalSize = size;
656
657 d->sendGeometryInfo();
658
659 Q_EMIT physicalSizeChanged();
660}
661
662/*!
663 * \enum QWaylandOutput::Subpixel
664 *
665 * This enum type is used to specify the subpixel arrangement of a QWaylandOutput.
666 *
667 * \value SubpixelUnknown The subpixel arrangement is not set.
668 * \value SubpixelNone There are no subpixels.
669 * \value SubpixelHorizontalRgb The subpixels are arranged horizontally in red, green, blue order.
670 * \value SubpixelHorizontalBgr The subpixels are arranged horizontally in blue, green, red order.
671 * \value SubpixelVerticalRgb The subpixels are arranged vertically in red, green, blue order.
672 * \value SubpixelVerticalBgr The subpixels are arranged vertically in blue, green, red order.
673 *
674 * \sa QWaylandOutput::subpixel
675 */
676
677/*!
678 * \qmlproperty enum QtWayland.Compositor::WaylandOutput::subpixel
679 *
680 * This property holds the subpixel arrangement of this WaylandOutput.
681 *
682 * \list
683 * \li WaylandOutput.SubpixelUnknown The subpixel arrangement is not set.
684 * \li WaylandOutput.SubpixelNone There are no subpixels.
685 * \li WaylandOutput.SubpixelHorizontalRgb The subpixels are arranged horizontally in red, green, blue order.
686 * \li WaylandOutput.SubpixelHorizontalBgr The subpixels are arranged horizontally in blue, green, red order.
687 * \li WaylandOutput.SubpixelVerticalRgb The subpixels are arranged vertically in red, green, blue order.
688 * \li WaylandOutput.SubpixelVerticalBgr The subpixels are arranged vertically in blue, green, red order.
689 * \endlist
690 *
691 * The default is WaylandOutput.SubpixelUnknown.
692 */
693
694/*!
695 * \property QWaylandOutput::subpixel
696 *
697 * This property holds the subpixel arrangement of this QWaylandOutput. The default is
698 * QWaylandOutput::SubpixelUnknown.
699 */
700QWaylandOutput::Subpixel QWaylandOutput::subpixel() const
701{
702 return d_func()->subpixel;
703}
704
705void QWaylandOutput::setSubpixel(const Subpixel &subpixel)
706{
707 Q_D(QWaylandOutput);
708 if (d->subpixel == subpixel)
709 return;
710
711 d->subpixel = subpixel;
712
713 d->sendGeometryInfo();
714
715 Q_EMIT subpixelChanged();
716}
717
718/*! \enum QWaylandOutput::Transform
719 *
720 * This enum type is used to specify the orientation of a QWaylandOutput.
721 *
722 * \value TransformNormal The orientation is normal.
723 * \value Transform90 The orientation is rotated 90 degrees.
724 * \value Transform180 The orientation is rotated 180 degrees.
725 * \value Transform270 The orientation is rotated 270 degrees.
726 * \value TransformFlipped The orientation is mirrored.
727 * \value TransformFlipped90 The orientation is mirrored, and rotated 90 degrees.
728 * \value TransformFlipped180 The orientation is mirrored, and rotated 180 degrees.
729 * \value TransformFlipped270 The orientation is mirrored, and rotated 270 degrees.
730 *
731 * \sa QWaylandOutput::transform
732*/
733
734/*!
735 * \qmlproperty enum QtWayland.Compositor::WaylandOutput::transform
736 *
737 * This property holds the transformation that the QWaylandCompositor applies to a surface
738 * to compensate for the orientation of the QWaylandOutput.
739 *
740 * \list
741 * \li WaylandOutput.TransformNormal The orientation is normal.
742 * \li WaylandOutput.Transform90 The orientation is rotated 90 degrees.
743 * \li WaylandOutput.Transform180 The orientation is rotated 180 degrees.
744 * \li WaylandOutput.Transform270 The orientation is rotated 270 degrees.
745 * \li WaylandOutput.TransformFlipped The orientation is mirrored.
746 * \li WaylandOutput.TransformFlipped90 The orientation is mirrored, then rotated 90 degrees.
747 * \li WaylandOutput.TransformFlipped180 The orientation is mirrored, then rotated 180 degrees.
748 * \li WaylandOutput.TransformFlipped270 The orientation is mirrored, then rotated 270 degrees.
749 * \endlist
750 *
751 * The default is WaylandOutput.TransformNormal.
752 */
753
754/*!
755 * \property QWaylandOutput::transform
756 *
757 * This property holds the transformation that the QWaylandCompositor applies to a surface
758 * to compensate for the orientation of the QWaylandOutput.
759 *
760 * The default is QWaylandOutput::TransformNormal.
761 */
762QWaylandOutput::Transform QWaylandOutput::transform() const
763{
764 return d_func()->transform;
765}
766
767void QWaylandOutput::setTransform(const Transform &transform)
768{
769 Q_D(QWaylandOutput);
770 if (d->transform == transform)
771 return;
772
773 d->transform = transform;
774
775 d->sendGeometryInfo();
776
777 Q_EMIT transformChanged();
778}
779
780/*!
781 * \qmlproperty int QtWayland.Compositor::WaylandOutput::scaleFactor
782 *
783 * This property holds the factor by which the WaylandCompositor scales surface buffers
784 * before they are displayed. It is used on high density output devices where unscaled content
785 * would be too small to be practical. The client can in turn set the scale factor of its
786 * buffer to match the output if it prefers to provide high resolution content that is
787 * suitable for the output device.
788 *
789 * The default is 1 (no scaling).
790 */
791
792/*!
793 * \property QWaylandOutput::scaleFactor
794 *
795 * This property holds the factor by which the QWaylandCompositor scales surface buffers
796 * before they are displayed. This is used on high density output devices where unscaled content
797 * would be too small to be practical. The client can in turn set the scale factor of its
798 * buffer to match the output if it prefers to provide high resolution content that is
799 * suitable for the output device.
800 *
801 * The default is 1 (no scaling).
802 */
803int QWaylandOutput::scaleFactor() const
804{
805 return d_func()->scaleFactor;
806}
807
808void QWaylandOutput::setScaleFactor(int scale)
809{
810 Q_D(QWaylandOutput);
811 if (d->scaleFactor == scale)
812 return;
813
814 d->scaleFactor = scale;
815
816 const auto resMap = d->resourceMap();
817 for (QWaylandOutputPrivate::Resource *resource : resMap) {
818 if (resource->version() >= 2) {
819 d->send_scale(resource->handle, scale);
820 d->send_done(resource->handle);
821 }
822 }
823
824 Q_EMIT scaleFactorChanged();
825}
826
827/*!
828 * \qmlproperty bool QtWayland.Compositor::WaylandOutput::sizeFollowsWindow
829 *
830 * This property controls whether the size of the WaylandOutput matches the
831 * size of its window.
832 *
833 * If this property is true, all modes previously added are replaced by a
834 * mode that matches window size and screen refresh rate.
835 *
836 * The default is false.
837 */
838
839/*!
840 * \property QWaylandOutput::sizeFollowsWindow
841 *
842 * This property controls whether the size of the QWaylandOutput matches the
843 * size of its window.
844 *
845 * If this property is true, all modes previously added are replaced by a
846 * mode that matches window size and screen refresh rate.
847 *
848 * The default is false.
849 */
850bool QWaylandOutput::sizeFollowsWindow() const
851{
852 return d_func()->sizeFollowsWindow;
853}
854
855void QWaylandOutput::setSizeFollowsWindow(bool follow)
856{
857 Q_D(QWaylandOutput);
858
859 if (follow != d->sizeFollowsWindow) {
860 d->sizeFollowsWindow = follow;
861 Q_EMIT sizeFollowsWindowChanged();
862 }
863}
864
865/*!
866 * \qmlproperty Window QtWayland.Compositor::WaylandOutput::window
867 *
868 * This property holds the Window for this WaylandOutput.
869 *
870 * \note This property can be set only once, before the WaylandOutput
871 * component is completed.
872 */
873
874/*!
875 * \property QWaylandOutput::window
876 *
877 * This property holds the QWindow for this QWaylandOutput.
878 */
879QWindow *QWaylandOutput::window() const
880{
881 return d_func()->window;
882}
883
884void QWaylandOutput::setWindow(QWindow *window)
885{
886 Q_D(QWaylandOutput);
887 if (d->window == window)
888 return;
889 if (d->initialized) {
890 qWarning(msg: "Setting QWindow %p on QWaylandOutput %p is not supported after QWaylandOutput has been initialized\n", window, this);
891 return;
892 }
893 d->window = window;
894 emit windowChanged();
895}
896
897/*!
898 * Informs QWaylandOutput that a frame has started.
899 */
900void QWaylandOutput::frameStarted()
901{
902 Q_D(QWaylandOutput);
903 for (int i = 0; i < d->surfaceViews.size(); i++) {
904 QWaylandSurfaceViewMapper &surfacemapper = d->surfaceViews[i];
905 if (surfacemapper.maybePrimaryView())
906 surfacemapper.surface->frameStarted();
907 }
908}
909
910/*!
911 * Sends pending frame callbacks.
912 */
913void QWaylandOutput::sendFrameCallbacks()
914{
915 Q_D(QWaylandOutput);
916 for (int i = 0; i < d->surfaceViews.size(); i++) {
917 const QWaylandSurfaceViewMapper &surfacemapper = d->surfaceViews.at(i);
918 if (surfacemapper.surface && surfacemapper.surface->hasContent()) {
919 if (!surfacemapper.has_entered) {
920 surfaceEnter(surface: surfacemapper.surface);
921 d->surfaceViews[i].has_entered = true;
922 }
923 if (auto primaryView = surfacemapper.maybePrimaryView()) {
924 if (!QWaylandViewPrivate::get(view: primaryView)->independentFrameCallback)
925 surfacemapper.surface->sendFrameCallbacks();
926 }
927 }
928 }
929 wl_display_flush_clients(display: d->compositor->display());
930}
931
932/*!
933 * \internal
934 */
935void QWaylandOutput::surfaceEnter(QWaylandSurface *surface)
936{
937 if (!surface)
938 return;
939
940 auto clientResource = resourceForClient(client: surface->client());
941 if (clientResource)
942 QWaylandSurfacePrivate::get(surface)->send_enter(clientResource);
943}
944
945/*!
946 * \internal
947 */
948void QWaylandOutput::surfaceLeave(QWaylandSurface *surface)
949{
950 if (!surface || !surface->client())
951 return;
952
953 auto *clientResource = resourceForClient(client: surface->client());
954 if (clientResource)
955 QWaylandSurfacePrivate::get(surface)->send_leave(clientResource);
956}
957
958/*!
959 * \internal
960 */
961bool QWaylandOutput::event(QEvent *event)
962{
963 if (event->type() == QEvent::Polish)
964 initialize();
965 return QObject::event(event);
966}
967
968QT_END_NAMESPACE
969
970#include "moc_qwaylandoutput.cpp"
971

source code of qtwayland/src/compositor/compositor_api/qwaylandoutput.cpp