[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#892203: kwayland: Implement zwp_linux_dmabuf_v1



Source: kwayland
Version: 5.12.1-1
Severity: important

Dear Maintainer,

zwp_linux_dmabuf protocol support is needed to properly work with the drm
backend to display Plasma Mobile on the Librem 5 development board.

The implementation of zwp_linux_dmabuf support is being tracked by KDE in
https://phabricator.kde.org/D10747

Also see relating email thread for complete details:
https://www.mail-archive.com/plasma-devel@kde.org/msg81108.html

The proposed patches attached to D10747 (and this bug report) do indeed
fix the display issue.

Please consider carrying the kwin patch ahead of the Plasma 5.13 release
(scheduled for June 2018).

-- System Information:
Debian Release: buster/sid
  APT prefers testing
  APT policy: (500, 'testing')
Architecture: armhf (armv7l)

Kernel: Linux 4.15.0-g837bff1 (SMP w/4 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=locale: Cannot
set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
ANSI_X3.4-1968), LANGUAGE=en_US.UTF-8 (charmap=locale: Cannot set
LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
ANSI_X3.4-1968)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)
diff --git a/src/client/protocols/linux-dmabuf-unstable-v1.xml b/src/client/protocols/linux-dmabuf-unstable-v1.xml
new file mode 100644
--- /dev/null
+++ b/src/client/protocols/linux-dmabuf-unstable-v1.xml
@@ -0,0 +1,348 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="linux_dmabuf_unstable_v1">
+
+  <copyright>
+    Copyright © 2014, 2015 Collabora, Ltd.
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the "Software"),
+    to deal in the Software without restriction, including without limitation
+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice (including the next
+    paragraph) shall be included in all copies or substantial portions of the
+    Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
+  </copyright>
+
+  <interface name="zwp_linux_dmabuf_v1" version="3">
+    <description summary="factory for creating dmabuf-based wl_buffers">
+      Following the interfaces from:
+      https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_image_dma_buf_import.txt
+      and the Linux DRM sub-system's AddFb2 ioctl.
+
+      This interface offers ways to create generic dmabuf-based
+      wl_buffers. Immediately after a client binds to this interface,
+      the set of supported formats and format modifiers is sent with
+      'format' and 'modifier' events.
+
+      The following are required from clients:
+
+      - Clients must ensure that either all data in the dma-buf is
+        coherent for all subsequent read access or that coherency is
+        correctly handled by the underlying kernel-side dma-buf
+        implementation.
+
+      - Don't make any more attachments after sending the buffer to the
+        compositor. Making more attachments later increases the risk of
+        the compositor not being able to use (re-import) an existing
+        dmabuf-based wl_buffer.
+
+      The underlying graphics stack must ensure the following:
+
+      - The dmabuf file descriptors relayed to the server will stay valid
+        for the whole lifetime of the wl_buffer. This means the server may
+        at any time use those fds to import the dmabuf into any kernel
+        sub-system that might accept it.
+
+      To create a wl_buffer from one or more dmabufs, a client creates a
+      zwp_linux_dmabuf_params_v1 object with a zwp_linux_dmabuf_v1.create_params
+      request. All planes required by the intended format are added with
+      the 'add' request. Finally, a 'create' or 'create_immed' request is
+      issued, which has the following outcome depending on the import success.
+
+      The 'create' request,
+      - on success, triggers a 'created' event which provides the final
+        wl_buffer to the client.
+      - on failure, triggers a 'failed' event to convey that the server
+        cannot use the dmabufs received from the client.
+
+      For the 'create_immed' request,
+      - on success, the server immediately imports the added dmabufs to
+        create a wl_buffer. No event is sent from the server in this case.
+      - on failure, the server can choose to either:
+        - terminate the client by raising a fatal error.
+        - mark the wl_buffer as failed, and send a 'failed' event to the
+          client. If the client uses a failed wl_buffer as an argument to any
+          request, the behaviour is compositor implementation-defined.
+
+      Warning! The protocol described in this file is experimental and
+      backward incompatible changes may be made. Backward compatible changes
+      may be added together with the corresponding interface version bump.
+      Backward incompatible changes are done by bumping the version number in
+      the protocol and interface names and resetting the interface version.
+      Once the protocol is to be declared stable, the 'z' prefix and the
+      version number in the protocol and interface names are removed and the
+      interface version number is reset.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="unbind the factory">
+        Objects created through this interface, especially wl_buffers, will
+        remain valid.
+      </description>
+    </request>
+
+    <request name="create_params">
+      <description summary="create a temporary object for buffer parameters">
+        This temporary object is used to collect multiple dmabuf handles into
+        a single batch to create a wl_buffer. It can only be used once and
+        should be destroyed after a 'created' or 'failed' event has been
+        received.
+      </description>
+      <arg name="params_id" type="new_id" interface="zwp_linux_buffer_params_v1"
+           summary="the new temporary"/>
+    </request>
+
+    <event name="format">
+      <description summary="supported buffer format">
+        This event advertises one buffer format that the server supports.
+        All the supported formats are advertised once when the client
+        binds to this interface. A roundtrip after binding guarantees
+        that the client has received all supported formats.
+
+        For the definition of the format codes, see the
+        zwp_linux_buffer_params_v1::create request.
+
+        Warning: the 'format' event is likely to be deprecated and replaced
+        with the 'modifier' event introduced in zwp_linux_dmabuf_v1
+        version 3, described below. Please refrain from using the information
+        received from this event.
+      </description>
+      <arg name="format" type="uint" summary="DRM_FORMAT code"/>
+    </event>
+
+    <event name="modifier" since="3">
+      <description summary="supported buffer format modifier">
+        This event advertises the formats that the server supports, along with
+        the modifiers supported for each format. All the supported modifiers
+        for all the supported formats are advertised once when the client
+        binds to this interface. A roundtrip after binding guarantees that
+        the client has received all supported format-modifier pairs.
+
+        For the definition of the format and modifier codes, see the
+        zwp_linux_buffer_params_v1::create request.
+      </description>
+      <arg name="format" type="uint" summary="DRM_FORMAT code"/>
+      <arg name="modifier_hi" type="uint"
+           summary="high 32 bits of layout modifier"/>
+      <arg name="modifier_lo" type="uint"
+           summary="low 32 bits of layout modifier"/>
+    </event>
+  </interface>
+
+  <interface name="zwp_linux_buffer_params_v1" version="3">
+    <description summary="parameters for creating a dmabuf-based wl_buffer">
+      This temporary object is a collection of dmabufs and other
+      parameters that together form a single logical buffer. The temporary
+      object may eventually create one wl_buffer unless cancelled by
+      destroying it before requesting 'create'.
+
+      Single-planar formats only require one dmabuf, however
+      multi-planar formats may require more than one dmabuf. For all
+      formats, an 'add' request must be called once per plane (even if the
+      underlying dmabuf fd is identical).
+
+      You must use consecutive plane indices ('plane_idx' argument for 'add')
+      from zero to the number of planes used by the drm_fourcc format code.
+      All planes required by the format must be given exactly once, but can
+      be given in any order. Each plane index can be set only once.
+    </description>
+
+    <enum name="error">
+      <entry name="already_used" value="0"
+             summary="the dmabuf_batch object has already been used to create a wl_buffer"/>
+      <entry name="plane_idx" value="1"
+             summary="plane index out of bounds"/>
+      <entry name="plane_set" value="2"
+             summary="the plane index was already set"/>
+      <entry name="incomplete" value="3"
+             summary="missing or too many planes to create a buffer"/>
+      <entry name="invalid_format" value="4"
+             summary="format not supported"/>
+      <entry name="invalid_dimensions" value="5"
+             summary="invalid width or height"/>
+      <entry name="out_of_bounds" value="6"
+             summary="offset + stride * height goes out of dmabuf bounds"/>
+      <entry name="invalid_wl_buffer" value="7"
+             summary="invalid wl_buffer resulted from importing dmabufs via
+               the create_immed request on given buffer_params"/>
+    </enum>
+
+    <request name="destroy" type="destructor">
+      <description summary="delete this object, used or not">
+        Cleans up the temporary data sent to the server for dmabuf-based
+        wl_buffer creation.
+      </description>
+    </request>
+
+    <request name="add">
+      <description summary="add a dmabuf to the temporary set">
+        This request adds one dmabuf to the set in this
+        zwp_linux_buffer_params_v1.
+
+        The 64-bit unsigned value combined from modifier_hi and modifier_lo
+        is the dmabuf layout modifier. DRM AddFB2 ioctl calls this the
+        fb modifier, which is defined in drm_mode.h of Linux UAPI.
+        This is an opaque token. Drivers use this token to express tiling,
+        compression, etc. driver-specific modifications to the base format
+        defined by the DRM fourcc code.
+
+        This request raises the PLANE_IDX error if plane_idx is too large.
+        The error PLANE_SET is raised if attempting to set a plane that
+        was already set.
+      </description>
+      <arg name="fd" type="fd" summary="dmabuf fd"/>
+      <arg name="plane_idx" type="uint" summary="plane index"/>
+      <arg name="offset" type="uint" summary="offset in bytes"/>
+      <arg name="stride" type="uint" summary="stride in bytes"/>
+      <arg name="modifier_hi" type="uint"
+           summary="high 32 bits of layout modifier"/>
+      <arg name="modifier_lo" type="uint"
+           summary="low 32 bits of layout modifier"/>
+    </request>
+
+    <enum name="flags">
+      <entry name="y_invert" value="1" summary="contents are y-inverted"/>
+      <entry name="interlaced" value="2" summary="content is interlaced"/>
+      <entry name="bottom_first" value="4" summary="bottom field first"/>
+    </enum>
+
+    <request name="create">
+      <description summary="create a wl_buffer from the given dmabufs">
+        This asks for creation of a wl_buffer from the added dmabuf
+        buffers. The wl_buffer is not created immediately but returned via
+        the 'created' event if the dmabuf sharing succeeds. The sharing
+        may fail at runtime for reasons a client cannot predict, in
+        which case the 'failed' event is triggered.
+
+        The 'format' argument is a DRM_FORMAT code, as defined by the
+        libdrm's drm_fourcc.h. The Linux kernel's DRM sub-system is the
+        authoritative source on how the format codes should work.
+
+        The 'flags' is a bitfield of the flags defined in enum "flags".
+        'y_invert' means the that the image needs to be y-flipped.
+
+        Flag 'interlaced' means that the frame in the buffer is not
+        progressive as usual, but interlaced. An interlaced buffer as
+        supported here must always contain both top and bottom fields.
+        The top field always begins on the first pixel row. The temporal
+        ordering between the two fields is top field first, unless
+        'bottom_first' is specified. It is undefined whether 'bottom_first'
+        is ignored if 'interlaced' is not set.
+
+        This protocol does not convey any information about field rate,
+        duration, or timing, other than the relative ordering between the
+        two fields in one buffer. A compositor may have to estimate the
+        intended field rate from the incoming buffer rate. It is undefined
+        whether the time of receiving wl_surface.commit with a new buffer
+        attached, applying the wl_surface state, wl_surface.frame callback
+        trigger, presentation, or any other point in the compositor cycle
+        is used to measure the frame or field times. There is no support
+        for detecting missed or late frames/fields/buffers either, and
+        there is no support whatsoever for cooperating with interlaced
+        compositor output.
+
+        The composited image quality resulting from the use of interlaced
+        buffers is explicitly undefined. A compositor may use elaborate
+        hardware features or software to deinterlace and create progressive
+        output frames from a sequence of interlaced input buffers, or it
+        may produce substandard image quality. However, compositors that
+        cannot guarantee reasonable image quality in all cases are recommended
+        to just reject all interlaced buffers.
+
+        Any argument errors, including non-positive width or height,
+        mismatch between the number of planes and the format, bad
+        format, bad offset or stride, may be indicated by fatal protocol
+        errors: INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS,
+        OUT_OF_BOUNDS.
+
+        Dmabuf import errors in the server that are not obvious client
+        bugs are returned via the 'failed' event as non-fatal. This
+        allows attempting dmabuf sharing and falling back in the client
+        if it fails.
+
+        This request can be sent only once in the object's lifetime, after
+        which the only legal request is destroy. This object should be
+        destroyed after issuing a 'create' request. Attempting to use this
+        object after issuing 'create' raises ALREADY_USED protocol error.
+
+        It is not mandatory to issue 'create'. If a client wants to
+        cancel the buffer creation, it can just destroy this object.
+      </description>
+      <arg name="width" type="int" summary="base plane width in pixels"/>
+      <arg name="height" type="int" summary="base plane height in pixels"/>
+      <arg name="format" type="uint" summary="DRM_FORMAT code"/>
+      <arg name="flags" type="uint" summary="see enum flags"/>
+    </request>
+
+    <event name="created">
+      <description summary="buffer creation succeeded">
+        This event indicates that the attempted buffer creation was
+        successful. It provides the new wl_buffer referencing the dmabuf(s).
+
+        Upon receiving this event, the client should destroy the
+        zlinux_dmabuf_params object.
+      </description>
+      <arg name="buffer" type="new_id" interface="wl_buffer"
+           summary="the newly created wl_buffer"/>
+    </event>
+
+    <event name="failed">
+      <description summary="buffer creation failed">
+        This event indicates that the attempted buffer creation has
+        failed. It usually means that one of the dmabuf constraints
+        has not been fulfilled.
+
+        Upon receiving this event, the client should destroy the
+        zlinux_buffer_params object.
+      </description>
+    </event>
+
+    <request name="create_immed" since="2">
+      <description summary="immediately create a wl_buffer from the given
+                     dmabufs">
+        This asks for immediate creation of a wl_buffer by importing the
+        added dmabufs.
+
+        In case of import success, no event is sent from the server, and the
+        wl_buffer is ready to be used by the client.
+
+        Upon import failure, either of the following may happen, as seen fit
+        by the implementation:
+        - the client is terminated with one of the following fatal protocol
+          errors:
+          - INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, OUT_OF_BOUNDS,
+            in case of argument errors such as mismatch between the number
+            of planes and the format, bad format, non-positive width or
+            height, or bad offset or stride.
+          - INVALID_WL_BUFFER, in case the cause for failure is unknown or
+            plaform specific.
+        - the server creates an invalid wl_buffer, marks it as failed and
+          sends a 'failed' event to the client. The result of using this
+          invalid wl_buffer as an argument in any request by the client is
+          defined by the compositor implementation.
+
+        This takes the same arguments as a 'create' request, and obeys the
+        same restrictions.
+      </description>
+      <arg name="buffer_id" type="new_id" interface="wl_buffer"
+           summary="id for the newly created wl_buffer"/>
+      <arg name="width" type="int" summary="base plane width in pixels"/>
+      <arg name="height" type="int" summary="base plane height in pixels"/>
+      <arg name="format" type="uint" summary="DRM_FORMAT code"/>
+      <arg name="flags" type="uint" summary="see enum flags"/>
+    </request>
+
+  </interface>
+
+</protocol>
diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt
--- a/src/server/CMakeLists.txt
+++ b/src/server/CMakeLists.txt
@@ -53,6 +53,7 @@
     xdgforeign_v2_interface.cpp
     xdgforeign_interface.cpp
     xdgshell_v6_interface.cpp
+    linuxdmabuf_v1_interface.cpp
 )
 
 ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
@@ -174,6 +175,11 @@
     BASENAME server_decoration_palette
 )
 
+ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
+    PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/linux-dmabuf-unstable-v1.xml
+    BASENAME linux-dmabuf-unstable-v1
+)
+
 set(SERVER_GENERATED_SRCS
     ${CMAKE_CURRENT_BINARY_DIR}/wayland-output-management-client-protocol.h
     ${CMAKE_CURRENT_BINARY_DIR}/wayland-output-management-server-protocol.h
@@ -221,6 +227,7 @@
     ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-foreign-unstable-v2-server-protocol.h
     ${CMAKE_CURRENT_BINARY_DIR}/wayland-idle-inhibit-unstable-v1-client-protocol.h
     ${CMAKE_CURRENT_BINARY_DIR}/wayland-idle-inhibit-unstable-v1-server-protocol.h
+    ${CMAKE_CURRENT_BINARY_DIR}/wayland-linux-dmabuf-unstable-v1-server-protocol.h
 )
 
 set_source_files_properties(${SERVER_GENERATED_SRCS} PROPERTIES SKIP_AUTOMOC ON)
@@ -299,6 +306,7 @@
   touch_interface.h
   xdgshell_interface.h
   xdgforeign_interface.h
+  linuxdmabuf_v1_interface.h
 )
 
 install(FILES
diff --git a/src/server/buffer_interface.h b/src/server/buffer_interface.h
--- a/src/server/buffer_interface.h
+++ b/src/server/buffer_interface.h
@@ -34,6 +34,10 @@
 {
 class SurfaceInterface;
 
+namespace LinuxDmabuf
+{
+    class Buffer;
+}
 
 /**
  * @brief Reference counted representation of a Wayland buffer on Server side.
@@ -104,6 +108,10 @@
      * @returns The native wl_shm_buffer if the BufferInterface represents a shared memory buffer, otherwise @c nullptr.
      **/
     wl_shm_buffer *shmBuffer();
+    /**
+     * Returns a pointer to the LinuxDmabuf::Buffer when the buffer is a dmabuf buffer, and nullptr otherwise.
+     */
+    LinuxDmabuf::Buffer *linuxDmabufBuffer();
     /**
      * @returns the native wl_resource wrapped by this BufferInterface.
      **/
diff --git a/src/server/buffer_interface.cpp b/src/server/buffer_interface.cpp
--- a/src/server/buffer_interface.cpp
+++ b/src/server/buffer_interface.cpp
@@ -21,12 +21,15 @@
 #include "display.h"
 #include "logging_p.h"
 #include "surface_interface.h"
+#include "linuxdmabuf_v1_interface.h"
 // Wayland
 #include <wayland-server.h>
 // EGL
 #include <EGL/egl.h>
 #include <QtGui/qopengl.h>
 
+#include "drm_fourcc.h"
+
 namespace KWayland
 {
 namespace Server
@@ -47,6 +50,7 @@
     QImage createImage();
     wl_resource *buffer;
     wl_shm_buffer *shmBuffer;
+    LinuxDmabuf::Buffer *dmabufBuffer;
     SurfaceInterface *surface;
     int refCount;
     QSize size;
@@ -103,11 +107,15 @@
 BufferInterface::Private::Private(BufferInterface *q, wl_resource *resource, SurfaceInterface *parent)
     : buffer(resource)
     , shmBuffer(wl_shm_buffer_get(resource))
+    , dmabufBuffer(nullptr)
     , surface(parent)
     , refCount(0)
     , alpha(false)
     , q(q)
 {
+    if (!shmBuffer && wl_resource_instance_of(resource, &wl_buffer_interface, LinuxDmabufUnstableV1Interface::bufferImplementation())) {
+        dmabufBuffer = static_cast<LinuxDmabuf::Buffer *>(wl_resource_get_user_data(resource));
+    }
     s_buffers << this;
     listener.notify = destroyListenerCallback;
     listener.link.prev = nullptr;
@@ -125,6 +133,43 @@
             alpha = false;
             break;
         }
+    } else if (dmabufBuffer) {
+        switch (dmabufBuffer->format()) {
+        case DRM_FORMAT_ARGB4444:
+        case DRM_FORMAT_ABGR4444:
+        case DRM_FORMAT_RGBA4444:
+        case DRM_FORMAT_BGRA4444:
+
+        case DRM_FORMAT_ARGB1555:
+        case DRM_FORMAT_ABGR1555:
+        case DRM_FORMAT_RGBA5551:
+        case DRM_FORMAT_BGRA5551:
+
+        case DRM_FORMAT_ARGB8888:
+        case DRM_FORMAT_ABGR8888:
+        case DRM_FORMAT_RGBA8888:
+        case DRM_FORMAT_BGRA8888:
+
+        case DRM_FORMAT_ARGB2101010:
+        case DRM_FORMAT_ABGR2101010:
+        case DRM_FORMAT_RGBA1010102:
+        case DRM_FORMAT_BGRA1010102:
+
+        case DRM_FORMAT_XRGB8888_A8:
+        case DRM_FORMAT_XBGR8888_A8:
+        case DRM_FORMAT_RGBX8888_A8:
+        case DRM_FORMAT_BGRX8888_A8:
+        case DRM_FORMAT_RGB888_A8:
+        case DRM_FORMAT_BGR888_A8:
+        case DRM_FORMAT_RGB565_A8:
+        case DRM_FORMAT_BGR565_A8:
+            alpha = true;
+            break;
+        default:
+            alpha = false;
+            break;
+        }
+        size = dmabufBuffer->size();
     } else if (parent) {
         EGLDisplay eglDisplay = parent->global()->display()->eglDisplay();
         static bool resolved = false;
@@ -275,6 +320,11 @@
     return d->shmBuffer;
 }
 
+LinuxDmabuf::Buffer *BufferInterface::linuxDmabufBuffer()
+{
+    return d->dmabufBuffer;
+}
+
 wl_resource *BufferInterface::resource() const
 {
     return d->buffer;
diff --git a/src/server/display.h b/src/server/display.h
--- a/src/server/display.h
+++ b/src/server/display.h
@@ -85,6 +85,7 @@
 class XdgForeignInterface;
 class AppMenuManagerInterface;
 class ServerSideDecorationPaletteManagerInterface;
+class LinuxDmabufUnstableV1Interface;
 
 /**
  * @brief Class holding the Wayland server display loop.
@@ -256,6 +257,12 @@
      **/
     ServerSideDecorationPaletteManagerInterface *createServerSideDecorationPaletteManager(QObject *parent = nullptr);
 
+    /**
+     * Creates the LinuxDmabufUnstableV1Interface in interface @p version.
+     *
+     * @returns A pointer to the created interface
+     **/
+    LinuxDmabufUnstableV1Interface *createLinuxDmabufInterface(QObject *parent = nullptr);
 
     /**
      * Gets the ClientConnection for the given @p client.
diff --git a/src/server/display.cpp b/src/server/display.cpp
--- a/src/server/display.cpp
+++ b/src/server/display.cpp
@@ -49,6 +49,7 @@
 #include "xdgshell_v6_interface_p.h"
 #include "appmenu_interface.h"
 #include "server_decoration_palette_interface.h"
+#include "linuxdmabuf_v1_interface.h"
 
 #include <QCoreApplication>
 #include <QDebug>
@@ -449,6 +450,13 @@
     return b;
 }
 
+LinuxDmabufUnstableV1Interface *Display::createLinuxDmabufInterface(QObject *parent)
+{
+    auto b = new LinuxDmabufUnstableV1Interface(this, parent);
+    connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; });
+    return b;
+}
+
 void Display::createShm()
 {
     Q_ASSERT(d->display);
diff --git a/src/server/drm_fourcc.h b/src/server/drm_fourcc.h
new file mode 100644
--- /dev/null
+++ b/src/server/drm_fourcc.h
@@ -0,0 +1,414 @@
+/*
+ * Copyright 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef DRM_FOURCC_H
+#define DRM_FOURCC_H
+
+//#include "drm.h"
+
+// These typedefs are copied from drm.h
+typedef uint32_t __u32;
+typedef uint64_t __u64;
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define fourcc_code(a, b, c, d) ((__u32)(a) | ((__u32)(b) << 8) | \
+				 ((__u32)(c) << 16) | ((__u32)(d) << 24))
+
+#define DRM_FORMAT_BIG_ENDIAN (1<<31) /* format is big endian instead of little endian */
+
+/* color index */
+#define DRM_FORMAT_C8		fourcc_code('C', '8', ' ', ' ') /* [7:0] C */
+
+/* 8 bpp Red */
+#define DRM_FORMAT_R8		fourcc_code('R', '8', ' ', ' ') /* [7:0] R */
+
+/* 16 bpp Red */
+#define DRM_FORMAT_R16		fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */
+
+/* 16 bpp RG */
+#define DRM_FORMAT_RG88		fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */
+#define DRM_FORMAT_GR88		fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */
+
+/* 32 bpp RG */
+#define DRM_FORMAT_RG1616	fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */
+#define DRM_FORMAT_GR1616	fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */
+
+/* 8 bpp RGB */
+#define DRM_FORMAT_RGB332	fourcc_code('R', 'G', 'B', '8') /* [7:0] R:G:B 3:3:2 */
+#define DRM_FORMAT_BGR233	fourcc_code('B', 'G', 'R', '8') /* [7:0] B:G:R 2:3:3 */
+
+/* 16 bpp RGB */
+#define DRM_FORMAT_XRGB4444	fourcc_code('X', 'R', '1', '2') /* [15:0] x:R:G:B 4:4:4:4 little endian */
+#define DRM_FORMAT_XBGR4444	fourcc_code('X', 'B', '1', '2') /* [15:0] x:B:G:R 4:4:4:4 little endian */
+#define DRM_FORMAT_RGBX4444	fourcc_code('R', 'X', '1', '2') /* [15:0] R:G:B:x 4:4:4:4 little endian */
+#define DRM_FORMAT_BGRX4444	fourcc_code('B', 'X', '1', '2') /* [15:0] B:G:R:x 4:4:4:4 little endian */
+
+#define DRM_FORMAT_ARGB4444	fourcc_code('A', 'R', '1', '2') /* [15:0] A:R:G:B 4:4:4:4 little endian */
+#define DRM_FORMAT_ABGR4444	fourcc_code('A', 'B', '1', '2') /* [15:0] A:B:G:R 4:4:4:4 little endian */
+#define DRM_FORMAT_RGBA4444	fourcc_code('R', 'A', '1', '2') /* [15:0] R:G:B:A 4:4:4:4 little endian */
+#define DRM_FORMAT_BGRA4444	fourcc_code('B', 'A', '1', '2') /* [15:0] B:G:R:A 4:4:4:4 little endian */
+
+#define DRM_FORMAT_XRGB1555	fourcc_code('X', 'R', '1', '5') /* [15:0] x:R:G:B 1:5:5:5 little endian */
+#define DRM_FORMAT_XBGR1555	fourcc_code('X', 'B', '1', '5') /* [15:0] x:B:G:R 1:5:5:5 little endian */
+#define DRM_FORMAT_RGBX5551	fourcc_code('R', 'X', '1', '5') /* [15:0] R:G:B:x 5:5:5:1 little endian */
+#define DRM_FORMAT_BGRX5551	fourcc_code('B', 'X', '1', '5') /* [15:0] B:G:R:x 5:5:5:1 little endian */
+
+#define DRM_FORMAT_ARGB1555	fourcc_code('A', 'R', '1', '5') /* [15:0] A:R:G:B 1:5:5:5 little endian */
+#define DRM_FORMAT_ABGR1555	fourcc_code('A', 'B', '1', '5') /* [15:0] A:B:G:R 1:5:5:5 little endian */
+#define DRM_FORMAT_RGBA5551	fourcc_code('R', 'A', '1', '5') /* [15:0] R:G:B:A 5:5:5:1 little endian */
+#define DRM_FORMAT_BGRA5551	fourcc_code('B', 'A', '1', '5') /* [15:0] B:G:R:A 5:5:5:1 little endian */
+
+#define DRM_FORMAT_RGB565	fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */
+#define DRM_FORMAT_BGR565	fourcc_code('B', 'G', '1', '6') /* [15:0] B:G:R 5:6:5 little endian */
+
+/* 24 bpp RGB */
+#define DRM_FORMAT_RGB888	fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */
+#define DRM_FORMAT_BGR888	fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */
+
+/* 32 bpp RGB */
+#define DRM_FORMAT_XRGB8888	fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */
+#define DRM_FORMAT_XBGR8888	fourcc_code('X', 'B', '2', '4') /* [31:0] x:B:G:R 8:8:8:8 little endian */
+#define DRM_FORMAT_RGBX8888	fourcc_code('R', 'X', '2', '4') /* [31:0] R:G:B:x 8:8:8:8 little endian */
+#define DRM_FORMAT_BGRX8888	fourcc_code('B', 'X', '2', '4') /* [31:0] B:G:R:x 8:8:8:8 little endian */
+
+#define DRM_FORMAT_ARGB8888	fourcc_code('A', 'R', '2', '4') /* [31:0] A:R:G:B 8:8:8:8 little endian */
+#define DRM_FORMAT_ABGR8888	fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */
+#define DRM_FORMAT_RGBA8888	fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */
+#define DRM_FORMAT_BGRA8888	fourcc_code('B', 'A', '2', '4') /* [31:0] B:G:R:A 8:8:8:8 little endian */
+
+#define DRM_FORMAT_XRGB2101010	fourcc_code('X', 'R', '3', '0') /* [31:0] x:R:G:B 2:10:10:10 little endian */
+#define DRM_FORMAT_XBGR2101010	fourcc_code('X', 'B', '3', '0') /* [31:0] x:B:G:R 2:10:10:10 little endian */
+#define DRM_FORMAT_RGBX1010102	fourcc_code('R', 'X', '3', '0') /* [31:0] R:G:B:x 10:10:10:2 little endian */
+#define DRM_FORMAT_BGRX1010102	fourcc_code('B', 'X', '3', '0') /* [31:0] B:G:R:x 10:10:10:2 little endian */
+
+#define DRM_FORMAT_ARGB2101010	fourcc_code('A', 'R', '3', '0') /* [31:0] A:R:G:B 2:10:10:10 little endian */
+#define DRM_FORMAT_ABGR2101010	fourcc_code('A', 'B', '3', '0') /* [31:0] A:B:G:R 2:10:10:10 little endian */
+#define DRM_FORMAT_RGBA1010102	fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */
+#define DRM_FORMAT_BGRA1010102	fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */
+
+/* packed YCbCr */
+#define DRM_FORMAT_YUYV		fourcc_code('Y', 'U', 'Y', 'V') /* [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian */
+#define DRM_FORMAT_YVYU		fourcc_code('Y', 'V', 'Y', 'U') /* [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian */
+#define DRM_FORMAT_UYVY		fourcc_code('U', 'Y', 'V', 'Y') /* [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian */
+#define DRM_FORMAT_VYUY		fourcc_code('V', 'Y', 'U', 'Y') /* [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian */
+
+#define DRM_FORMAT_AYUV		fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */
+
+/*
+ * 2 plane RGB + A
+ * index 0 = RGB plane, same format as the corresponding non _A8 format has
+ * index 1 = A plane, [7:0] A
+ */
+#define DRM_FORMAT_XRGB8888_A8	fourcc_code('X', 'R', 'A', '8')
+#define DRM_FORMAT_XBGR8888_A8	fourcc_code('X', 'B', 'A', '8')
+#define DRM_FORMAT_RGBX8888_A8	fourcc_code('R', 'X', 'A', '8')
+#define DRM_FORMAT_BGRX8888_A8	fourcc_code('B', 'X', 'A', '8')
+#define DRM_FORMAT_RGB888_A8	fourcc_code('R', '8', 'A', '8')
+#define DRM_FORMAT_BGR888_A8	fourcc_code('B', '8', 'A', '8')
+#define DRM_FORMAT_RGB565_A8	fourcc_code('R', '5', 'A', '8')
+#define DRM_FORMAT_BGR565_A8	fourcc_code('B', '5', 'A', '8')
+
+/*
+ * 2 plane YCbCr
+ * index 0 = Y plane, [7:0] Y
+ * index 1 = Cr:Cb plane, [15:0] Cr:Cb little endian
+ * or
+ * index 1 = Cb:Cr plane, [15:0] Cb:Cr little endian
+ */
+#define DRM_FORMAT_NV12		fourcc_code('N', 'V', '1', '2') /* 2x2 subsampled Cr:Cb plane */
+#define DRM_FORMAT_NV21		fourcc_code('N', 'V', '2', '1') /* 2x2 subsampled Cb:Cr plane */
+#define DRM_FORMAT_NV16		fourcc_code('N', 'V', '1', '6') /* 2x1 subsampled Cr:Cb plane */
+#define DRM_FORMAT_NV61		fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */
+#define DRM_FORMAT_NV24		fourcc_code('N', 'V', '2', '4') /* non-subsampled Cr:Cb plane */
+#define DRM_FORMAT_NV42		fourcc_code('N', 'V', '4', '2') /* non-subsampled Cb:Cr plane */
+
+/*
+ * 3 plane YCbCr
+ * index 0: Y plane, [7:0] Y
+ * index 1: Cb plane, [7:0] Cb
+ * index 2: Cr plane, [7:0] Cr
+ * or
+ * index 1: Cr plane, [7:0] Cr
+ * index 2: Cb plane, [7:0] Cb
+ */
+#define DRM_FORMAT_YUV410	fourcc_code('Y', 'U', 'V', '9') /* 4x4 subsampled Cb (1) and Cr (2) planes */
+#define DRM_FORMAT_YVU410	fourcc_code('Y', 'V', 'U', '9') /* 4x4 subsampled Cr (1) and Cb (2) planes */
+#define DRM_FORMAT_YUV411	fourcc_code('Y', 'U', '1', '1') /* 4x1 subsampled Cb (1) and Cr (2) planes */
+#define DRM_FORMAT_YVU411	fourcc_code('Y', 'V', '1', '1') /* 4x1 subsampled Cr (1) and Cb (2) planes */
+#define DRM_FORMAT_YUV420	fourcc_code('Y', 'U', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes */
+#define DRM_FORMAT_YVU420	fourcc_code('Y', 'V', '1', '2') /* 2x2 subsampled Cr (1) and Cb (2) planes */
+#define DRM_FORMAT_YUV422	fourcc_code('Y', 'U', '1', '6') /* 2x1 subsampled Cb (1) and Cr (2) planes */
+#define DRM_FORMAT_YVU422	fourcc_code('Y', 'V', '1', '6') /* 2x1 subsampled Cr (1) and Cb (2) planes */
+#define DRM_FORMAT_YUV444	fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */
+#define DRM_FORMAT_YVU444	fourcc_code('Y', 'V', '2', '4') /* non-subsampled Cr (1) and Cb (2) planes */
+
+
+/*
+ * Format Modifiers:
+ *
+ * Format modifiers describe, typically, a re-ordering or modification
+ * of the data in a plane of an FB.  This can be used to express tiled/
+ * swizzled formats, or compression, or a combination of the two.
+ *
+ * The upper 8 bits of the format modifier are a vendor-id as assigned
+ * below.  The lower 56 bits are assigned as vendor sees fit.
+ */
+
+/* Vendor Ids: */
+#define DRM_FORMAT_MOD_NONE           0
+#define DRM_FORMAT_MOD_VENDOR_NONE    0
+#define DRM_FORMAT_MOD_VENDOR_INTEL   0x01
+#define DRM_FORMAT_MOD_VENDOR_AMD     0x02
+#define DRM_FORMAT_MOD_VENDOR_NV      0x03
+#define DRM_FORMAT_MOD_VENDOR_SAMSUNG 0x04
+#define DRM_FORMAT_MOD_VENDOR_QCOM    0x05
+#define DRM_FORMAT_MOD_VENDOR_VIVANTE 0x06
+#define DRM_FORMAT_MOD_VENDOR_BROADCOM 0x07
+/* add more to the end as needed */
+
+#define DRM_FORMAT_RESERVED	      ((1ULL << 56) - 1)
+
+#define fourcc_mod_code(vendor, val) \
+	((((__u64)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | (val & 0x00ffffffffffffffULL))
+
+/*
+ * Format Modifier tokens:
+ *
+ * When adding a new token please document the layout with a code comment,
+ * similar to the fourcc codes above. drm_fourcc.h is considered the
+ * authoritative source for all of these.
+ */
+
+/*
+ * Invalid Modifier
+ *
+ * This modifier can be used as a sentinel to terminate the format modifiers
+ * list, or to initialize a variable with an invalid modifier. It might also be
+ * used to report an error back to userspace for certain APIs.
+ */
+#define DRM_FORMAT_MOD_INVALID	fourcc_mod_code(NONE, DRM_FORMAT_RESERVED)
+
+/*
+ * Linear Layout
+ *
+ * Just plain linear layout. Note that this is different from no specifying any
+ * modifier (e.g. not setting DRM_MODE_FB_MODIFIERS in the DRM_ADDFB2 ioctl),
+ * which tells the driver to also take driver-internal information into account
+ * and so might actually result in a tiled framebuffer.
+ */
+#define DRM_FORMAT_MOD_LINEAR	fourcc_mod_code(NONE, 0)
+
+/* Intel framebuffer modifiers */
+
+/*
+ * Intel X-tiling layout
+ *
+ * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb)
+ * in row-major layout. Within the tile bytes are laid out row-major, with
+ * a platform-dependent stride. On top of that the memory can apply
+ * platform-depending swizzling of some higher address bits into bit6.
+ *
+ * This format is highly platforms specific and not useful for cross-driver
+ * sharing. It exists since on a given platform it does uniquely identify the
+ * layout in a simple way for i915-specific userspace.
+ */
+#define I915_FORMAT_MOD_X_TILED	fourcc_mod_code(INTEL, 1)
+
+/*
+ * Intel Y-tiling layout
+ *
+ * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb)
+ * in row-major layout. Within the tile bytes are laid out in OWORD (16 bytes)
+ * chunks column-major, with a platform-dependent height. On top of that the
+ * memory can apply platform-depending swizzling of some higher address bits
+ * into bit6.
+ *
+ * This format is highly platforms specific and not useful for cross-driver
+ * sharing. It exists since on a given platform it does uniquely identify the
+ * layout in a simple way for i915-specific userspace.
+ */
+#define I915_FORMAT_MOD_Y_TILED	fourcc_mod_code(INTEL, 2)
+
+/*
+ * Intel Yf-tiling layout
+ *
+ * This is a tiled layout using 4Kb tiles in row-major layout.
+ * Within the tile pixels are laid out in 16 256 byte units / sub-tiles which
+ * are arranged in four groups (two wide, two high) with column-major layout.
+ * Each group therefore consits out of four 256 byte units, which are also laid
+ * out as 2x2 column-major.
+ * 256 byte units are made out of four 64 byte blocks of pixels, producing
+ * either a square block or a 2:1 unit.
+ * 64 byte blocks of pixels contain four pixel rows of 16 bytes, where the width
+ * in pixel depends on the pixel depth.
+ */
+#define I915_FORMAT_MOD_Yf_TILED fourcc_mod_code(INTEL, 3)
+
+/*
+ * Intel color control surface (CCS) for render compression
+ *
+ * The framebuffer format must be one of the 8:8:8:8 RGB formats.
+ * The main surface will be plane index 0 and must be Y/Yf-tiled,
+ * the CCS will be plane index 1.
+ *
+ * Each CCS tile matches a 1024x512 pixel area of the main surface.
+ * To match certain aspects of the 3D hardware the CCS is
+ * considered to be made up of normal 128Bx32 Y tiles, Thus
+ * the CCS pitch must be specified in multiples of 128 bytes.
+ *
+ * In reality the CCS tile appears to be a 64Bx64 Y tile, composed
+ * of QWORD (8 bytes) chunks instead of OWORD (16 bytes) chunks.
+ * But that fact is not relevant unless the memory is accessed
+ * directly.
+ */
+#define I915_FORMAT_MOD_Y_TILED_CCS	fourcc_mod_code(INTEL, 4)
+#define I915_FORMAT_MOD_Yf_TILED_CCS	fourcc_mod_code(INTEL, 5)
+
+/*
+ * Tiled, NV12MT, grouped in 64 (pixels) x 32 (lines) -sized macroblocks
+ *
+ * Macroblocks are laid in a Z-shape, and each pixel data is following the
+ * standard NV12 style.
+ * As for NV12, an image is the result of two frame buffers: one for Y,
+ * one for the interleaved Cb/Cr components (1/2 the height of the Y buffer).
+ * Alignment requirements are (for each buffer):
+ * - multiple of 128 pixels for the width
+ * - multiple of  32 pixels for the height
+ *
+ * For more information: see https://linuxtv.org/downloads/v4l-dvb-apis/re32.html
+ */
+#define DRM_FORMAT_MOD_SAMSUNG_64_32_TILE	fourcc_mod_code(SAMSUNG, 1)
+
+/* Vivante framebuffer modifiers */
+
+/*
+ * Vivante 4x4 tiling layout
+ *
+ * This is a simple tiled layout using tiles of 4x4 pixels in a row-major
+ * layout.
+ */
+#define DRM_FORMAT_MOD_VIVANTE_TILED		fourcc_mod_code(VIVANTE, 1)
+
+/*
+ * Vivante 64x64 super-tiling layout
+ *
+ * This is a tiled layout using 64x64 pixel super-tiles, where each super-tile
+ * contains 8x4 groups of 2x4 tiles of 4x4 pixels (like above) each, all in row-
+ * major layout.
+ *
+ * For more information: see
+ * https://github.com/etnaviv/etna_viv/blob/master/doc/hardware.md#texture-tiling
+ */
+#define DRM_FORMAT_MOD_VIVANTE_SUPER_TILED	fourcc_mod_code(VIVANTE, 2)
+
+/*
+ * Vivante 4x4 tiling layout for dual-pipe
+ *
+ * Same as the 4x4 tiling layout, except every second 4x4 pixel tile starts at a
+ * different base address. Offsets from the base addresses are therefore halved
+ * compared to the non-split tiled layout.
+ */
+#define DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED	fourcc_mod_code(VIVANTE, 3)
+
+/*
+ * Vivante 64x64 super-tiling layout for dual-pipe
+ *
+ * Same as the 64x64 super-tiling layout, except every second 4x4 pixel tile
+ * starts at a different base address. Offsets from the base addresses are
+ * therefore halved compared to the non-split super-tiled layout.
+ */
+#define DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED fourcc_mod_code(VIVANTE, 4)
+
+/* NVIDIA Tegra frame buffer modifiers */
+
+/*
+ * Some modifiers take parameters, for example the number of vertical GOBs in
+ * a block. Reserve the lower 32 bits for parameters
+ */
+#define __fourcc_mod_tegra_mode_shift 32
+#define fourcc_mod_tegra_code(val, params) \
+	fourcc_mod_code(NV, ((((__u64)val) << __fourcc_mod_tegra_mode_shift) | params))
+#define fourcc_mod_tegra_mod(m) \
+	(m & ~((1ULL << __fourcc_mod_tegra_mode_shift) - 1))
+#define fourcc_mod_tegra_param(m) \
+	(m & ((1ULL << __fourcc_mod_tegra_mode_shift) - 1))
+
+/*
+ * Tegra Tiled Layout, used by Tegra 2, 3 and 4.
+ *
+ * Pixels are arranged in simple tiles of 16 x 16 bytes.
+ */
+#define NV_FORMAT_MOD_TEGRA_TILED fourcc_mod_tegra_code(1, 0)
+
+/*
+ * Tegra 16Bx2 Block Linear layout, used by TK1/TX1
+ *
+ * Pixels are arranged in 64x8 Groups Of Bytes (GOBs). GOBs are then stacked
+ * vertically by a power of 2 (1 to 32 GOBs) to form a block.
+ *
+ * Within a GOB, data is ordered as 16B x 2 lines sectors laid in Z-shape.
+ *
+ * Parameter 'v' is the log2 encoding of the number of GOBs stacked vertically.
+ * Valid values are:
+ *
+ * 0 == ONE_GOB
+ * 1 == TWO_GOBS
+ * 2 == FOUR_GOBS
+ * 3 == EIGHT_GOBS
+ * 4 == SIXTEEN_GOBS
+ * 5 == THIRTYTWO_GOBS
+ *
+ * Chapter 20 "Pixel Memory Formats" of the Tegra X1 TRM describes this format
+ * in full detail.
+ */
+#define NV_FORMAT_MOD_TEGRA_16BX2_BLOCK(v) fourcc_mod_tegra_code(2, v)
+
+/*
+ * Broadcom VC4 "T" format
+ *
+ * This is the primary layout that the V3D GPU can texture from (it
+ * can't do linear).  The T format has:
+ *
+ * - 64b utiles of pixels in a raster-order grid according to cpp.  It's 4x4
+ *   pixels at 32 bit depth.
+ *
+ * - 1k subtiles made of a 4x4 raster-order grid of 64b utiles (so usually
+ *   16x16 pixels).
+ *
+ * - 4k tiles made of a 2x2 grid of 1k subtiles (so usually 32x32 pixels).  On
+ *   even 4k tile rows, they're arranged as (BL, TL, TR, BR), and on odd rows
+ *   they're (TR, BR, BL, TL), where bottom left is start of memory.
+ *
+ * - an image made of 4k tiles in rows either left-to-right (even rows of 4k
+ *   tiles) or right-to-left (odd rows of 4k tiles).
+ */
+#define DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED fourcc_mod_code(BROADCOM, 1)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* DRM_FOURCC_H */
diff --git a/src/server/linuxdmabuf_v1_interface.h b/src/server/linuxdmabuf_v1_interface.h
new file mode 100644
--- /dev/null
+++ b/src/server/linuxdmabuf_v1_interface.h
@@ -0,0 +1,181 @@
+/********************************************************************
+Copyright © 2018 Fredrik Höglund <fredrik@kde.org>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#ifndef WAYLAND_SERVER_LINUXDMABUF_INTERFACE_H
+#define WAYLAND_SERVER_LINUXDMABUF_INTERFACE_H
+
+#include <KWayland/Server/kwaylandserver_export.h>
+#include "global.h"
+#include "resource.h"
+
+#include <QSize>
+
+
+struct wl_buffer_interface;
+
+namespace KWayland
+{
+
+namespace Server
+{
+
+namespace LinuxDmabuf
+{
+    enum Flag {
+        YInverted        = (1 << 0), /// Contents are y-inverted
+        Interlaced       = (1 << 1), /// Content is interlaced
+        BottomFieldFirst = (1 << 2)  /// Bottom field first
+    };
+
+    Q_DECLARE_FLAGS(Flags, Flag)
+
+    /**
+     * Represents a plane in a buffer
+     */
+    struct Plane {
+        int fd;              /// The dmabuf file descriptor
+        uint32_t offset;     /// The offset from the start of buffer
+        uint32_t stride;     /// The distance from the start of a row to the next row in bytes
+        uint64_t modifier;   /// The layout modifier
+    };
+
+    /**
+     * The base class for linux-dmabuf buffers
+     *
+     * Compositors should reimplement this class to store objects specific
+     * to the underlying graphics stack.
+     */
+    class Buffer {
+    public:
+        /**
+         * Creates a new Buffer.
+         */
+        Buffer(uint32_t format, const QSize &size) : m_format(format), m_size(size) {}
+
+        /**
+         * Destroys the Buffer.
+         */
+        virtual ~Buffer() = default;
+
+        /**
+         * Returns the DRM format code for the buffer.
+         */
+        uint32_t format() const { return m_format; }
+
+        /**
+         * Returns the size of the buffer.
+         */
+        QSize size() const { return m_size; }
+
+    private:
+        uint32_t m_format;
+        QSize m_size;
+    };
+}
+
+
+/**
+ * Represents the global zpw_linux_dmabuf_v1 interface.
+ *
+ * This interface provides a way for clients to create generic dmabuf based wl_buffers.
+ */
+class KWAYLANDSERVER_EXPORT LinuxDmabufUnstableV1Interface : public Global
+{
+    Q_OBJECT
+
+public:
+    /**
+     * The Bridge class provides an interface from the LinuxDmabufInterface to the compositor
+     */
+    class Bridge {
+    public:
+        Bridge() = default;
+        virtual ~Bridge() = default;
+
+        /**
+         * Returns the DRM format codes supported by the compositor.
+         */
+        virtual QVector<uint32_t> supportedFormats() const = 0;
+
+        /**
+         * Returns the layout-modifiers supported for the given DRM format code.
+         */
+        virtual QVector<uint64_t> supportedModifiers(uint32_t format) const = 0;
+
+        /**
+         * Imports a linux-dmabuf buffer into the compositor.
+         *
+         * The returned buffer takes ownership of the file descriptor for each plane.
+         * Note that it is the responsibility of the caller to close the file descriptors
+         * when the import fails.
+         *
+         * @return The imported buffer on success, and nullptr otherwise.
+         */
+        virtual LinuxDmabuf::Buffer *importBuffer(const QVector<LinuxDmabuf::Plane> &planes,
+                                                  uint32_t format,
+                                                  const QSize &size,
+                                                  LinuxDmabuf::Flags flags) = 0;
+    };
+
+    /**
+     * Destroys the LinuxDmabufUnstableV1Interface.
+     */
+    virtual ~LinuxDmabufUnstableV1Interface();
+
+    /**
+     * Sets the compositor bridge for the dmabuf interface.
+     */
+    void setBridge(Bridge *bridge);
+
+    /**
+     * Returns the compositor bridge for the dmabuf interface.
+     */
+    Bridge *bridge() const;
+
+    /**
+     * Returns the LinuxDmabufInterface for the given resource.
+     **/
+    static LinuxDmabufUnstableV1Interface *get(wl_resource *native);
+
+    /**
+     * Returns a pointer to the wl_buffer implementation for imported dmabufs.
+     */
+    static const struct wl_buffer_interface *bufferImplementation();
+
+private:
+    /**
+     * @internal
+     */
+    explicit LinuxDmabufUnstableV1Interface(Display *display, QObject *parent = nullptr);
+    friend class Display;
+
+private:
+    class Private;
+    Private *d_func() const;
+};
+
+
+} // namespace Server
+} // namespace KWayland
+
+Q_DECLARE_METATYPE(KWayland::Server::LinuxDmabufUnstableV1Interface*)
+Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Server::LinuxDmabuf::Flags)
+
+#endif // WAYLAND_SERVER_LINUXDMABUF_INTERFACE_H
diff --git a/src/server/linuxdmabuf_v1_interface.cpp b/src/server/linuxdmabuf_v1_interface.cpp
new file mode 100644
--- /dev/null
+++ b/src/server/linuxdmabuf_v1_interface.cpp
@@ -0,0 +1,490 @@
+/********************************************************************
+Copyright © 2018 Fredrik Höglund <fredrik@kde.org>
+
+Based on the libweston implementation,
+Copyright © 2014, 2015 Collabora, Ltd.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#include <KWayland/Server/kwaylandserver_export.h>
+#include "linuxdmabuf_v1_interface.h"
+#include "wayland-linux-dmabuf-unstable-v1-server-protocol.h"
+#include "wayland-server-protocol.h"
+#include "global_p.h"
+
+#include "drm_fourcc.h"
+
+#include <QVector>
+
+#include <unistd.h>
+#include <assert.h>
+
+#include <array>
+
+
+namespace KWayland
+{
+
+namespace Server
+{
+
+
+class LinuxDmabufParams
+{
+public:
+    LinuxDmabufParams(LinuxDmabufUnstableV1Interface *dmabufInterface, wl_client *client, uint32_t version, uint32_t id);
+    ~LinuxDmabufParams();
+
+    void postNoMemory() { wl_resource_post_no_memory(m_resource); }
+
+    wl_resource *resource() const { return m_resource; }
+
+    void add(int fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint64_t modifier);
+    void create(wl_client *client, uint32_t bufferId, const QSize &size, uint32_t format, uint32_t flags);
+
+    static void destroy(wl_client *client, wl_resource *resource);
+    static void add(wl_client *client, wl_resource *resource, int fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo);
+    static void create(wl_client *client, wl_resource *resource, int width, int height, uint32_t format, uint32_t flags);
+    static void createImmed(wl_client *client, wl_resource *resource, uint32_t new_id, int width, int height, uint32_t format, uint32_t flags);
+
+private:
+    static const struct zwp_linux_buffer_params_v1_interface s_interface;
+
+    wl_resource *m_resource;
+    LinuxDmabufUnstableV1Interface *m_dmabufInterface;
+    std::array<LinuxDmabuf::Plane, 4> m_planes;
+    size_t m_planeCount = 0;
+    bool m_bufferCreated = false;
+};
+
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+const struct zwp_linux_buffer_params_v1_interface LinuxDmabufParams::s_interface = {
+    destroy,
+    add,
+    create,
+    createImmed
+};
+#endif
+
+
+LinuxDmabufParams::LinuxDmabufParams(LinuxDmabufUnstableV1Interface *dmabufInterface, wl_client *client, uint32_t version, uint32_t id)
+    : m_dmabufInterface(dmabufInterface)
+{
+    m_resource = wl_resource_create(client, &zwp_linux_buffer_params_v1_interface, version, id);
+    if (!m_resource) {
+        return;
+    }
+
+    wl_resource_set_implementation(m_resource, &s_interface, this,
+                                   [](wl_resource *resource) {
+                                       delete static_cast<LinuxDmabufParams *>(wl_resource_get_user_data(resource));
+                                   });
+
+    for (auto &plane : m_planes) {
+        plane.fd = -1;
+        plane.offset = 0;
+        plane.stride = 0;
+        plane.modifier = 0;
+    }
+}
+
+
+LinuxDmabufParams::~LinuxDmabufParams()
+{
+    // Close the file descriptors
+    for (auto &plane : m_planes) {
+        if (plane.fd != -1) {
+            ::close(plane.fd);
+        }
+    }
+}
+
+
+void LinuxDmabufParams::create(wl_client *client, uint32_t bufferId, const QSize &size, uint32_t format, uint32_t flags)
+{
+    // Validate the parameters
+    // -----------------------
+    const uint32_t width = size.width();
+    const uint32_t height = size.height();
+
+    if (m_bufferCreated) {
+        wl_resource_post_error(m_resource,
+                               ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
+                               "params was already used to create a wl_buffer");
+        return;
+    }
+
+    if (m_planeCount == 0) {
+        wl_resource_post_error(m_resource,
+                               ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+                               "no dmabuf has been added to the params");
+        return;
+    }
+
+    // Check for holes in the dmabufs set (e.g. [0, 1, 3])
+    for (uint32_t i = 0; i < m_planeCount; i++) {
+        if (m_planes[i].fd != -1)
+            continue;
+
+        wl_resource_post_error(m_resource,
+                               ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+                               "no dmabuf has been added for plane %i", i);
+        return;
+    }
+
+    if (width < 1 || height < 1) {
+        wl_resource_post_error(m_resource,
+                               ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
+                               "invalid width %d or height %d", width, height);
+        return;
+    }
+
+    for (uint32_t i = 0; i < m_planeCount; i++) {
+        auto &plane = m_planes[i];
+
+        if (uint64_t(plane.offset) + plane.stride > UINT32_MAX) {
+            wl_resource_post_error(m_resource,
+                                   ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+                                   "size overflow for plane %i", i);
+            return;
+        }
+
+        if (i == 0 && uint64_t(plane.offset) + plane.stride * height > UINT32_MAX) {
+            wl_resource_post_error(m_resource,
+                                   ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+                                   "size overflow for plane %i", i);
+            return;
+        }
+
+        // Don't report an error as it might be caused by the kernel not supporting seeking on dmabuf
+        off_t size = ::lseek(plane.fd, 0, SEEK_END);
+        if (size == -1)
+            continue;
+
+        if (plane.offset >= size) {
+            wl_resource_post_error(m_resource,
+                                   ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+                                   "invalid offset %i for plane %i",
+                                   plane.offset, i);
+            return;
+        }
+
+        if (plane.offset + plane.stride > size) {
+            wl_resource_post_error(m_resource,
+                                   ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+                                   "invalid stride %i for plane %i",
+                                   plane.stride, i);
+            return;
+        }
+
+        // Only valid for first plane as other planes might be
+        // sub-sampled according to fourcc format
+        if (i == 0 && plane.offset + plane.stride * height > size) {
+            wl_resource_post_error(m_resource,
+                                   ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+                                   "invalid buffer stride or height for plane %i", i);
+            return;
+        }
+    }
+
+
+    // Import the buffer
+    // -----------------
+    QVector<LinuxDmabuf::Plane> planes;
+    planes.reserve(m_planeCount);
+    for (uint32_t i = 0; i < m_planeCount; i++)
+        planes << m_planes[i];
+
+    LinuxDmabuf::Buffer *buffer = m_dmabufInterface->bridge()->importBuffer(planes, format, size, (LinuxDmabuf::Flags) flags);
+    if (buffer) {
+        // The buffer has ownership of the file descriptors now
+        for (auto &plane : m_planes) {
+            plane.fd = -1;
+        }
+
+        wl_resource *resource = wl_resource_create(client, &wl_buffer_interface, 1, bufferId);
+        if (!resource ) {
+            postNoMemory();
+            delete buffer;
+            return;
+        }
+
+        wl_resource_set_implementation(resource, m_dmabufInterface->bufferImplementation(), buffer,
+                                       [](wl_resource *resource) { // Destructor
+                                            delete static_cast<LinuxDmabuf::Buffer *>(wl_resource_get_user_data(resource));
+                                       });
+
+        // XXX Do we need this?
+        //buffer->setResource(resource);
+
+        // Send a 'created' event when the request is not for an immediate import, i.e. bufferId is zero
+        if (bufferId == 0) {
+            zwp_linux_buffer_params_v1_send_created(m_resource, resource);
+        }
+
+        m_bufferCreated = true;
+    } else {
+        if (bufferId == 0) {
+            zwp_linux_buffer_params_v1_send_failed(m_resource);
+        } else {
+            // since the behavior is left implementation defined by the
+            // protocol in case of create_immed failure due to an unknown cause,
+            // we choose to treat it as a fatal error and immediately kill the
+            // client instead of creating an invalid handle and waiting for it
+            // to be used.
+            wl_resource_post_error(m_resource,
+                                   ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER,
+                                   "importing the supplied dmabufs failed");
+        }
+    }
+}
+
+
+void LinuxDmabufParams::add(int fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint64_t modifier)
+{
+    if (m_bufferCreated) {
+        wl_resource_post_error(m_resource,
+                               ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
+                               "params was already used to create a wl_buffer");
+        ::close(fd);
+        return;
+    }
+
+    if (plane_idx >= m_planes.size()) {
+        wl_resource_post_error(m_resource,
+                               ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
+                               "plane index %u is too high", plane_idx);
+        ::close(fd);
+        return;
+    }
+
+    auto &plane = m_planes[plane_idx];
+
+    if (plane.fd != -1) {
+        wl_resource_post_error(m_resource,
+                               ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
+                               "a dmabuf has already been added for plane %u",
+                               plane_idx);
+        ::close(fd);
+        return;
+    }
+
+    plane.fd = fd;
+    plane.offset = offset;
+    plane.stride = stride;
+    plane.modifier = modifier;
+
+    m_planeCount++;
+}
+
+
+
+// --------------------------------------------------------------------
+
+
+
+void LinuxDmabufParams::destroy(wl_client *client, wl_resource *resource)
+{
+    Q_UNUSED(client)
+    wl_resource_destroy(resource);
+}
+
+
+void LinuxDmabufParams::add(wl_client *client, wl_resource *resource,
+                            int fd, uint32_t plane_idx,
+                            uint32_t offset, uint32_t stride,
+                            uint32_t modifier_hi, uint32_t modifier_lo)
+{
+    Q_UNUSED(client)
+
+    LinuxDmabufParams *params = static_cast<LinuxDmabufParams *>(wl_resource_get_user_data(resource));
+    assert(params->m_resource == resource);
+    params->add(fd, plane_idx, offset, stride, (uint64_t(modifier_hi) << 32) | modifier_lo);
+}
+
+
+void LinuxDmabufParams::create(wl_client *client, wl_resource *resource,
+                               int width, int height, uint32_t format, uint32_t flags)
+{
+    Q_UNUSED(client)
+
+    LinuxDmabufParams *params = static_cast<LinuxDmabufParams *>(wl_resource_get_user_data(resource));
+    assert(params->m_resource == resource);
+    params->create(client, 0, QSize(width, height), format, flags);
+}
+
+
+void LinuxDmabufParams::createImmed(wl_client *client, wl_resource *resource,
+                                    uint32_t new_id, int width, int height,
+                                    uint32_t format, uint32_t flags)
+{
+    Q_UNUSED(client)
+
+    LinuxDmabufParams *params = static_cast<LinuxDmabufParams *>(wl_resource_get_user_data(resource));
+    assert(params->m_resource == resource);
+    params->create(client, new_id, QSize(width, height), format, flags);
+}
+
+
+
+// --------------------------------------------------------------------
+
+
+
+class LinuxDmabufUnstableV1Interface::Private : public Global::Private
+{
+public:
+    Private(LinuxDmabufUnstableV1Interface *q, Display *display);
+    ~Private();
+
+    static const struct wl_buffer_interface *bufferImplementation() { return &s_bufferImplementation; }
+    LinuxDmabufUnstableV1Interface::Bridge *bridge;
+    LinuxDmabufUnstableV1Interface * const q;
+    static const uint32_t s_version;
+
+    void bind(wl_client *client, uint32_t version, uint32_t id) override final;
+    void createParams(wl_client *client, wl_resource *resource, uint32_t id);
+
+    static void unbind(wl_client *client, wl_resource *resource);
+    static void createParamsCallback(wl_client *client, wl_resource *resource, uint32_t id);
+
+private:
+    static const struct zwp_linux_dmabuf_v1_interface s_implementation;
+    static const struct wl_buffer_interface s_bufferImplementation;
+};
+
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+const struct zwp_linux_dmabuf_v1_interface LinuxDmabufUnstableV1Interface::Private::s_implementation = {
+    [](wl_client *, wl_resource *resource) { wl_resource_destroy(resource); }, // unbind
+    createParamsCallback
+};
+
+
+const struct wl_buffer_interface LinuxDmabufUnstableV1Interface::Private::s_bufferImplementation = {
+    [](wl_client *, wl_resource *resource) { wl_resource_destroy(resource); } // destroy
+};
+
+const uint32_t LinuxDmabufUnstableV1Interface::Private::s_version = 3;
+#endif
+
+
+LinuxDmabufUnstableV1Interface::Private::Private(LinuxDmabufUnstableV1Interface *q, Display *display)
+    : Global::Private(display, &zwp_linux_dmabuf_v1_interface, s_version),
+      q(q)
+{
+}
+
+
+LinuxDmabufUnstableV1Interface::Private::~Private()
+{
+}
+
+
+void LinuxDmabufUnstableV1Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id)
+{
+    wl_resource *resource = wl_resource_create(client, &zwp_linux_dmabuf_v1_interface, std::min(s_version, version), id);
+    if (!resource) {
+        wl_client_post_no_memory(client);
+        return;
+    }
+
+    wl_resource_set_implementation(resource, &s_implementation, this, nullptr);
+
+    // Send formats & modifiers
+    // ------------------------
+    const QVector<uint32_t> formats = bridge->supportedFormats();
+    for (uint32_t format : formats) {
+        QVector<uint64_t> modifiers = bridge->supportedModifiers(format);
+        if (modifiers.isEmpty()) {
+            modifiers << DRM_FORMAT_MOD_INVALID;
+        }
+
+        for (uint64_t modifier : qAsConst(modifiers)) {
+            if (version >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
+                const uint32_t modifier_lo = modifier & 0xFFFFFFFF;
+                const uint32_t modifier_hi = modifier >> 32;
+                zwp_linux_dmabuf_v1_send_modifier(resource, format, modifier_hi, modifier_lo);
+            } else if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) {
+                zwp_linux_dmabuf_v1_send_format(resource, format);
+            }
+        }
+    }
+}
+
+
+void LinuxDmabufUnstableV1Interface::Private::createParams(wl_client *client, wl_resource *resource, uint32_t id)
+{
+    LinuxDmabufParams *params = new LinuxDmabufParams(q, client, wl_resource_get_version(resource), id);
+    if (!params->resource()) {
+        wl_resource_post_no_memory(resource);
+        delete params;
+    }
+}
+
+
+void LinuxDmabufUnstableV1Interface::Private::createParamsCallback(wl_client *client, wl_resource *resource, uint32_t id)
+{
+    LinuxDmabufUnstableV1Interface::Private *global = static_cast<LinuxDmabufUnstableV1Interface::Private *>(wl_resource_get_user_data(resource));
+    global->createParams(client, resource, id);
+}
+
+
+
+// --------------------------------------------------------------------
+
+
+
+LinuxDmabufUnstableV1Interface::LinuxDmabufUnstableV1Interface(Display *display, QObject *parent)
+    : Global(new Private(this, display), parent)
+{
+}
+
+
+LinuxDmabufUnstableV1Interface::~LinuxDmabufUnstableV1Interface()
+{
+}
+
+
+void LinuxDmabufUnstableV1Interface::setBridge(LinuxDmabufUnstableV1Interface::Bridge *bridge)
+{
+    d_func()->bridge = bridge;
+}
+
+
+LinuxDmabufUnstableV1Interface::Bridge *LinuxDmabufUnstableV1Interface::bridge() const
+{
+    return d_func()->bridge;
+}
+
+
+const struct wl_buffer_interface *LinuxDmabufUnstableV1Interface::bufferImplementation()
+{
+    return LinuxDmabufUnstableV1Interface::Private::bufferImplementation();
+}
+
+
+LinuxDmabufUnstableV1Interface::Private *LinuxDmabufUnstableV1Interface::d_func() const
+{
+    return reinterpret_cast<Private*>(d.data());
+}
+
+
+} // namespace Server
+} // namespace Wayland

Attachment: signature.asc
Description: OpenPGP digital signature


Reply to: