1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qwaylandviewporter_p.h"
5
6#include <QtWaylandCompositor/QWaylandSurface>
7#include <QtWaylandCompositor/QWaylandCompositor>
8
9#include <QtWaylandCompositor/private/qwaylandsurface_p.h>
10
11QT_BEGIN_NAMESPACE
12
13/*!
14 \class QWaylandViewporter
15 \inmodule QtWaylandCompositor
16 \since 5.13
17 \brief Provides an extension for surface resizing and cropping.
18
19 The QWaylandViewporter extension provides a way for clients to resize and crop surface
20 contents.
21
22 QWaylandViewporter corresponds to the Wayland interface, \c wp_viewporter.
23*/
24
25/*!
26 Constructs a QWaylandViewporter object.
27*/
28QWaylandViewporter::QWaylandViewporter()
29 : QWaylandCompositorExtensionTemplate<QWaylandViewporter>(*new QWaylandViewporterPrivate)
30{
31}
32
33/*!
34 * Constructs a QWaylandViewporter object for the provided \a compositor.
35 */
36QWaylandViewporter::QWaylandViewporter(QWaylandCompositor *compositor)
37 : QWaylandCompositorExtensionTemplate<QWaylandViewporter>(compositor, *new QWaylandViewporterPrivate())
38{
39}
40
41/*!
42 Initializes the extension.
43*/
44void QWaylandViewporter::initialize()
45{
46 Q_D(QWaylandViewporter);
47
48 QWaylandCompositorExtensionTemplate::initialize();
49 auto *compositor = static_cast<QWaylandCompositor *>(extensionContainer());
50 if (!compositor) {
51 qWarning() << "Failed to find QWaylandCompositor when initializing QWaylandViewporter";
52 return;
53 }
54 d->init(compositor->display(), 1);
55}
56
57/*!
58 Returns the Wayland interface for the QWaylandViewporter.
59*/
60const wl_interface *QWaylandViewporter::interface()
61{
62 return QWaylandViewporterPrivate::interface();
63}
64
65void QWaylandViewporterPrivate::wp_viewporter_destroy(Resource *resource)
66{
67 // Viewport objects are allowed ot outlive the viewporter
68 wl_resource_destroy(resource->handle);
69}
70
71void QWaylandViewporterPrivate::wp_viewporter_get_viewport(Resource *resource, uint id, wl_resource *surfaceResource)
72{
73 auto *surface = QWaylandSurface::fromResource(resource: surfaceResource);
74 if (!surface) {
75 qWarning() << "Couldn't find surface for viewporter";
76 return;
77 }
78
79 auto *surfacePrivate = QWaylandSurfacePrivate::get(surface);
80 if (surfacePrivate->viewport) {
81 wl_resource_post_error(resource->handle, WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS,
82 "viewport already exists for surface");
83 return;
84 }
85
86 surfacePrivate->viewport = new Viewport(surface, resource->client(), id);
87}
88
89QWaylandViewporterPrivate::Viewport::Viewport(QWaylandSurface *surface, wl_client *client, int id)
90 : QtWaylandServer::wp_viewport(client, id, /*version*/ 1)
91 , m_surface(surface)
92{
93 Q_ASSERT(surface);
94}
95
96QWaylandViewporterPrivate::Viewport::~Viewport()
97{
98 if (m_surface) {
99 auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface);
100 Q_ASSERT(surfacePrivate->viewport == this);
101 surfacePrivate->viewport = nullptr;
102 }
103}
104
105// This function has to be called immediately after a surface is committed, before no
106// other client events have been dispatched, or we may incorrectly error out on an
107// incomplete pending state. See comment below.
108void QWaylandViewporterPrivate::Viewport::checkCommittedState()
109{
110 auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface);
111
112 // We can't use the current state for destination/source when checking,
113 // as that has fallbacks to the buffer size so we can't distinguish
114 // between the set/unset case. We use the pending state because no other
115 // requests has modified it yet.
116 QSize destination = surfacePrivate->pending.destinationSize;
117 QRectF source = surfacePrivate->pending.sourceGeometry;
118
119 if (!destination.isValid() && source.size() != source.size().toSize()) {
120 wl_resource_post_error(resource()->handle, error_bad_size,
121 "non-integer size (%fx%f) with unset destination",
122 source.width(), source.height());
123 return;
124 }
125
126 if (m_surface->bufferSize().isValid()) {
127 QRectF max = QRectF(QPointF(), m_surface->bufferSize() / m_surface->bufferScale());
128 // We can't use QRectF.contains, because that would return false for values on the border
129 if (max.united(r: source) != max) {
130 wl_resource_post_error(resource()->handle, error_out_of_buffer,
131 "source %f,%f, %fx%f extends outside attached buffer %fx%f",
132 source.x(), source.y(), source.width(), source.height(),
133 max.width(), max.height());
134 return;
135 }
136 }
137}
138
139
140void QWaylandViewporterPrivate::Viewport::wp_viewport_destroy_resource(Resource *resource)
141{
142 Q_UNUSED(resource);
143 delete this;
144}
145
146void QWaylandViewporterPrivate::Viewport::wp_viewport_destroy(Resource *resource)
147{
148 if (m_surface) {
149 auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface);
150 surfacePrivate->pending.destinationSize = QSize();
151 surfacePrivate->pending.sourceGeometry = QRectF();
152 }
153 wl_resource_destroy(resource->handle);
154}
155
156void QWaylandViewporterPrivate::Viewport::wp_viewport_set_source(QtWaylandServer::wp_viewport::Resource *resource, wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height)
157{
158 Q_UNUSED(resource);
159
160 if (!m_surface) {
161 wl_resource_post_error(resource->handle, error_no_surface,
162 "set_source requested for destroyed surface");
163 return;
164 }
165
166 QPointF position(wl_fixed_to_double(f: x), wl_fixed_to_double(f: y));
167 QSizeF size(wl_fixed_to_double(f: width), wl_fixed_to_double(f: height));
168 QRectF sourceGeometry(position, size);
169
170 if (sourceGeometry == QRectF(-1, -1, -1, -1)) {
171 auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface);
172 surfacePrivate->pending.sourceGeometry = QRectF();
173 return;
174 }
175
176 if (position.x() < 0 || position.y() < 0) {
177 wl_resource_post_error(resource->handle, error_bad_value,
178 "negative position in set_source");
179 return;
180 }
181
182 if (!size.isValid()) {
183 wl_resource_post_error(resource->handle, error_bad_value,
184 "negative size in set_source");
185 return;
186 }
187
188 auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface);
189 surfacePrivate->pending.sourceGeometry = sourceGeometry;
190}
191
192void QWaylandViewporterPrivate::Viewport::wp_viewport_set_destination(QtWaylandServer::wp_viewport::Resource *resource, int32_t width, int32_t height)
193{
194 Q_UNUSED(resource);
195
196 if (!m_surface) {
197 wl_resource_post_error(resource->handle, error_no_surface,
198 "set_destination requested for destroyed surface");
199 return;
200 }
201
202 QSize destinationSize(width, height);
203 if (!destinationSize.isValid() && destinationSize != QSize(-1, -1)) {
204 wl_resource_post_error(resource->handle, error_bad_value,
205 "negative size in set_destination");
206 return;
207 }
208 auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface);
209 surfacePrivate->pending.destinationSize = destinationSize;
210}
211
212QT_END_NAMESPACE
213
214#include "moc_qwaylandviewporter.cpp"
215

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