1 /********************************************************************
2 Copyright 2014  Martin Gräßlin <mgraesslin@kde.org>
3 Copyright © 2020 Roman Gilg <subdiff@gmail.com>
4 
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) version 3, or any
9 later version accepted by the membership of KDE e.V. (or its
10 successor approved by the membership of KDE e.V.), which shall
11 act as a proxy defined in Section 6 of version 3 of the license.
12 
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 Lesser General Public License for more details.
17 
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library.  If not, see <http://www.gnu.org/licenses/>.
20 *********************************************************************/
21 #include "buffer_p.h"
22 
23 #include "client.h"
24 #include "display.h"
25 #include "logging.h"
26 #include "surface.h"
27 
28 #include "wayland/buffer_manager.h"
29 #include "wayland/display.h"
30 
31 #include "linux_dmabuf_v1.h"
32 #include "linux_dmabuf_v1_p.h"
33 
34 #include <drm_fourcc.h>
35 
36 #include <EGL/egl.h>
37 #include <QtGui/qopengl.h>
38 
39 namespace EGL
40 {
41 using eglQueryWaylandBufferWL_func
42     = GLboolean (*)(EGLDisplay dpy, struct wl_resource* buffer, EGLint attribute, EGLint* value);
43 }
44 
45 namespace Wrapland::Server
46 {
47 
48 int constexpr default_bpp{32};
49 
Private(Buffer * buffer,ShmImage::Format format)50 ShmImage::Private::Private(Buffer* buffer, ShmImage::Format format)
51     : format{format}
52     , stride{wl_shm_buffer_get_stride(buffer->d_ptr->shmBuffer)}
53     , bpp{default_bpp}
54     , data{static_cast<uchar*>(wl_shm_buffer_get_data(buffer->d_ptr->shmBuffer))}
55     , buffer{buffer}
56     , display{buffer->d_ptr->display}
57 {
58 }
59 
~Private()60 ShmImage::Private::~Private()
61 {
62     display->bufferManager()->endShmAccess();
63 }
64 
createQImage()65 QImage ShmImage::Private::createQImage()
66 {
67     if (!image.isNull()) {
68         return image;
69     }
70 
71     [[maybe_unused]] auto const hasAccess
72         = display->bufferManager()->beginShmAccess(buffer->d_ptr->shmBuffer);
73     assert(hasAccess);
74 
75     QImage::Format qtFormat{QImage::Format_Invalid};
76     switch (format) {
77     case ShmImage::Format::argb8888:
78         qtFormat = QImage::Format_ARGB32_Premultiplied;
79         break;
80     case ShmImage::Format::xrgb8888:
81         qtFormat = QImage::Format_RGB32;
82         break;
83     default:
84         assert(false);
85     }
86 
87     auto const size = buffer->size();
88     return QImage(
89         data, size.width(), size.height(), stride, qtFormat, &imageBufferCleanupHandler, display);
90 }
91 
imageBufferCleanupHandler(void * info)92 void ShmImage::Private::imageBufferCleanupHandler(void* info)
93 {
94     auto display = static_cast<Wayland::Display*>(info);
95     display->bufferManager()->endShmAccess();
96 }
97 
ShmImage(Buffer * buffer,ShmImage::Format format)98 ShmImage::ShmImage(Buffer* buffer, ShmImage::Format format)
99     : d_ptr{new Private(buffer, format)}
100 {
101 }
102 
ShmImage(ShmImage const & img)103 ShmImage::ShmImage(ShmImage const& img)
104     : d_ptr{new Private(img.d_ptr->buffer, img.d_ptr->format)}
105 {
106     d_ptr->display->bufferManager()->beginShmAccess(d_ptr->buffer->d_ptr->shmBuffer);
107 }
108 
operator =(ShmImage const & img)109 ShmImage& ShmImage::operator=(ShmImage const& img)
110 {
111     if (this != &img) {
112         d_ptr->display->bufferManager()->endShmAccess();
113         img.d_ptr->display->bufferManager()->beginShmAccess(img.d_ptr->buffer->d_ptr->shmBuffer);
114 
115         d_ptr->format = img.d_ptr->format;
116         d_ptr->stride = img.d_ptr->stride;
117         d_ptr->bpp = img.d_ptr->bpp;
118         d_ptr->data = img.d_ptr->data;
119         d_ptr->buffer = img.d_ptr->buffer;
120         d_ptr->display = img.d_ptr->display;
121     }
122 
123     return *this;
124 }
125 
ShmImage(ShmImage && img)126 ShmImage::ShmImage(ShmImage&& img) noexcept
127     : d_ptr{std::move(img.d_ptr)}
128 {
129 }
130 
operator =(ShmImage && img)131 ShmImage& ShmImage::operator=(ShmImage&& img) noexcept
132 {
133     if (this != &img) {
134         d_ptr = std::move(img.d_ptr);
135     }
136 
137     return *this;
138 }
139 
140 ShmImage::~ShmImage() = default;
141 
format() const142 ShmImage::Format ShmImage::format() const
143 {
144     return d_ptr->format;
145 }
146 
stride() const147 int32_t ShmImage::stride() const
148 {
149     return d_ptr->stride;
150 }
151 
bpp() const152 int32_t ShmImage::bpp() const
153 {
154     return d_ptr->bpp;
155 }
156 
data() const157 uchar* ShmImage::data() const
158 {
159     return d_ptr->data;
160 }
161 
createQImage()162 QImage ShmImage::createQImage()
163 {
164     return d_ptr->createQImage();
165 }
166 
getFormat(wl_shm_buffer * shmBuffer)167 ShmImage::Format getFormat(wl_shm_buffer* shmBuffer)
168 {
169     switch (wl_shm_buffer_get_format(shmBuffer)) {
170     case WL_SHM_FORMAT_ARGB8888:
171         return ShmImage::Format::argb8888;
172     case WL_SHM_FORMAT_XRGB8888:
173         return ShmImage::Format::xrgb8888;
174     default:
175         return ShmImage::Format::invalid;
176     }
177 }
178 
get(Buffer * buffer)179 std::optional<ShmImage> ShmImage::get(Buffer* buffer)
180 {
181     auto display = buffer->d_ptr->display;
182     auto shmBuffer = buffer->d_ptr->shmBuffer;
183 
184     if (!shmBuffer) {
185         return std::nullopt;
186     }
187 
188     if (!display->bufferManager()->beginShmAccess(shmBuffer)) {
189         return std::nullopt;
190     }
191 
192     auto const imageFormat = getFormat(shmBuffer);
193     if (imageFormat == ShmImage::Format::invalid) {
194         display->bufferManager()->endShmAccess();
195         return std::nullopt;
196     }
197 
198     return ShmImage(buffer, imageFormat);
199 }
200 
Private(Buffer * q,wl_resource * wlResource,Surface * surface,Wayland::Display * display)201 Buffer::Private::Private(Buffer* q,
202                          wl_resource* wlResource,
203                          Surface* surface,
204                          Wayland::Display* display)
205     : resource(wlResource)
206     , shmBuffer(wl_shm_buffer_get(wlResource))
207     , surface(surface)
208     , display(display)
209     , q_ptr{q}
210 {
211     if (!shmBuffer
212         && wl_resource_instance_of(resource, &wl_buffer_interface, BufferV1::interface())) {
213         dmabufBuffer = Wayland::Resource<LinuxDmabufBufferV1>::handle(resource);
214     }
215 
216     destroyWrapper.buffer = q;
217     destroyWrapper.listener.notify = destroyListenerCallback;
218     destroyWrapper.listener.link.prev = nullptr;
219     destroyWrapper.listener.link.next = nullptr;
220     wl_resource_add_destroy_listener(resource, &destroyWrapper.listener);
221 
222     if (shmBuffer) {
223         size = QSize(wl_shm_buffer_get_width(shmBuffer), wl_shm_buffer_get_height(shmBuffer));
224         // check alpha
225         switch (wl_shm_buffer_get_format(shmBuffer)) {
226         case WL_SHM_FORMAT_ARGB8888:
227             alpha = true;
228             break;
229         case WL_SHM_FORMAT_XRGB8888:
230         default:
231             alpha = false;
232             break;
233         }
234     } else if (dmabufBuffer) {
235         switch (dmabufBuffer->format()) {
236         case DRM_FORMAT_ARGB4444:
237         case DRM_FORMAT_ABGR4444:
238         case DRM_FORMAT_RGBA4444:
239         case DRM_FORMAT_BGRA4444:
240 
241         case DRM_FORMAT_ARGB1555:
242         case DRM_FORMAT_ABGR1555:
243         case DRM_FORMAT_RGBA5551:
244         case DRM_FORMAT_BGRA5551:
245 
246         case DRM_FORMAT_ARGB8888:
247         case DRM_FORMAT_ABGR8888:
248         case DRM_FORMAT_RGBA8888:
249         case DRM_FORMAT_BGRA8888:
250 
251         case DRM_FORMAT_ARGB2101010:
252         case DRM_FORMAT_ABGR2101010:
253         case DRM_FORMAT_RGBA1010102:
254         case DRM_FORMAT_BGRA1010102:
255 
256         case DRM_FORMAT_XRGB8888_A8:
257         case DRM_FORMAT_XBGR8888_A8:
258         case DRM_FORMAT_RGBX8888_A8:
259         case DRM_FORMAT_BGRX8888_A8:
260         case DRM_FORMAT_RGB888_A8:
261         case DRM_FORMAT_BGR888_A8:
262         case DRM_FORMAT_RGB565_A8:
263         case DRM_FORMAT_BGR565_A8:
264             alpha = true;
265             break;
266         default:
267             alpha = false;
268             break;
269         }
270         size = dmabufBuffer->size();
271     } else if (surface) {
272         EGLDisplay eglDisplay = surface->client()->display()->eglDisplay();
273 
274         using namespace EGL;
275         static eglQueryWaylandBufferWL_func eglQueryWaylandBufferWL{nullptr};
276         static bool resolved{false};
277 
278         if (!resolved && eglDisplay != EGL_NO_DISPLAY) {
279             eglQueryWaylandBufferWL = reinterpret_cast<eglQueryWaylandBufferWL_func>(
280                 eglGetProcAddress("eglQueryWaylandBufferWL"));
281             resolved = true;
282         }
283 
284         if (eglQueryWaylandBufferWL) {
285             EGLint width = 0;
286             EGLint height = 0;
287             bool valid = false;
288             valid = eglQueryWaylandBufferWL(eglDisplay, resource, EGL_WIDTH, &width);
289             valid = valid && eglQueryWaylandBufferWL(eglDisplay, resource, EGL_HEIGHT, &height);
290             if (valid) {
291                 size = QSize(width, height);
292             }
293             // check alpha
294             EGLint format = 0;
295             if (eglQueryWaylandBufferWL(eglDisplay, resource, EGL_TEXTURE_FORMAT, &format)) {
296                 switch (format) {
297                 case EGL_TEXTURE_RGBA:
298                     alpha = true;
299                     break;
300                 case EGL_TEXTURE_RGB:
301                 default:
302                     alpha = false;
303                     break;
304                 }
305             }
306         }
307     }
308 }
309 
~Private()310 Buffer::Private::~Private()
311 {
312     wl_list_remove(&destroyWrapper.listener.link);
313     display->bufferManager()->removeBuffer(q_ptr);
314 }
315 
make(wl_resource * wlResource,Surface * surface)316 std::shared_ptr<Buffer> Buffer::make(wl_resource* wlResource, Surface* surface)
317 {
318     auto backendDisplay = Wayland::Display::backendCast(surface->client()->display());
319     auto buffer = std::shared_ptr<Buffer>{new Buffer(wlResource, surface)};
320     backendDisplay->bufferManager()->addBuffer(buffer);
321     return buffer;
322 }
323 
make(wl_resource * wlResource,Display * display)324 std::shared_ptr<Buffer> Buffer::make(wl_resource* wlResource, Display* display)
325 {
326     auto backendDisplay = Wayland::Display::backendCast(display);
327     auto buffer = std::shared_ptr<Buffer>{new Buffer(wlResource, display)};
328     backendDisplay->bufferManager()->addBuffer(buffer);
329     return buffer;
330 }
331 
destroyListenerCallback(wl_listener * listener,void * data)332 void Buffer::Private::destroyListenerCallback(wl_listener* listener, [[maybe_unused]] void* data)
333 {
334     // The wl_container_of macro can not be used with auto keyword and in the macro from libwayland
335     // the alignment is increased.
336     // Relevant clang-tidy checks are:
337     // * clang-diagnostic-cast-align
338     // * cppcoreguidelines-pro-bounds-pointer-arithmetic
339     // * hicpp-use-auto
340     // * modernize-use-auto
341     // NOLINTNEXTLINE
342     DestroyWrapper* wrapper = wl_container_of(listener, wrapper, listener);
343 
344     wrapper->buffer->d_ptr->resource = nullptr;
345     Q_EMIT wrapper->buffer->resourceDestroyed();
346 }
347 
Buffer(wl_resource * wlResource,Surface * surface)348 Buffer::Buffer(wl_resource* wlResource, Surface* surface)
349     : QObject(nullptr)
350     , d_ptr(new Private(this,
351                         wlResource,
352                         surface,
353                         Wayland::Display::backendCast(surface->client()->display())))
354 {
355 }
356 
Buffer(wl_resource * wlResource,Display * display)357 Buffer::Buffer(wl_resource* wlResource, Display* display)
358     : QObject(nullptr)
359     , d_ptr(new Private(this, wlResource, nullptr, Wayland::Display::backendCast(display)))
360 {
361 }
362 
get(Display * display,wl_resource * resource)363 std::shared_ptr<Buffer> Buffer::get(Display* display, wl_resource* resource)
364 {
365     if (!resource) {
366         return nullptr;
367     }
368     // TODO(unknown author): verify resource is a buffer
369     auto buffer = Wayland::Display::backendCast(display)->bufferManager()->fromResource(resource);
370     return buffer ? buffer.value() : make(resource, display);
371 }
372 
~Buffer()373 Buffer::~Buffer()
374 {
375     if (d_ptr->committed && d_ptr->resource) {
376         wl_buffer_send_release(d_ptr->resource);
377         wl_client_flush(wl_resource_get_client(d_ptr->resource));
378     }
379 }
380 
shmImage()381 std::optional<ShmImage> Buffer::shmImage()
382 {
383     return ShmImage::get(this);
384 }
385 
surface() const386 Surface* Buffer::surface() const
387 {
388     return d_ptr->surface;
389 }
390 
shmBuffer()391 wl_shm_buffer* Buffer::shmBuffer()
392 {
393     return d_ptr->shmBuffer;
394 }
395 
linuxDmabufBuffer()396 LinuxDmabufBufferV1* Buffer::linuxDmabufBuffer()
397 {
398     return d_ptr->dmabufBuffer;
399 }
400 
resource() const401 wl_resource* Buffer::resource() const
402 {
403     return d_ptr->resource;
404 }
405 
size() const406 QSize Buffer::size() const
407 {
408     return d_ptr->size;
409 }
410 
hasAlphaChannel() const411 bool Buffer::hasAlphaChannel() const
412 {
413     return d_ptr->alpha;
414 }
415 
setCommitted()416 void Buffer::setCommitted()
417 {
418     d_ptr->committed = true;
419 }
420 
421 }
422