diff --git a/src/client/protocols/linux-dmabuf-unstable-v1.xml b/src/client/protocols/linux-dmabuf-unstable-v1.xml new file mode 100644 index 0000000..154afe2 --- /dev/null +++ b/src/client/protocols/linux-dmabuf-unstable-v1.xml @@ -0,0 +1,348 @@ + + + + + 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. + + + + + 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. + + + + + Objects created through this interface, especially wl_buffers, will + remain valid. + + + + + + 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. + + + + + + + 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. + + + + + + + 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. + + + + + + + + + + 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. + + + + + + + + + + + + + + + + Cleans up the temporary data sent to the server for dmabuf-based + wl_buffer creation. + + + + + + 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. + + + + + + + + + + + + + + + + + + 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. + + + + + + + + + + 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. + + + + + + + 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. + + + + + + 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. + + + + + + + + + + + diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index adf047b..684da5b 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -1,315 +1,323 @@ set(SERVER_LIB_SRCS appmenu_interface.cpp buffer_interface.cpp clientconnection.cpp compositor_interface.cpp datadevice_interface.cpp datadevicemanager_interface.cpp dataoffer_interface.cpp datasource_interface.cpp display.cpp dpms_interface.cpp filtered_display.cpp global.cpp idle_interface.cpp idleinhibit_interface.cpp idleinhibit_interface_v1.cpp fakeinput_interface.cpp keyboard_interface.cpp outputconfiguration_interface.cpp outputchangeset.cpp outputmanagement_interface.cpp outputdevice_interface.cpp logging.cpp output_interface.cpp pointer_interface.cpp plasmashell_interface.cpp plasmawindowmanagement_interface.cpp pointerconstraints_interface.cpp pointerconstraints_interface_v1.cpp pointergestures_interface.cpp pointergestures_interface_v1.cpp qtsurfaceextension_interface.cpp region_interface.cpp relativepointer_interface.cpp relativepointer_interface_v1.cpp resource.cpp seat_interface.cpp slide_interface.cpp shadow_interface.cpp blur_interface.cpp contrast_interface.cpp server_decoration_interface.cpp server_decoration_palette_interface.cpp shell_interface.cpp surface_interface.cpp subcompositor_interface.cpp touch_interface.cpp textinput_interface.cpp textinput_interface_v0.cpp textinput_interface_v2.cpp xdgshell_interface.cpp xdgshell_v5_interface.cpp xdgforeign_v2_interface.cpp xdgforeign_interface.cpp xdgshell_v6_interface.cpp + linuxdmabuf_v1_interface.cpp ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/output-management.xml BASENAME output-management ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/outputdevice.xml BASENAME org_kde_kwin_outputdevice ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-shell.xml BASENAME plasma-shell ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-window-management.xml BASENAME plasma-window-management ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/surface-extension.xml BASENAME qt-surface-extension ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/idle.xml BASENAME idle ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/fake-input.xml BASENAME fake-input ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/shadow.xml BASENAME shadow ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/dpms.xml BASENAME dpms ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/blur.xml BASENAME blur ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/contrast.xml BASENAME contrast ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/relative-pointer-unstable-v1.xml BASENAME relativepointer-unstable-v1 ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/slide.xml BASENAME slide ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/server-decoration.xml BASENAME server_decoration ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/text-input.xml BASENAME text ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/text-input-unstable-v2.xml BASENAME text-input-unstable-v2 ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/xdg-shell-unstable-v5.xml BASENAME xdg-shell-v5 ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/xdg-shell-unstable-v6.xml BASENAME xdg-shell-v6 ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/pointer-gestures-unstable-v1.xml BASENAME pointer-gestures-unstable-v1 ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/pointer-constraints-unstable-v1.xml BASENAME pointer-constraints-unstable-v1 ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/xdg-foreign-unstable-v2.xml BASENAME xdg-foreign-unstable-v2 ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/idle-inhibit-unstable-v1.xml BASENAME idle-inhibit-unstable-v1 ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/appmenu.xml BASENAME appmenu ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/server-decoration-palette.xml 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 ${CMAKE_CURRENT_BINARY_DIR}/wayland-org_kde_kwin_outputdevice-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-org_kde_kwin_outputdevice-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-shell-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-shell-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-window-management-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-window-management-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-qt-surface-extension-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-qt-surface-extension-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-idle-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-idle-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-fake-input-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-fake-input-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-shadow-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-shadow-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-dpms-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-dpms-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-contrast-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-contrast-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-relativepointer-unstable-v1-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-relativepointer-unstable-v1-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-slide-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-slide-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-server_decoration-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-server_decoration-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-server_decoration_palette-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-server_decoration_palette-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-text-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-text-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-text-input-unstable-v2-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-text-input-unstable-v2-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-shell-v5-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-shell-v5-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-shell-v6-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-shell-v6-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-pointer-gestures-unstable-v1-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-pointer-gestures-unstable-v1-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-pointer-constraints-unstable-v1-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-pointer-constraints-unstable-v1-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-foreign-unstable-v2-client-protocol.h ${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) add_library(KF5WaylandServer ${SERVER_LIB_SRCS}) generate_export_header(KF5WaylandServer BASE_NAME KWaylandServer EXPORT_FILE_NAME KWayland/Server/kwaylandserver_export.h ) add_library(KF5::WaylandServer ALIAS KF5WaylandServer) target_include_directories(KF5WaylandServer INTERFACE "$") target_link_libraries(KF5WaylandServer PUBLIC Qt5::Gui PRIVATE Wayland::Server EGL::EGL Qt5::Concurrent ) set_target_properties(KF5WaylandServer PROPERTIES VERSION ${KWAYLAND_VERSION_STRING} SOVERSION ${KWAYLAND_SOVERSION} EXPORT_NAME WaylandServer ) install(TARGETS KF5WaylandServer EXPORT KF5WaylandTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) set(SERVER_LIB_HEADERS ${CMAKE_CURRENT_BINARY_DIR}/KWayland/Server/kwaylandserver_export.h appmenu_interface.h blur_interface.h contrast_interface.h buffer_interface.h clientconnection.h compositor_interface.h datadevice_interface.h datadevicemanager_interface.h dataoffer_interface.h datasource_interface.h display.h dpms_interface.h filtered_display.h fakeinput_interface.h global.h idle_interface.h idleinhibit_interface.h keyboard_interface.h outputdevice_interface.h outputchangeset.h outputconfiguration_interface.h outputmanagement_interface.h output_interface.h pointer_interface.h pointerconstraints_interface.h pointergestures_interface.h plasmashell_interface.h plasmawindowmanagement_interface.h qtsurfaceextension_interface.h region_interface.h relativepointer_interface.h resource.h seat_interface.h server_decoration_interface.h server_decoration_palette_interface.h shadow_interface.h shell_interface.h slide_interface.h subcompositor_interface.h surface_interface.h textinput_interface.h touch_interface.h xdgshell_interface.h xdgforeign_interface.h + linuxdmabuf_v1_interface.h ) install(FILES ${SERVER_LIB_HEADERS} DESTINATION ${KF5_INCLUDE_INSTALL_DIR}/KWayland/Server COMPONENT Devel ) # make available to ecm_add_qch in parent folder set(KWaylandServer_APIDOX_SRCS ${SERVER_LIB_HEADERS} PARENT_SCOPE) include(ECMGeneratePriFile) ecm_generate_pri_file(BASE_NAME KWaylandServer LIB_NAME KF5WaylandServer DEPS "core" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) diff --git a/src/server/buffer_interface.cpp b/src/server/buffer_interface.cpp index 71c5a97..84252a2 100644 --- a/src/server/buffer_interface.cpp +++ b/src/server/buffer_interface.cpp @@ -1,303 +1,353 @@ /******************************************************************** Copyright 2014 Martin Gräßlin 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 . *********************************************************************/ #include "buffer_interface.h" #include "display.h" #include "logging_p.h" #include "surface_interface.h" +#include "linuxdmabuf_v1_interface.h" // Wayland #include // EGL #include #include +#include "drm_fourcc.h" + namespace KWayland { namespace Server { namespace EGL { typedef GLboolean(*eglQueryWaylandBufferWL_func)(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value); eglQueryWaylandBufferWL_func eglQueryWaylandBufferWL = nullptr; } class BufferInterface::Private { public: Private(BufferInterface *q, wl_resource *resource, SurfaceInterface *parent); ~Private(); QImage::Format format() const; QImage createImage(); wl_resource *buffer; wl_shm_buffer *shmBuffer; + LinuxDmabuf::Buffer *dmabufBuffer; SurfaceInterface *surface; int refCount; QSize size; bool alpha; static BufferInterface *get(wl_resource *r); private: static void destroyListenerCallback(wl_listener *listener, void *data); static Private *cast(wl_resource *r); static void imageBufferCleanupHandler(void *info); static QList s_buffers; static Private *s_accessedBuffer; static int s_accessCounter; BufferInterface *q; wl_listener listener; }; QList BufferInterface::Private::s_buffers; BufferInterface::Private *BufferInterface::Private::s_accessedBuffer = nullptr; int BufferInterface::Private::s_accessCounter = 0; BufferInterface::Private *BufferInterface::Private::cast(wl_resource *r) { auto it = std::find_if(s_buffers.constBegin(), s_buffers.constEnd(), [r](Private *d) { return d->buffer == r; }); if (it == s_buffers.constEnd()) { return nullptr; } return *it; } BufferInterface *BufferInterface::Private::get(wl_resource *r) { Private *p = cast(r); if (!p) { return nullptr; } return p->q; } void BufferInterface::Private::imageBufferCleanupHandler(void *info) { Private *p = reinterpret_cast(info); Q_ASSERT(p == s_accessedBuffer); Q_ASSERT(s_accessCounter > 0); s_accessCounter--; if (s_accessCounter == 0) { s_accessedBuffer = nullptr; } wl_shm_buffer_end_access(p->shmBuffer); } 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(wl_resource_get_user_data(resource)); + } s_buffers << this; listener.notify = destroyListenerCallback; listener.link.prev = nullptr; listener.link.next = nullptr; wl_resource_add_destroy_listener(resource, &listener); if (shmBuffer) { size = QSize(wl_shm_buffer_get_width(shmBuffer), wl_shm_buffer_get_height(shmBuffer)); // check alpha switch (wl_shm_buffer_get_format(shmBuffer)) { case WL_SHM_FORMAT_ARGB8888: alpha = true; break; case WL_SHM_FORMAT_XRGB8888: default: 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; using namespace EGL; if (!resolved && eglDisplay != EGL_NO_DISPLAY) { eglQueryWaylandBufferWL = (eglQueryWaylandBufferWL_func)eglGetProcAddress("eglQueryWaylandBufferWL"); resolved = true; } if (eglQueryWaylandBufferWL) { EGLint width, height; bool valid = false; valid = eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_WIDTH, &width); valid = valid && eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_HEIGHT, &height); if (valid) { size = QSize(width, height); } // check alpha EGLint format; if (eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_TEXTURE_FORMAT, &format)) { switch (format) { case EGL_TEXTURE_RGBA: alpha = true; break; case EGL_TEXTURE_RGB: default: alpha = false; break; } } } } } BufferInterface::Private::~Private() { wl_list_remove(&listener.link); s_buffers.removeAll(this); } BufferInterface *BufferInterface::get(wl_resource *r) { if (!r) { return nullptr; } // TODO: verify it's a buffer BufferInterface *b = Private::get(r); if (b) { return b; } return new BufferInterface(r, nullptr); } BufferInterface::BufferInterface(wl_resource *resource, SurfaceInterface *parent) : QObject() , d(new Private(this, resource, parent)) { } BufferInterface::~BufferInterface() { if (d->refCount != 0) { qCWarning(KWAYLAND_SERVER) << "Buffer destroyed while still being referenced, ref count:" << d->refCount; } } void BufferInterface::Private::destroyListenerCallback(wl_listener *listener, void *data) { Q_UNUSED(listener); auto b = cast(reinterpret_cast(data)); b->buffer = nullptr; emit b->q->aboutToBeDestroyed(b->q); delete b->q; } void BufferInterface::ref() { d->refCount++; } void BufferInterface::unref() { Q_ASSERT(d->refCount > 0); d->refCount--; if (d->refCount == 0) { if (d->buffer) { wl_buffer_send_release(d->buffer); wl_client_flush(wl_resource_get_client(d->buffer)); } deleteLater(); } } QImage::Format BufferInterface::Private::format() const { if (!shmBuffer) { return QImage::Format_Invalid; } switch (wl_shm_buffer_get_format(shmBuffer)) { case WL_SHM_FORMAT_ARGB8888: return QImage::Format_ARGB32_Premultiplied; case WL_SHM_FORMAT_XRGB8888: return QImage::Format_RGB32; default: return QImage::Format_Invalid; } } QImage BufferInterface::data() { return std::move(d->createImage()); } QImage BufferInterface::Private::createImage() { if (!shmBuffer) { return QImage(); } if (s_accessedBuffer != nullptr && s_accessedBuffer != this) { return QImage(); } const QImage::Format imageFormat = format(); if (imageFormat == QImage::Format_Invalid) { return QImage(); } s_accessedBuffer = this; s_accessCounter++; wl_shm_buffer_begin_access(shmBuffer); return std::move(QImage((const uchar*)wl_shm_buffer_get_data(shmBuffer), size.width(), size.height(), wl_shm_buffer_get_stride(shmBuffer), imageFormat, &imageBufferCleanupHandler, this)); } bool BufferInterface::isReferenced() const { return d->refCount > 0; } SurfaceInterface *BufferInterface::surface() const { return d->surface; } wl_shm_buffer *BufferInterface::shmBuffer() { return d->shmBuffer; } +LinuxDmabuf::Buffer *BufferInterface::linuxDmabufBuffer() +{ + return d->dmabufBuffer; +} + wl_resource *BufferInterface::resource() const { return d->buffer; } QSize BufferInterface::size() const { return d->size; } void BufferInterface::setSize(const QSize &size) { if (d->shmBuffer || d->size == size) { return; } d->size = size; emit sizeChanged(); } bool BufferInterface::hasAlphaChannel() const { return d->alpha; } } } diff --git a/src/server/buffer_interface.h b/src/server/buffer_interface.h index 1a764e1..8214e36 100644 --- a/src/server/buffer_interface.h +++ b/src/server/buffer_interface.h @@ -1,189 +1,197 @@ /******************************************************************** Copyright 2014 Martin Gräßlin 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 . *********************************************************************/ #ifndef WAYLAND_SERVER_BUFFER_INTERFACE_H #define WAYLAND_SERVER_BUFFER_INTERFACE_H #include #include #include struct wl_resource; struct wl_shm_buffer; namespace KWayland { namespace Server { class SurfaceInterface; +namespace LinuxDmabuf +{ + class Buffer; +} /** * @brief Reference counted representation of a Wayland buffer on Server side. * * This class encapsulates a rendering buffer which is normally attached to a SurfaceInterface. * A client should not render to a Wayland buffer as long as the buffer gets used by the server. * The server signals whether it's still used. This class provides a convenience access for this * functionality by performing reference counting and deleting the BufferInterface instance * automatically once it is no longer accessed. * * The BufferInterface is referenced as long as it is attached to a SurfaceInterface. If one wants * to keep access to the BufferInterface for a longer time ensure to call ref on first usage and * unref again once access to it is no longer needed. * * In Wayland the buffer is an abstract concept and a buffer might represent multiple different * concrete buffer techniques. This class has direct support for shared memory buffers built and * provides access to the native buffer for different (e.g. EGL/drm) buffers. * * If the EGL display has been registered in the Display the BufferInterface can also provide * some information about an EGL/drm buffer. * * For shared memory buffers a direct conversion to a memory-mapped QImage possible using the * data method. Please refer to the documentation for notes on the restrictions when using the * shared memory-mapped QImages. * * @see Display * @see SurfaceInterace **/ class KWAYLANDSERVER_EXPORT BufferInterface : public QObject { Q_OBJECT public: virtual ~BufferInterface(); /** * Reference the BufferInterface. * * As long as the reference counting has not reached @c 0 the BufferInterface is valid * and blocked for usage by the client. * * @see unref * @see isReferenced **/ void ref(); /** * Unreference the BufferInterface. * * If the reference counting reached @c 0 the BufferInterface is released, so that the * client can use it again. The instance of this BufferInterface will be automatically * deleted. * * @see ref * @see isReferenced **/ void unref(); /** * @returns whether the BufferInterface is currently referenced * * @see ref * @see unref **/ bool isReferenced() const; /** * @returns The SurfaceInterface this BufferInterface is attached to. **/ SurfaceInterface *surface() const; /** * @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. **/ wl_resource *resource() const; /** * Creates a QImage for the shared memory buffer. * * If the BufferInterface does not reference a shared memory buffer a null QImage is returned. * * The QImage shares the memory with the buffer and this constraints how the returned * QImage can be used and when this method can be invoked. * * It is not safe to have two shared memory QImages for different BufferInterfaces at * the same time. This method ensures that this does not happen and returns a null * QImage if a different BufferInterface's data is still mapped to a QImage. Please note * that this also applies to all implicitly data shared copies. * * In case it is needed to keep a copy, a deep copy has to be performed by using QImage::copy. * * As the underlying shared memory buffer is owned by a different client it is not safe to * write to the returned QImage. The image is a read-only buffer. If there is need to modify * the image, perform a deep copy. * **/ QImage data(); /** * Returns the size of this BufferInterface. * Note: only for shared memory buffers (shmBuffer) the size can be derived, * for other buffers it might be possible to derive the size if an EGL display * is set on Display otherwise the user of the BufferInterface has to use setSize to * provide the proper size. * @see setSize * @see Display::setEglDisplay * @since 5.3 **/ QSize size() const; /** * Sets the @p size for non shared memory buffers. * @see size * @see sizeChanged * @since 5.3 **/ void setSize(const QSize &size); /** * Returns whether the format of the BufferInterface has an alpha channel. * For shared memory buffers returns @c true for format @c WL_SHM_FORMAT_ARGB8888, * for all other formats returns @c false. * * For EGL buffers returns @c true for format @c EGL_TEXTURE_RGBA, for all other formats * returns @c false. * * If the format cannot be queried the default value (@c false) is returned. * * @since 5.4 **/ bool hasAlphaChannel() const; static BufferInterface *get(wl_resource *r); Q_SIGNALS: void aboutToBeDestroyed(KWayland::Server::BufferInterface*); /** * Emitted when the size of the Buffer changes. * @since 5.3 **/ void sizeChanged(); private: friend class SurfaceInterface; explicit BufferInterface(wl_resource *resource, SurfaceInterface *parent); class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Server::BufferInterface*) #endif diff --git a/src/server/display.cpp b/src/server/display.cpp index 391db06..ef54237 100644 --- a/src/server/display.cpp +++ b/src/server/display.cpp @@ -1,568 +1,576 @@ /******************************************************************** Copyright 2014 Martin Gräßlin 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 . *********************************************************************/ #include "display.h" #include "compositor_interface.h" #include "datadevicemanager_interface.h" #include "dpms_interface.h" #include "outputconfiguration_interface.h" #include "outputmanagement_interface.h" #include "outputdevice_interface.h" #include "idle_interface.h" #include "idleinhibit_interface_p.h" #include "fakeinput_interface.h" #include "logging_p.h" #include "output_interface.h" #include "plasmashell_interface.h" #include "plasmawindowmanagement_interface.h" #include "pointerconstraints_interface_p.h" #include "pointergestures_interface_p.h" #include "qtsurfaceextension_interface.h" #include "seat_interface.h" #include "shadow_interface.h" #include "blur_interface.h" #include "contrast_interface.h" #include "relativepointer_interface_p.h" #include "server_decoration_interface.h" #include "slide_interface.h" #include "shell_interface.h" #include "subcompositor_interface.h" #include "textinput_interface_p.h" #include "xdgshell_v5_interface_p.h" #include "xdgforeign_interface.h" #include "xdgshell_v6_interface_p.h" #include "appmenu_interface.h" #include "server_decoration_palette_interface.h" +#include "linuxdmabuf_v1_interface.h" #include #include #include #include #include #include #include namespace KWayland { namespace Server { class Display::Private { public: Private(Display *q); void flush(); void dispatch(); void setRunning(bool running); void installSocketNotifier(); wl_display *display = nullptr; wl_event_loop *loop = nullptr; QString socketName = QStringLiteral("wayland-0"); bool running = false; QList outputs; QList outputdevices; QVector seats; QVector clients; EGLDisplay eglDisplay = EGL_NO_DISPLAY; private: Display *q; }; Display::Private::Private(Display *q) : q(q) { } void Display::Private::installSocketNotifier() { if (!QThread::currentThread()) { return; } int fd = wl_event_loop_get_fd(loop); if (fd == -1) { qCWarning(KWAYLAND_SERVER) << "Did not get the file descriptor for the event loop"; return; } QSocketNotifier *m_notifier = new QSocketNotifier(fd, QSocketNotifier::Read, q); QObject::connect(m_notifier, &QSocketNotifier::activated, q, [this] { dispatch(); } ); QObject::connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, q, [this] { flush(); }); setRunning(true); } Display::Display(QObject *parent) : QObject(parent) , d(new Private(this)) { } Display::~Display() { terminate(); if (d->display) { wl_display_destroy(d->display); } } void Display::Private::flush() { if (!display || !loop) { return; } wl_display_flush_clients(display); } void Display::Private::dispatch() { if (!display || !loop) { return; } if (wl_event_loop_dispatch(loop, 0) != 0) { qCWarning(KWAYLAND_SERVER) << "Error on dispatching Wayland event loop"; } } void Display::setSocketName(const QString &name) { if (d->socketName == name) { return; } d->socketName = name; emit socketNameChanged(d->socketName); } QString Display::socketName() const { return d->socketName; } void Display::start(StartMode mode) { Q_ASSERT(!d->running); Q_ASSERT(!d->display); d->display = wl_display_create(); if (mode == StartMode::ConnectToSocket) { if (wl_display_add_socket(d->display, qPrintable(d->socketName)) != 0) { qCWarning(KWAYLAND_SERVER) << "Failed to create Wayland socket"; return; } } d->loop = wl_display_get_event_loop(d->display); d->installSocketNotifier(); } void Display::startLoop() { Q_ASSERT(!d->running); Q_ASSERT(d->display); d->installSocketNotifier(); } void Display::dispatchEvents(int msecTimeout) { Q_ASSERT(d->display); if (d->running) { d->dispatch(); } else if (d->loop) { wl_event_loop_dispatch(d->loop, msecTimeout); wl_display_flush_clients(d->display); } } void Display::terminate() { if (!d->running) { return; } emit aboutToTerminate(); wl_display_terminate(d->display); wl_display_destroy(d->display); d->display = nullptr; d->loop = nullptr; d->setRunning(false); } void Display::Private::setRunning(bool r) { Q_ASSERT(running != r); running = r; emit q->runningChanged(running); } OutputInterface *Display::createOutput(QObject *parent) { OutputInterface *output = new OutputInterface(this, parent); connect(output, &QObject::destroyed, this, [this,output] { d->outputs.removeAll(output); }); connect(this, &Display::aboutToTerminate, output, [this,output] { removeOutput(output); }); d->outputs << output; return output; } CompositorInterface *Display::createCompositor(QObject *parent) { CompositorInterface *compositor = new CompositorInterface(this, parent); connect(this, &Display::aboutToTerminate, compositor, [this,compositor] { delete compositor; }); return compositor; } ShellInterface *Display::createShell(QObject *parent) { ShellInterface *shell = new ShellInterface(this, parent); connect(this, &Display::aboutToTerminate, shell, [this,shell] { delete shell; }); return shell; } OutputDeviceInterface *Display::createOutputDevice(QObject *parent) { OutputDeviceInterface *output = new OutputDeviceInterface(this, parent); connect(output, &QObject::destroyed, this, [this,output] { d->outputdevices.removeAll(output); }); connect(this, &Display::aboutToTerminate, output, [this,output] { removeOutputDevice(output); }); d->outputdevices << output; return output; } OutputManagementInterface *Display::createOutputManagement(QObject *parent) { OutputManagementInterface *om = new OutputManagementInterface(this, parent); connect(this, &Display::aboutToTerminate, om, [this,om] { delete om; }); return om; } SeatInterface *Display::createSeat(QObject *parent) { SeatInterface *seat = new SeatInterface(this, parent); connect(seat, &QObject::destroyed, this, [this, seat] { d->seats.removeAll(seat); }); connect(this, &Display::aboutToTerminate, seat, [this,seat] { delete seat; }); d->seats << seat; return seat; } SubCompositorInterface *Display::createSubCompositor(QObject *parent) { auto c = new SubCompositorInterface(this, parent); connect(this, &Display::aboutToTerminate, c, [this,c] { delete c; }); return c; } DataDeviceManagerInterface *Display::createDataDeviceManager(QObject *parent) { auto m = new DataDeviceManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, m, [this,m] { delete m; }); return m; } PlasmaShellInterface *Display::createPlasmaShell(QObject* parent) { auto s = new PlasmaShellInterface(this, parent); connect(this, &Display::aboutToTerminate, s, [this, s] { delete s; }); return s; } PlasmaWindowManagementInterface *Display::createPlasmaWindowManagement(QObject *parent) { auto wm = new PlasmaWindowManagementInterface(this, parent); connect(this, &Display::aboutToTerminate, wm, [this, wm] { delete wm; }); return wm; } QtSurfaceExtensionInterface *Display::createQtSurfaceExtension(QObject *parent) { auto s = new QtSurfaceExtensionInterface(this, parent); connect(this, &Display::aboutToTerminate, s, [this, s] { delete s; }); return s; } IdleInterface *Display::createIdle(QObject *parent) { auto i = new IdleInterface(this, parent); connect(this, &Display::aboutToTerminate, i, [this, i] { delete i; }); return i; } FakeInputInterface *Display::createFakeInput(QObject *parent) { auto i = new FakeInputInterface(this, parent); connect(this, &Display::aboutToTerminate, i, [this, i] { delete i; }); return i; } ShadowManagerInterface *Display::createShadowManager(QObject *parent) { auto s = new ShadowManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, s, [this, s] { delete s; }); return s; } BlurManagerInterface *Display::createBlurManager(QObject *parent) { auto b = new BlurManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); return b; } ContrastManagerInterface *Display::createContrastManager(QObject *parent) { auto b = new ContrastManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); return b; } SlideManagerInterface *Display::createSlideManager(QObject *parent) { auto b = new SlideManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); return b; } DpmsManagerInterface *Display::createDpmsManager(QObject *parent) { auto d = new DpmsManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, d, [this, d] { delete d; }); return d; } ServerSideDecorationManagerInterface *Display::createServerSideDecorationManager(QObject *parent) { auto d = new ServerSideDecorationManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, d, [d] { delete d; }); return d; } TextInputManagerInterface *Display::createTextInputManager(const TextInputInterfaceVersion &version, QObject *parent) { TextInputManagerInterface *t = nullptr; switch (version) { case TextInputInterfaceVersion::UnstableV0: t = new TextInputManagerUnstableV0Interface(this, parent); break; case TextInputInterfaceVersion::UnstableV1: // unsupported return nullptr; case TextInputInterfaceVersion::UnstableV2: t = new TextInputManagerUnstableV2Interface(this, parent); } connect(this, &Display::aboutToTerminate, t, [t] { delete t; }); return t; } XdgShellInterface *Display::createXdgShell(const XdgShellInterfaceVersion &version, QObject *parent) { XdgShellInterface *x = nullptr; switch (version) { case XdgShellInterfaceVersion::UnstableV5: x = new XdgShellV5Interface(this, parent); break; case XdgShellInterfaceVersion::UnstableV6: x = new XdgShellV6Interface(this, parent); break; } connect(this, &Display::aboutToTerminate, x, [x] { delete x; }); return x; } RelativePointerManagerInterface *Display::createRelativePointerManager(const RelativePointerInterfaceVersion &version, QObject *parent) { RelativePointerManagerInterface *r = nullptr; switch (version) { case RelativePointerInterfaceVersion::UnstableV1: r = new RelativePointerManagerUnstableV1Interface(this, parent); break; } connect(this, &Display::aboutToTerminate, r, [r] { delete r; }); return r; } PointerGesturesInterface *Display::createPointerGestures(const PointerGesturesInterfaceVersion &version, QObject *parent) { PointerGesturesInterface *p = nullptr; switch (version) { case PointerGesturesInterfaceVersion::UnstableV1: p = new PointerGesturesUnstableV1Interface(this, parent); break; } connect(this, &Display::aboutToTerminate, p, [p] { delete p; }); return p; } PointerConstraintsInterface *Display::createPointerConstraints(const PointerConstraintsInterfaceVersion &version, QObject *parent) { PointerConstraintsInterface *p = nullptr; switch (version) { case PointerConstraintsInterfaceVersion::UnstableV1: p = new PointerConstraintsUnstableV1Interface(this, parent); break; } connect(this, &Display::aboutToTerminate, p, [p] { delete p; }); return p; } XdgForeignInterface *Display::createXdgForeignInterface(QObject *parent) { XdgForeignInterface *foreign = new XdgForeignInterface(this, parent); connect(this, &Display::aboutToTerminate, foreign, [this,foreign] { delete foreign; }); return foreign; } IdleInhibitManagerInterface *Display::createIdleInhibitManager(const IdleInhibitManagerInterfaceVersion &version, QObject *parent) { IdleInhibitManagerInterface *i = nullptr; switch (version) { case IdleInhibitManagerInterfaceVersion::UnstableV1: i = new IdleInhibitManagerUnstableV1Interface(this, parent); break; } connect(this, &Display::aboutToTerminate, i, [this,i] { delete i; }); return i; } AppMenuManagerInterface *Display::createAppMenuManagerInterface(QObject *parent) { auto b = new AppMenuManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); return b; } ServerSideDecorationPaletteManagerInterface *Display::createServerSideDecorationPaletteManager(QObject *parent) { auto b = new ServerSideDecorationPaletteManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); 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); wl_display_init_shm(d->display); } void Display::removeOutput(OutputInterface *output) { d->outputs.removeAll(output); delete output; } void Display::removeOutputDevice(OutputDeviceInterface *output) { d->outputdevices.removeAll(output); delete output; } quint32 Display::nextSerial() { return wl_display_next_serial(d->display); } quint32 Display::serial() { return wl_display_get_serial(d->display); } bool Display::isRunning() const { return d->running; } Display::operator wl_display*() { return d->display; } Display::operator wl_display*() const { return d->display; } QList< OutputInterface* > Display::outputs() const { return d->outputs; } QList< OutputDeviceInterface* > Display::outputDevices() const { return d->outputdevices; } QVector Display::seats() const { return d->seats; } ClientConnection *Display::getConnection(wl_client *client) { Q_ASSERT(client); auto it = std::find_if(d->clients.constBegin(), d->clients.constEnd(), [client](ClientConnection *c) { return c->client() == client; } ); if (it != d->clients.constEnd()) { return *it; } // no ConnectionData yet, create it auto c = new ClientConnection(client, this); d->clients << c; connect(c, &ClientConnection::disconnected, this, [this] (ClientConnection *c) { const int index = d->clients.indexOf(c); Q_ASSERT(index != -1); d->clients.remove(index); Q_ASSERT(d->clients.indexOf(c) == -1); emit clientDisconnected(c); } ); emit clientConnected(c); return c; } QVector< ClientConnection* > Display::connections() const { return d->clients; } ClientConnection *Display::createClient(int fd) { Q_ASSERT(fd != -1); Q_ASSERT(d->display); wl_client *c = wl_client_create(d->display, fd); if (!c) { return nullptr; } return getConnection(c); } void Display::setEglDisplay(void *display) { if (d->eglDisplay != EGL_NO_DISPLAY) { qCWarning(KWAYLAND_SERVER) << "EGLDisplay cannot be changed"; return; } d->eglDisplay = (EGLDisplay)display; } void *Display::eglDisplay() const { return d->eglDisplay; } } } diff --git a/src/server/display.h b/src/server/display.h index 69e03b7..da77d44 100644 --- a/src/server/display.h +++ b/src/server/display.h @@ -1,301 +1,308 @@ /******************************************************************** Copyright 2014 Martin Gräßlin 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 . *********************************************************************/ #ifndef WAYLAND_SERVER_DISPLAY_H #define WAYLAND_SERVER_DISPLAY_H #include #include #include #include "clientconnection.h" struct wl_client; struct wl_display; struct wl_event_loop; namespace KWayland { /** * @short KWayland Server. * * This namespace groups all classes related to the Server module. * * The main entry point into the KWayland::Server API is the Display class. * It allows to create a Wayland server and create various global objects on it. * * KWayland::Server is an API to easily create a head-less Wayland server with a * Qt style API. * * @see Display **/ namespace Server { class CompositorInterface; class DataDeviceManagerInterface; class DpmsManagerInterface; class IdleInterface; enum class IdleInhibitManagerInterfaceVersion; class IdleInhibitManagerInterface; class FakeInputInterface; class OutputInterface; class OutputDeviceInterface; class OutputConfigurationInterface; class OutputManagementInterface; class PlasmaShellInterface; class PlasmaWindowManagementInterface; class QtSurfaceExtensionInterface; class SeatInterface; class ShadowManagerInterface; class BlurManagerInterface; class ContrastManagerInterface; class ServerSideDecorationManagerInterface; class SlideManagerInterface; class ShellInterface; class SubCompositorInterface; enum class TextInputInterfaceVersion; class TextInputManagerInterface; class XdgShellV5Interface; enum class XdgShellInterfaceVersion; class XdgShellInterface; enum class RelativePointerInterfaceVersion; class RelativePointerManagerInterface; enum class PointerGesturesInterfaceVersion; class PointerGesturesInterface; enum class PointerConstraintsInterfaceVersion; class PointerConstraintsInterface; class XdgForeignInterface; class AppMenuManagerInterface; class ServerSideDecorationPaletteManagerInterface; +class LinuxDmabufUnstableV1Interface; /** * @brief Class holding the Wayland server display loop. * * @todo Improve documentation **/ class KWAYLANDSERVER_EXPORT Display : public QObject { Q_OBJECT Q_PROPERTY(QString socketName READ socketName WRITE setSocketName NOTIFY socketNameChanged) Q_PROPERTY(bool running READ isRunning NOTIFY runningChanged) public: explicit Display(QObject *parent = nullptr); virtual ~Display(); void setSocketName(const QString &name); QString socketName() const; quint32 serial(); quint32 nextSerial(); /** * How to setup the server connection. * @li ConnectToSocket: the server will open the socket identified by the socket name * @li ConnectClientsOnly: only connections through createClient are possible **/ enum class StartMode { ConnectToSocket, ConnectClientsOnly }; void start(StartMode mode = StartMode::ConnectToSocket); void terminate(); /** * Starts the event loop for the server socket. * This method should only be used if start() is used before creating the * QCoreApplication. In that case start() cannot fully setup the event processing * and the loop needs to be started after the QCoreApplication got created. * @see start * @see dispatchEvents **/ void startLoop(); /** * Dispatches pending events in a blocking way. May only be used if the Display is * created and started before the QCoreApplication is created. Once the QCoreApplication * is created and the event loop is started this method delegates to the normal dispatch * handling. * @see startLoop **/ void dispatchEvents(int msecTimeout = -1); /** * Create a client for the given file descriptor. * * The client is created as if it connected through the normal server * socket. This method can be used to create a connection bypassing the * normal socket connection. It's recommended to use together with * socketpair and pass the other side of the socket to the client. * * @param fd The file descriptor for the socket to the client * @returns The new ClientConnection or @c null on failure. **/ ClientConnection *createClient(int fd); operator wl_display*(); operator wl_display*() const; bool isRunning() const; OutputInterface *createOutput(QObject *parent = nullptr); void removeOutput(OutputInterface *output); QList outputs() const; OutputDeviceInterface *createOutputDevice(QObject *parent = nullptr); void removeOutputDevice(OutputDeviceInterface *output); QList outputDevices() const; CompositorInterface *createCompositor(QObject *parent = nullptr); void createShm(); ShellInterface *createShell(QObject *parent = nullptr); SeatInterface *createSeat(QObject *parent = nullptr); /** * @returns All SeatInterface currently managed on the Display. * @since 5.6 **/ QVector seats() const; SubCompositorInterface *createSubCompositor(QObject *parent = nullptr); DataDeviceManagerInterface *createDataDeviceManager(QObject *parent = nullptr); OutputManagementInterface *createOutputManagement(QObject *parent = nullptr); PlasmaShellInterface *createPlasmaShell(QObject *parent = nullptr); PlasmaWindowManagementInterface *createPlasmaWindowManagement(QObject *parent = nullptr); QtSurfaceExtensionInterface *createQtSurfaceExtension(QObject *parent = nullptr); IdleInterface *createIdle(QObject *parent = nullptr); FakeInputInterface *createFakeInput(QObject *parent = nullptr); ShadowManagerInterface *createShadowManager(QObject *parent = nullptr); BlurManagerInterface *createBlurManager(QObject *parent = nullptr); ContrastManagerInterface *createContrastManager(QObject *parent = nullptr); SlideManagerInterface *createSlideManager(QObject *parent = nullptr); DpmsManagerInterface *createDpmsManager(QObject *parent = nullptr); /** * @since 5.6 **/ ServerSideDecorationManagerInterface *createServerSideDecorationManager(QObject *parent = nullptr); /** * Create the text input manager in interface @p version. * @returns The created manager object * @since 5.23 **/ TextInputManagerInterface *createTextInputManager(const TextInputInterfaceVersion &version, QObject *parent = nullptr); /** * Creates the XdgShell in interface @p version. * * @since 5.25 **/ XdgShellInterface *createXdgShell(const XdgShellInterfaceVersion &version, QObject *parent = nullptr); /** * Creates the RelativePointerManagerInterface in interface @p version * * @returns The created manager object * @since 5.28 **/ RelativePointerManagerInterface *createRelativePointerManager(const RelativePointerInterfaceVersion &version, QObject *parent = nullptr); /** * Creates the PointerGesturesInterface in interface @p version * * @returns The created manager object * @since 5.29 **/ PointerGesturesInterface *createPointerGestures(const PointerGesturesInterfaceVersion &version, QObject *parent = nullptr); /** * Creates the PointerConstraintsInterface in interface @p version * * @returns The created manager object * @since 5.29 **/ PointerConstraintsInterface *createPointerConstraints(const PointerConstraintsInterfaceVersion &version, QObject *parent = nullptr); /** * Creates the XdgForeignInterface in interface @p version * * @returns The created manager object * @since 5.40 **/ XdgForeignInterface *createXdgForeignInterface(QObject *parent = nullptr); /** * Creates the IdleInhibitManagerInterface in interface @p version. * * @returns The created manager object * @since 5.41 **/ IdleInhibitManagerInterface *createIdleInhibitManager(const IdleInhibitManagerInterfaceVersion &version, QObject *parent = nullptr); /** * Creates the AppMenuManagerInterface in interface @p version. * * @returns The created manager object * @since 5.42 **/ AppMenuManagerInterface *createAppMenuManagerInterface(QObject *parent = nullptr); /** * Creates the ServerSideDecorationPaletteManagerInterface in interface @p version. * * @returns The created manager object * @since 5.42 **/ 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. * If there is no ClientConnection yet for the given @p client, it will be created. * @param client The native client for which the ClientConnection is retrieved * @return The ClientConnection for the given native client **/ ClientConnection *getConnection(wl_client *client); QVector connections() const; /** * Set the EGL @p display for this Wayland display. * The EGLDisplay can only be set once and must be alive as long as the Wayland display * is alive. The user should have set up the binding between the EGLDisplay and the * Wayland display prior to calling this method. * * @see eglDisplay * @since 5.3 **/ void setEglDisplay(void *display); /** * @returns the EGLDisplay used for this Wayland display or EGL_NO_DISPLAY if not set. * @see setEglDisplay * @since 5.3 **/ void *eglDisplay() const; Q_SIGNALS: void socketNameChanged(const QString&); void runningChanged(bool); void aboutToTerminate(); void clientConnected(KWayland::Server::ClientConnection*); void clientDisconnected(KWayland::Server::ClientConnection*); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/server/drm_fourcc.h b/src/server/drm_fourcc.h new file mode 100644 index 0000000..70d492a --- /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.cpp b/src/server/linuxdmabuf_v1_interface.cpp new file mode 100644 index 0000000..c5fa147 --- /dev/null +++ b/src/server/linuxdmabuf_v1_interface.cpp @@ -0,0 +1,482 @@ +/******************************************************************** +Copyright © 2018 Fredrik Höglund + +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 . +*********************************************************************/ + +#include +#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 + +#include +#include + +#include + + +namespace KWayland +{ + +namespace Server +{ + + +class LinuxDmabufParams +{ +public: + LinuxDmabufParams(LinuxDmabufUnstableV1Interface *dmabufInterface, wl_client *client, uint32_t version, uint32_t id); + ~LinuxDmabufParams(); + + template + void postError(uint32_t code, const char *msg, Args... args) { + wl_resource_post_error(m_resource, code, msg, args...); + } + + 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 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(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) { + postError(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, + "params was already used to create a wl_buffer"); + return; + } + + if (m_planeCount == 0) { + postError(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; + + postError(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, + "no dmabuf has been added for plane %i", i); + return; + } + + if (width < 1 || height < 1) { + postError(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) { + postError(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) { + postError(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) { + postError(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) { + postError(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) { + postError(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + "invalid buffer stride or height for plane %i", i); + return; + } + } + + + // Import the buffer + // ----------------- + QVector 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(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. + postError(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) { + postError(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()) { + postError(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) { + postError(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(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(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(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 formats = bridge->supportedFormats(); + for (uint32_t format : formats) { + QVector 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(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(d.data()); +} + + +} // namespace Server +} // namespace Wayland diff --git a/src/server/linuxdmabuf_v1_interface.h b/src/server/linuxdmabuf_v1_interface.h new file mode 100644 index 0000000..ecbd82d --- /dev/null +++ b/src/server/linuxdmabuf_v1_interface.h @@ -0,0 +1,181 @@ +/******************************************************************** +Copyright © 2018 Fredrik Höglund + +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 . +*********************************************************************/ + +#ifndef WAYLAND_SERVER_LINUXDMABUF_INTERFACE_H +#define WAYLAND_SERVER_LINUXDMABUF_INTERFACE_H + +#include +#include "global.h" +#include "resource.h" + +#include + + +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 the 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 supportedFormats() const = 0; + + /** + * Returns the layout-modifiers supported for the given DRM format code. + */ + virtual QVector 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 &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