1 /*
2     SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
3 
4     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6 
7 #include "drmclientbuffer.h"
8 #include "clientbuffer_p.h"
9 #include "display.h"
10 
11 #include <EGL/egl.h>
12 #include <QtGui/qopengl.h>
13 
14 #ifndef EGL_WL_bind_wayland_display
15 #define EGL_WAYLAND_Y_INVERTED_WL 0x31DB
16 #endif
17 
18 namespace KWaylandServer
19 {
20 typedef GLboolean (*eglQueryWaylandBufferWL_func)(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
21 static eglQueryWaylandBufferWL_func eglQueryWaylandBufferWL = nullptr;
22 
23 class DrmClientBufferPrivate : public ClientBufferPrivate
24 {
25 public:
26     int textureFormat = 0;
27     int width = 0;
28     int height = 0;
29     int yInverted = 0;
30     bool hasAlphaChannel = false;
31 };
32 
DrmClientBuffer(wl_resource * resource,DrmClientBufferIntegration * integration)33 DrmClientBuffer::DrmClientBuffer(wl_resource *resource, DrmClientBufferIntegration *integration)
34     : ClientBuffer(resource, *new DrmClientBufferPrivate)
35 {
36     Q_D(DrmClientBuffer);
37 
38     EGLDisplay eglDisplay = integration->display()->eglDisplay();
39     if (!eglQueryWaylandBufferWL(eglDisplay, resource, EGL_TEXTURE_FORMAT, &d->textureFormat)) {
40         // The proprietary Nvidia driver doesn't support querying the EGL_TEXTURE_FORMAT.
41         // We must assume that the buffer has an alpha channel for transparency to work.
42         d->textureFormat = EGL_TEXTURE_RGBA;
43     }
44 
45     eglQueryWaylandBufferWL(eglDisplay, resource, EGL_WIDTH, &d->width);
46     eglQueryWaylandBufferWL(eglDisplay, resource, EGL_HEIGHT, &d->height);
47 
48     if (!eglQueryWaylandBufferWL(eglDisplay, resource, EGL_WAYLAND_Y_INVERTED_WL, &d->yInverted)) {
49         // If EGL_WAYLAND_Y_INVERTED_WL is unsupported, we must assume that the buffer is inverted.
50         d->yInverted = true;
51     }
52 }
53 
textureFormat() const54 int DrmClientBuffer::textureFormat() const
55 {
56     Q_D(const DrmClientBuffer);
57     return d->textureFormat;
58 }
59 
size() const60 QSize DrmClientBuffer::size() const
61 {
62     Q_D(const DrmClientBuffer);
63     return QSize(d->width, d->height);
64 }
65 
hasAlphaChannel() const66 bool DrmClientBuffer::hasAlphaChannel() const
67 {
68     Q_D(const DrmClientBuffer);
69     return d->textureFormat == EGL_TEXTURE_RGBA;
70 }
71 
origin() const72 ClientBuffer::Origin DrmClientBuffer::origin() const
73 {
74     Q_D(const DrmClientBuffer);
75     return d->yInverted ? Origin::TopLeft : Origin::BottomLeft;
76 }
77 
DrmClientBufferIntegration(Display * display)78 DrmClientBufferIntegration::DrmClientBufferIntegration(Display *display)
79     : ClientBufferIntegration(display)
80 {
81 }
82 
createBuffer(::wl_resource * resource)83 ClientBuffer *DrmClientBufferIntegration::createBuffer(::wl_resource *resource)
84 {
85     EGLDisplay eglDisplay = display()->eglDisplay();
86     static bool resolved = false;
87     if (!resolved && eglDisplay != EGL_NO_DISPLAY) {
88         eglQueryWaylandBufferWL = (eglQueryWaylandBufferWL_func)eglGetProcAddress("eglQueryWaylandBufferWL");
89         resolved = true;
90     }
91 
92     EGLint height;
93     if (eglQueryWaylandBufferWL(eglDisplay, resource, EGL_HEIGHT, &height)) {
94         return new DrmClientBuffer(resource, this);
95     }
96     return nullptr;
97 }
98 
99 } // namespace KWaylandServer
100