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

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