diff --git a/CMakeLists.txt b/CMakeLists.txt index 8013a6c..8ec127e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ include(FeatureSummary) find_package(Qt5 REQUIRED COMPONENTS Core DBus Widgets X11Extras) find_package(ECM 1.7.0 NO_MODULE REQUIRED) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(KDEInstallDirs) include(KDECMakeSettings) @@ -29,6 +29,39 @@ find_package(KF5 REQUIRED COMPONENTS XmlGui ) +find_package(KF5Wayland CONFIG) +set_package_properties(KF5Wayland PROPERTIES DESCRIPTION "KDE Frameworks Wayland integration package" + URL "https://github.com/KDE/kwayland" + TYPE OPTIONAL + PURPOSE "Required for wayland integration with KWin server" + ) + +find_package(epoxy) +set_package_properties(epoxy PROPERTIES DESCRIPTION "libepoxy" + URL "http://github.com/anholt/libepoxy" + TYPE OPTIONAL + PURPOSE "OpenGL dispatch library for GBM backend" + ) + +find_package(Libdrm) +set_package_properties(Libdrm PROPERTIES DESCRIPTION "Libdrm" + TYPE OPTIONAL + PURPOSE "Required for GBM access to compositor buffers on Wayland" + ) + +set(HAVE_DRM FALSE) +if(KF5Wayland_FOUND AND Libdrm_FOUND AND epoxy_FOUND) + set(HAVE_DRM TRUE) +endif() + +find_package(gbm) +set_package_properties(gbm PROPERTIES TYPE OPTIONAL PURPOSE "Required for egl ouput of drm backend.") +set(HAVE_GBM FALSE) +if(HAVE_DRM AND gbm_FOUND) + set(HAVE_GBM TRUE) +endif() + + find_package(X11 REQUIRED) if(WIN32) diff --git a/cmake/modules/FindLibdrm.cmake b/cmake/modules/FindLibdrm.cmake new file mode 100644 index 0000000..ebaa87d --- /dev/null +++ b/cmake/modules/FindLibdrm.cmake @@ -0,0 +1,126 @@ +#.rst: +# FindLibdrm +# ------- +# +# Try to find libdrm on a Unix system. +# +# This will define the following variables: +# +# ``Libdrm_FOUND`` +# True if (the requested version of) libdrm is available +# ``Libdrm_VERSION`` +# The version of libdrm +# ``Libdrm_LIBRARIES`` +# This can be passed to target_link_libraries() instead of the ``Libdrm::Libdrm`` +# target +# ``Libdrm_INCLUDE_DIRS`` +# This should be passed to target_include_directories() if the target is not +# used for linking +# ``Libdrm_DEFINITIONS`` +# This should be passed to target_compile_options() if the target is not +# used for linking +# +# If ``Libdrm_FOUND`` is TRUE, it will also define the following imported target: +# +# ``Libdrm::Libdrm`` +# The libdrm library +# +# In general we recommend using the imported target, as it is easier to use. +# Bear in mind, however, that if the target is in the link interface of an +# exported library, it must be made available by the package config file. + +#============================================================================= +# Copyright 2014 Alex Merry +# Copyright 2014 Martin Gräßlin +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +if(CMAKE_VERSION VERSION_LESS 2.8.12) + message(FATAL_ERROR "CMake 2.8.12 is required by FindLibdrm.cmake") +endif() +if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12) + message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use FindLibdrm.cmake") +endif() + +if(NOT WIN32) + # Use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + find_package(PkgConfig) + pkg_check_modules(PKG_Libdrm QUIET libdrm) + + set(Libdrm_DEFINITIONS ${PKG_Libdrm_CFLAGS_OTHER}) + set(Libdrm_VERSION ${PKG_Libdrm_VERSION}) + + find_path(Libdrm_INCLUDE_DIR + NAMES + xf86drm.h + HINTS + ${PKG_Libdrm_INCLUDE_DIRS} + ) + find_library(Libdrm_LIBRARY + NAMES + drm + HINTS + ${PKG_Libdrm_LIBRARY_DIRS} + ) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Libdrm + FOUND_VAR + Libdrm_FOUND + REQUIRED_VARS + Libdrm_LIBRARY + Libdrm_INCLUDE_DIR + VERSION_VAR + Libdrm_VERSION + ) + + if(Libdrm_FOUND AND NOT TARGET Libdrm::Libdrm) + add_library(Libdrm::Libdrm UNKNOWN IMPORTED) + set_target_properties(Libdrm::Libdrm PROPERTIES + IMPORTED_LOCATION "${Libdrm_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${Libdrm_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${Libdrm_INCLUDE_DIR}" + INTERFACE_INCLUDE_DIRECTORIES "${Libdrm_INCLUDE_DIR}/libdrm" + ) + endif() + + mark_as_advanced(Libdrm_LIBRARY Libdrm_INCLUDE_DIR) + + # compatibility variables + set(Libdrm_LIBRARIES ${Libdrm_LIBRARY}) + set(Libdrm_INCLUDE_DIRS ${Libdrm_INCLUDE_DIR}) + set(Libdrm_VERSION_STRING ${Libdrm_VERSION}) + +else() + message(STATUS "FindLibdrm.cmake cannot find libdrm on Windows systems.") + set(Libdrm_FOUND FALSE) +endif() + +include(FeatureSummary) +set_package_properties(Libdrm PROPERTIES + URL "https://wiki.freedesktop.org/dri/" + DESCRIPTION "Userspace interface to kernel DRM services." +) diff --git a/cmake/modules/Findepoxy.cmake b/cmake/modules/Findepoxy.cmake new file mode 100644 index 0000000..dfd8c3c --- /dev/null +++ b/cmake/modules/Findepoxy.cmake @@ -0,0 +1,56 @@ +# - Try to find libepoxy +# Once done this will define +# +# epoxy_FOUND - System has libepoxy +# epoxy_LIBRARY - The libepoxy library +# epoxy_INCLUDE_DIR - The libepoxy include dir +# epoxy_DEFINITIONS - Compiler switches required for using libepoxy +# epoxy_HAS_GLX - Whether GLX support is available + +# Copyright (c) 2014 Fredrik Höglund +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +if (NOT WIN32) + find_package(PkgConfig) + pkg_check_modules(PKG_epoxy QUIET epoxy) + + set(epoxy_DEFINITIONS ${PKG_epoxy_CFLAGS}) + + find_path(epoxy_INCLUDE_DIR NAMES epoxy/gl.h HINTS ${PKG_epoxy_INCLUDEDIR} ${PKG_epoxy_INCLUDE_DIRS}) + find_library(epoxy_LIBRARY NAMES epoxy HINTS ${PKG_epoxy_LIBDIR} ${PKG_epoxy_LIBRARY_DIRS}) + find_file(epoxy_GLX_HEADER NAMES epoxy/glx.h HINTS ${epoxy_INCLUDE_DIR}) + + if (epoxy_GLX_HEADER STREQUAL "epoxy_GLX_HEADER-NOTFOUND") + set(epoxy_HAS_GLX FALSE CACHE BOOL "whether glx is available") + else () + set(epoxy_HAS_GLX TRUE CACHE BOOL "whether glx is available") + endif() + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(epoxy DEFAULT_MSG epoxy_LIBRARY epoxy_INCLUDE_DIR) + + mark_as_advanced(epoxy_INCLUDE_DIR epoxy_LIBRARY epoxy_HAS_GLX) +endif() diff --git a/cmake/modules/Findgbm.cmake b/cmake/modules/Findgbm.cmake new file mode 100644 index 0000000..6dfc895 --- /dev/null +++ b/cmake/modules/Findgbm.cmake @@ -0,0 +1,125 @@ +#.rst: +# Findgbm +# ------- +# +# Try to find gbm on a Unix system. +# +# This will define the following variables: +# +# ``gbm_FOUND`` +# True if (the requested version of) gbm is available +# ``gbm_VERSION`` +# The version of gbm +# ``gbm_LIBRARIES`` +# This can be passed to target_link_libraries() instead of the ``gbm::gbm`` +# target +# ``gbm_INCLUDE_DIRS`` +# This should be passed to target_include_directories() if the target is not +# used for linking +# ``gbm_DEFINITIONS`` +# This should be passed to target_compile_options() if the target is not +# used for linking +# +# If ``gbm_FOUND`` is TRUE, it will also define the following imported target: +# +# ``gbm::gbm`` +# The gbm library +# +# In general we recommend using the imported target, as it is easier to use. +# Bear in mind, however, that if the target is in the link interface of an +# exported library, it must be made available by the package config file. + +#============================================================================= +# Copyright 2014 Alex Merry +# Copyright 2014 Martin Gräßlin +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +if(CMAKE_VERSION VERSION_LESS 2.8.12) + message(FATAL_ERROR "CMake 2.8.12 is required by Findgbm.cmake") +endif() +if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12) + message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use Findgbm.cmake") +endif() + +if(NOT WIN32) + # Use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + find_package(PkgConfig) + pkg_check_modules(PKG_gbm QUIET gbm) + + set(gbm_DEFINITIONS ${PKG_gbm_CFLAGS_OTHER}) + set(gbm_VERSION ${PKG_gbm_VERSION}) + + find_path(gbm_INCLUDE_DIR + NAMES + gbm.h + HINTS + ${PKG_gbm_INCLUDE_DIRS} + ) + find_library(gbm_LIBRARY + NAMES + gbm + HINTS + ${PKG_gbm_LIBRARY_DIRS} + ) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(gbm + FOUND_VAR + gbm_FOUND + REQUIRED_VARS + gbm_LIBRARY + gbm_INCLUDE_DIR + VERSION_VAR + gbm_VERSION + ) + + if(gbm_FOUND AND NOT TARGET gbm::gbm) + add_library(gbm::gbm UNKNOWN IMPORTED) + set_target_properties(gbm::gbm PROPERTIES + IMPORTED_LOCATION "${gbm_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${gbm_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${gbm_INCLUDE_DIR}" + ) + endif() + + mark_as_advanced(gbm_LIBRARY gbm_INCLUDE_DIR) + + # compatibility variables + set(gbm_LIBRARIES ${gbm_LIBRARY}) + set(gbm_INCLUDE_DIRS ${gbm_INCLUDE_DIR}) + set(gbm_VERSION_STRING ${gbm_VERSION}) + +else() + message(STATUS "Findgbm.cmake cannot find gbm on Windows systems.") + set(gbm_FOUND FALSE) +endif() + +include(FeatureSummary) +set_package_properties(gbm PROPERTIES + URL "http://www.mesa3d.org" + DESCRIPTION "Mesa gbm library." +) diff --git a/framebuffers/CMakeLists.txt b/framebuffers/CMakeLists.txt index 32f42c3..97c7a51 100644 --- a/framebuffers/CMakeLists.txt +++ b/framebuffers/CMakeLists.txt @@ -1,3 +1,5 @@ add_subdirectory (qt) add_subdirectory (x11) - +if(HAVE_GBM) + add_subdirectory (gbm) +endif() diff --git a/framebuffers/gbm/CMakeLists.txt b/framebuffers/gbm/CMakeLists.txt new file mode 100644 index 0000000..49afb37 --- /dev/null +++ b/framebuffers/gbm/CMakeLists.txt @@ -0,0 +1,29 @@ +include_directories (${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} +) + +set (krfb_framebuffer_gbm_SRCS + logging.cpp + gbmframebuffer.cpp + gbmframebufferplugin.cpp +) + +add_library(krfb_framebuffer_gbm + MODULE + ${krfb_framebuffer_gbm_SRCS} +) + +target_link_libraries (krfb_framebuffer_gbm + Qt5::Core + Qt5::Gui + KF5::CoreAddons + KF5::WaylandClient + ${epoxy_LIBRARY} + Libdrm::Libdrm + gbm::gbm + krfbprivate +) + +install (TARGETS krfb_framebuffer_gbm + DESTINATION ${PLUGIN_INSTALL_DIR}/krfb +) diff --git a/framebuffers/gbm/gbmframebuffer.cpp b/framebuffers/gbm/gbmframebuffer.cpp new file mode 100644 index 0000000..9561982 --- /dev/null +++ b/framebuffers/gbm/gbmframebuffer.cpp @@ -0,0 +1,330 @@ +/* This file is part of the KDE project + Copyright (C) 2016 Oleg Chernovskiy + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#include "gbmframebuffer.h" +#include "logging.h" +#include "gbmframebuffer.moc" +// KWayland +#include +#include +#include +// Qt +#include +#include +#include +// system +#include +#include +#include +#include +#include + +static QString formatGLError(GLenum err) +{ + switch(err) { + case GL_NO_ERROR: return QStringLiteral("GL_NO_ERROR"); + case GL_INVALID_ENUM: return QStringLiteral("GL_INVALID_ENUM"); + case GL_INVALID_VALUE: return QStringLiteral("GL_INVALID_VALUE"); + case GL_INVALID_OPERATION: return QStringLiteral("GL_INVALID_OPERATION"); + case GL_STACK_OVERFLOW: return QStringLiteral("GL_STACK_OVERFLOW"); + case GL_STACK_UNDERFLOW: return QStringLiteral("GL_STACK_UNDERFLOW"); + case GL_OUT_OF_MEMORY: return QStringLiteral("GL_OUT_OF_MEMORY"); + default: return QLatin1String("0x") + QString::number(err, 16); + } +} + +GbmFrameBuffer::GbmFrameBuffer(WId id, QObject *parent) + : FrameBuffer(id, parent) +{ + + // TODO: check out the case when new resolution was applied on KWin side + // TODO: check possibility of multi-screen configuration + QSize size = QApplication::screens().first()->size(); + m_img = new QImage(size, QImage::Format_ARGB32); + fb = reinterpret_cast(m_img->bits()); + + // open DRM device + setupDrm(); + + // get EGL client extensions + initClientEglExtensions(); + + // initialize EGL context and display + initEgl(); + + // get KWin connection + initWaylandConnection(); +} + +void GbmFrameBuffer::setupDrm() +{ + m_drmFd = open("/dev/dri/card0", O_RDWR); + m_gbmDevice = gbm_create_device(m_drmFd); + if(!m_gbmDevice) { + qCCritical(KRFB_GBM) << "Cannot create GBM device:" << strerror(errno); + m_valid = false; + return; + } +} + +// cloned from KWin AbstractEglBackend +void GbmFrameBuffer::initClientEglExtensions() +{ + if(!m_valid) + return; + + // Get the list of client extensions + const char* clientExtensionsCString = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + const QByteArray clientExtensionsString = QByteArray::fromRawData(clientExtensionsCString, qstrlen(clientExtensionsCString)); + if (clientExtensionsString.isEmpty()) { + // If eglQueryString() returned NULL, the implementation doesn't support + // EGL_EXT_client_extensions. Expect an EGL_BAD_DISPLAY error. + qCCritical(KRFB_GBM) << "No client extensions defined!" << formatGLError(eglGetError()); + m_valid = false; + } + + m_egl.extensions = clientExtensionsString.split(' '); +} + +void GbmFrameBuffer::initEgl() +{ + if(!m_valid) + return; + + // Use eglGetPlatformDisplayEXT() to get the display pointer + // if the implementation supports it. + if (!m_egl.extensions.contains(QByteArrayLiteral("EGL_EXT_platform_base")) || + !m_egl.extensions.contains(QByteArrayLiteral("EGL_MESA_platform_gbm"))) { + qCCritical(KRFB_GBM) << "One of required EGL extensions is missing"; + m_valid = false; + return; + } + + m_egl.display = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA, m_gbmDevice, nullptr); + if (m_egl.display == EGL_NO_DISPLAY) { + qCCritical(KRFB_GBM) << "Error during obtaining EGL display:" << formatGLError(eglGetError()); + m_valid = false; + return; + } + + EGLint major, minor; + if (eglInitialize(m_egl.display, &major, &minor) == EGL_FALSE) { + qCCritical(KRFB_GBM) << "Error during eglInitialize:" << formatGLError(eglGetError()); + m_valid = false; + return; + } + + if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) { + qCCritical(KRFB_GBM) << "bind OpenGL API failed"; + m_valid = false; + return; + } + qCDebug(KRFB_GBM) << "Egl Initialize succeeded"; + qCDebug(KRFB_GBM) << QString("EGL version: %1.%2").arg(major).arg(minor); + + m_egl.context = eglCreateContext(m_egl.display, nullptr, EGL_NO_CONTEXT, nullptr); + if(m_egl.context == EGL_NO_CONTEXT) { + qCCritical(KRFB_GBM) << "Couldn't create EGL context:" << formatGLError(eglGetError()); + m_valid = false; + } +} + +void GbmFrameBuffer::initWaylandConnection() +{ + if(!m_valid) + return; + + using namespace KWayland::Client; + ConnectionThread *conn = ConnectionThread::fromApplication(this); + if(!conn) { // trying to instantiate wayland not having a wayland platform? + m_valid = false; + return; + } + + // what do we do if server dies? + // since we use foreign connection, the whole application will be disconnected, + // not only plugin, let's assume application will die anyway + + Registry *registry = new Registry(this); + registry->create(conn); + connect(registry, &Registry::remoteAccessManagerAnnounced, this, + [this, registry] (qint32 name, qint32 version) { + m_interface = registry->createRemoteAccessManager(name, version, this); + connect(m_interface, &RemoteAccessManager::bufferReady, this, &GbmFrameBuffer::obtainBuffer); + } + ); + registry->setup(); +} + +void GbmFrameBuffer::obtainBuffer(KWayland::Client::RemoteBuffer *rbuf) +{ + using namespace KWayland::Client; + connect(rbuf, &RemoteBuffer::paramsObtained, this, + [this, rbuf] (qint32 fd, quint32 width, quint32 height, quint32 stride, quint32 format) { + updateHandle(fd, width, height, stride, format); + // deleteLater() is not working due to QTBUG-18434 (or similar) + // TODO: try with Qt 5.8.0 + delete rbuf; + } + ); +} + +void GbmFrameBuffer::updateHandle(qint32 gbmHandle, quint32 width, quint32 height, quint32 stride, quint32 format) +{ + qCDebug(KRFB_GBM) << QString("Incoming GBM fd %1, %2x%3, stride %4, fourcc 0x%5") + .arg(gbmHandle).arg(width).arg(height).arg(stride).arg(QString::number(format, 16)); + + if(!gbm_device_is_format_supported(m_gbmDevice, format, GBM_BO_USE_SCANOUT)) { + qCCritical(KRFB_GBM) << "GBM format is not supported by device!"; + return; + } + + if(this->width() != (int) width || this->height() != (int) height) { + qCCritical(KRFB_GBM) << QString("Size of GBM buffer (%3x%4) is not equal to expected (%1x%2)!") + .arg(this->width()).arg(this->height()).arg(width).arg(height); + return; + } + + // import GBM buffer that was passed from KWin + gbm_import_fd_data importInfo = {gbmHandle, width, height, stride, format}; + gbm_bo *imported = gbm_bo_import(m_gbmDevice, GBM_BO_IMPORT_FD, &importInfo, GBM_BO_USE_SCANOUT); + if(!imported) { + qCCritical(KRFB_GBM) << "Cannot import passed GBM fd:" << strerror(errno); + return; + } + + // bind context to render thread + eglMakeCurrent(m_egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, m_egl.context); + + // create EGL image from imported BO + EGLImageKHR image = eglCreateImageKHR(m_egl.display, NULL, EGL_NATIVE_PIXMAP_KHR, imported, NULL); + if (image == EGL_NO_IMAGE_KHR) { + qCCritical(KRFB_GBM) << "Error creating EGLImageKHR" << formatGLError(glGetError()); + return; + } + // create GL 2D texture for framebuffer + GLuint texture; + glGenTextures(1, &texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, texture); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); + + // bind framebuffer to copy pixels from + GLuint framebuffer; + glGenFramebuffers(1, &framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); + const GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + qCCritical(KRFB_GBM) << "glCheckFramebufferStatus failed:" << formatGLError(glGetError()); + glDeleteTextures(1, &texture); + glDeleteFramebuffers(1, &framebuffer); + eglDestroyImageKHR(m_egl.display, image); + return; + } + + glViewport(0, 0, width, height); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_img->bits()); + tiles.append(m_img->rect()); + + // unbind all + glDeleteTextures(1, &texture); + glDeleteFramebuffers(1, &framebuffer); + eglDestroyImageKHR(m_egl.display, image); + + // from this point buffer object can be safely freed + close(gbmHandle); +} + +GbmFrameBuffer::~GbmFrameBuffer() +{ + fb = nullptr; + + delete m_img; + m_img = nullptr; + + if(m_egl.context != EGL_NO_CONTEXT) { + eglDestroyContext(m_egl.display, m_egl.context); + } + + if(m_gbmDevice) { + gbm_device_destroy(m_gbmDevice); + } + + if(m_drmFd) { + close(m_drmFd); + } +} + +int GbmFrameBuffer::depth() +{ + return m_img->depth(); +} + +int GbmFrameBuffer::height() +{ + return m_img->height(); +} + +int GbmFrameBuffer::width() +{ + return m_img->width(); +} + +void GbmFrameBuffer::getServerFormat(rfbPixelFormat &format) +{ + format.bitsPerPixel = 32; + format.depth = 32; + format.trueColour = true; + + format.bigEndian = false; + + // GL images have different shift + format.redShift = 0; + format.greenShift = 8; + format.blueShift = 16; + format.redMax = 0xff; + format.greenMax = 0xff; + format.blueMax = 0xff; +} + +void GbmFrameBuffer::updateFrameBuffer() +{ + // do nothing +} + +int GbmFrameBuffer::paddedWidth() +{ + return m_img->bytesPerLine(); +} + +void GbmFrameBuffer::startMonitor() +{ + // not needed - we get events as a signals from Wayland interface + // no other updates are possible +} + +void GbmFrameBuffer::stopMonitor() +{ + +} diff --git a/framebuffers/gbm/gbmframebuffer.h b/framebuffers/gbm/gbmframebuffer.h new file mode 100644 index 0000000..ab694f2 --- /dev/null +++ b/framebuffers/gbm/gbmframebuffer.h @@ -0,0 +1,85 @@ +/* This file is part of the KDE project + Copyright (C) 2016 Oleg Chernovskiy + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KRFB_FRAMEBUFFER_GBM_GBMFRAMEBUFFER_H +#define KRFB_FRAMEBUFFER_GBM_GBMFRAMEBUFFER_H + +#include "framebuffer.h" +// Qt +#include +// system +#include +#include +#include + +namespace KWayland +{ +namespace Client +{ +class RemoteAccessManager; +class RemoteBuffer; +} +} + +/** + @author Oleg Chernovskiy +*/ +class GbmFrameBuffer : public FrameBuffer +{ + Q_OBJECT +public: + explicit GbmFrameBuffer(WId id, QObject *parent = 0); + + ~GbmFrameBuffer(); + + int depth() override; + int height() override; + int width() override; + int paddedWidth() override; + void getServerFormat(rfbPixelFormat &format) override; + void startMonitor() override; + void stopMonitor() override; + inline bool isValid() { return m_valid; }; + +public Q_SLOTS: + void updateFrameBuffer(); + +private: + void initWaylandConnection(); + void setupDrm(); + void initClientEglExtensions(); + void initEgl(); + void obtainBuffer(KWayland::Client::RemoteBuffer *rbuf); + void updateHandle(qint32 gbmHandle, quint32 width, quint32 height, quint32 stride, quint32 format); + + qint32 m_drmFd = 0; // for GBM buffer mmap + gbm_device *m_gbmDevice = nullptr; // for passed GBM buffer retrieval + struct { + QList extensions; + EGLDisplay display = EGL_NO_DISPLAY; + EGLContext context = EGL_NO_CONTEXT; + } m_egl; + + KWayland::Client::RemoteAccessManager *m_interface = nullptr; + QImage *m_img; + + bool m_valid = true; +}; + +#endif diff --git a/framebuffers/gbm/gbmframebufferplugin.cpp b/framebuffers/gbm/gbmframebufferplugin.cpp new file mode 100644 index 0000000..3c1fce1 --- /dev/null +++ b/framebuffers/gbm/gbmframebufferplugin.cpp @@ -0,0 +1,50 @@ +/* This file is part of the KDE project + Copyright (C) 2016 Oleg Chernovskiy + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "gbmframebufferplugin.h" + +#include "gbmframebuffer.h" + +#include + +K_PLUGIN_FACTORY_WITH_JSON(GbmFrameBufferPluginFactory, "krfb_framebuffer_gbm.json", + registerPlugin();) + +GbmFrameBufferPlugin::GbmFrameBufferPlugin(QObject *parent, const QVariantList &args) + : FrameBufferPlugin(parent, args) +{ +} + +GbmFrameBufferPlugin::~GbmFrameBufferPlugin() +{ +} + +FrameBuffer *GbmFrameBufferPlugin::frameBuffer(WId id) +{ + auto p = new GbmFrameBuffer(id); + if (!p->isValid()) { + delete p; + return nullptr; + } + + return p; +} + +#include "gbmframebufferplugin.moc" + diff --git a/framebuffers/gbm/gbmframebufferplugin.h b/framebuffers/gbm/gbmframebufferplugin.h new file mode 100644 index 0000000..07c1b2f --- /dev/null +++ b/framebuffers/gbm/gbmframebufferplugin.h @@ -0,0 +1,45 @@ +/* This file is part of the KDE project + Copyright (C) 2016 Oleg Chernovskiy + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KRFB_FRAMEBUFFER_GBM_GBMFRAMEBUFFERPLUGIN_H +#define KRFB_FRAMEBUFFER_GBM_GBMFRAMEBUFFERPLUGIN_H + +#include "framebufferplugin.h" + +#include + +class FrameBuffer; + +class GbmFrameBufferPlugin : public FrameBufferPlugin +{ + Q_OBJECT + +public: + GbmFrameBufferPlugin(QObject *parent, const QVariantList &args); + virtual ~GbmFrameBufferPlugin(); + + FrameBuffer *frameBuffer(WId id) override; + +private: + Q_DISABLE_COPY(GbmFrameBufferPlugin) +}; + + +#endif // Header guard + diff --git a/framebuffers/gbm/krfb_framebuffer_gbm.json b/framebuffers/gbm/krfb_framebuffer_gbm.json new file mode 100644 index 0000000..db280c8 --- /dev/null +++ b/framebuffers/gbm/krfb_framebuffer_gbm.json @@ -0,0 +1,62 @@ +{ + "Encoding": "UTF-8", + "KPlugin": { + "Description": "GBM based Framebuffer for KRfb.", + "Description[ca@valencia]": "«Framebuffer» basat en GBM per al KRfb.", + "Description[ca]": "«Framebuffer» basat en GBM per al KRfb.", + "Description[cs]": "Framebuffer založený na GBM pro KRfb.", + "Description[de]": "GBM-basierter Framebuffer für KRfb", + "Description[es]": "Framebuffer basado en GBM para KRfb.", + "Description[fi]": "QT-perustainen Kehyspuskuri KRfb:lle", + "Description[gl]": "Framebuffer baseado en GBM para KRfb.", + "Description[it]": "Framebuffer basato su GBM per KRfb.", + "Description[ko]": "KRfb용 GBM 기반 프레임버퍼입니다.", + "Description[nl]": "Op GBM gebaseerd framebuffer voor KRfb.", + "Description[pl]": "Bufor ramki na podstawie GBM dla KRfb.", + "Description[pt]": "'Framebuffer' baseado no GBM para o KRfb.", + "Description[pt_BR]": "Framebuffer baseado no GBM para o KRfb.", + "Description[sk]": "Framebuffer založený na GBM pre KRfb.", + "Description[sl]": "Slikovni medpomnilnik za KRfb, ki temelji na GBM", + "Description[sr@ijekavian]": "Кадробафер за КРФБ на основу КуТ‑у", + "Description[sr@ijekavianlatin]": "Kadrobafer za KRFB na osnovu GBM‑u", + "Description[sr@latin]": "Kadrobafer za KRFB na osnovu GBM‑u", + "Description[sr]": "Кадробафер за КРФБ на основу КуТ‑у", + "Description[sv]": "X11-rambuffert för Krfb.", + "Description[uk]": "Заснований на GBM буфер кадрів для KRfb.", + "Description[x-test]": "xxGBM based Framebuffer for KRfb.xx", + "Description[zh_CN]": "KRfb 的基于 GBM 的帧缓冲。", + "EnabledByDefault": true, + "Id": "gbm", + "License": "GPL", + "Name": "GBM Framebuffer for KRfb", + "Name[ast]": "Framebuffer GBM pa KRfb", + "Name[ca@valencia]": "«Framebuffer» GBM per al KRfb.", + "Name[ca]": "«Framebuffer» GBM per al KRfb.", + "Name[cs]": "GBM Framebuffer pro KRfb", + "Name[de]": "GBM-Framebuffer für KRfb", + "Name[es]": "Framebuffer de GBM para KRfb", + "Name[fi]": "QT-kehyspuskuri KRfb:lle", + "Name[gl]": "Framebuffer de GBM para KRfb", + "Name[it]": "Framebuffer GBM per KRfb", + "Name[ko]": "KRfb용 GBM 프레임버퍼", + "Name[nl]": "GBM-framebuffer voor KRfb", + "Name[pl]": "Bufor ramki GBM dla KRfb", + "Name[pt]": "'Framebuffer' do GBM para o KRfb", + "Name[pt_BR]": "Framebuffer do GBM para o KRfb", + "Name[sk]": "GBM Framebuffer pre KRfb", + "Name[sl]": "Slikovni medpomnilnik GBM za KRfb", + "Name[sr@ijekavian]": "КуТ‑ов кадробафер за КРФБ", + "Name[sr@ijekavianlatin]": "GBM‑ov kadrobafer za KRFB", + "Name[sr@latin]": "GBM‑ov kadrobafer za KRFB", + "Name[sr]": "КуТ‑ов кадробафер за КРФБ", + "Name[sv]": "X11-rambuffert för Krfb", + "Name[uk]": "Буфер кадрів на GBM для KRfb", + "Name[x-test]": "xxGBM Framebuffer for KRfbxx", + "Name[zh_CN]": "KRfb 的 GBM 帧缓冲", + "ServiceTypes": [ + "krfb/framebuffer" + ], + "Version": "0.1", + "Website": "http://www.kde.org" + } +} diff --git a/framebuffers/gbm/logging.cpp b/framebuffers/gbm/logging.cpp new file mode 100644 index 0000000..bc3556a --- /dev/null +++ b/framebuffers/gbm/logging.cpp @@ -0,0 +1,21 @@ +/* This file is part of the KDE project + Copyright (C) 2016 Oleg Chernovskiy + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "logging.h" +Q_LOGGING_CATEGORY(KRFB_GBM, "krfb-gbm", QtCriticalMsg) diff --git a/framebuffers/gbm/logging.h b/framebuffers/gbm/logging.h new file mode 100644 index 0000000..4059ccd --- /dev/null +++ b/framebuffers/gbm/logging.h @@ -0,0 +1,26 @@ +/* This file is part of the KDE project + Copyright (C) 2016 Oleg Chernovskiy + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KRFB_FRAMEBUFFER_GBM_LOGGING +#define KRFB_FRAMEBUFFER_GBM_LOGGING +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(KRFB_GBM) +#endif diff --git a/framebuffers/qt/qtframebufferplugin.cpp b/framebuffers/qt/qtframebufferplugin.cpp index 5068566..7a079a1 100644 --- a/framebuffers/qt/qtframebufferplugin.cpp +++ b/framebuffers/qt/qtframebufferplugin.cpp @@ -23,9 +23,10 @@ #include "qtframebuffer.h" #include +#include K_PLUGIN_FACTORY_WITH_JSON(QtFrameBufferPluginFactory, "krfb_framebuffer_qt.json", - registerPlugin();) + registerPlugin();) QtFrameBufferPlugin::QtFrameBufferPlugin(QObject *parent, const QVariantList &args) : FrameBufferPlugin(parent, args) @@ -38,6 +39,10 @@ QtFrameBufferPlugin::~QtFrameBufferPlugin() FrameBuffer *QtFrameBufferPlugin::frameBuffer(WId id) { + // works only under X11 + if(!QX11Info::isPlatformX11()) + return nullptr; + return new QtFrameBuffer(id); } diff --git a/framebuffers/x11/x11framebufferplugin.cpp b/framebuffers/x11/x11framebufferplugin.cpp index 4ebad8a..619f3c8 100644 --- a/framebuffers/x11/x11framebufferplugin.cpp +++ b/framebuffers/x11/x11framebufferplugin.cpp @@ -23,9 +23,10 @@ #include "x11framebuffer.h" #include +#include K_PLUGIN_FACTORY_WITH_JSON(X11FrameBufferPluginFactory, "krfb_framebuffer_x11.json", - registerPlugin();) + registerPlugin();) X11FrameBufferPlugin::X11FrameBufferPlugin(QObject *parent, const QVariantList &args) : FrameBufferPlugin(parent, args) @@ -38,6 +39,10 @@ X11FrameBufferPlugin::~X11FrameBufferPlugin() FrameBuffer *X11FrameBufferPlugin::frameBuffer(WId id) { + // works only under X11 + if(!QX11Info::isPlatformX11()) + return nullptr; + return new X11FrameBuffer(id); }