diff --git a/plugins/platforms/drm/CMakeLists.txt b/plugins/platforms/drm/CMakeLists.txt index 5328321cc..a857f47ff 100644 --- a/plugins/platforms/drm/CMakeLists.txt +++ b/plugins/platforms/drm/CMakeLists.txt @@ -1,33 +1,38 @@ set(DRM_SOURCES drm_backend.cpp drm_object.cpp drm_object_connector.cpp drm_object_crtc.cpp drm_object_plane.cpp drm_output.cpp drm_buffer.cpp drm_inputeventfilter.cpp logging.cpp scene_qpainter_drm_backend.cpp screens_drm.cpp ) if(HAVE_GBM) - set(DRM_SOURCES ${DRM_SOURCES} egl_gbm_backend.cpp drm_buffer_gbm.cpp gbm_surface.cpp) + set(DRM_SOURCES ${DRM_SOURCES} + egl_gbm_backend.cpp + drm_buffer_gbm.cpp + gbm_surface.cpp + remoteaccess_manager.cpp + ) endif() include_directories(${CMAKE_SOURCE_DIR}/platformsupport/scenes/opengl) add_library(KWinWaylandDrmBackend MODULE ${DRM_SOURCES}) target_link_libraries(KWinWaylandDrmBackend kwin Libdrm::Libdrm SceneQPainterBackend SceneOpenGLBackend) if(HAVE_GBM) target_link_libraries(KWinWaylandDrmBackend gbm::gbm) endif() install( TARGETS KWinWaylandDrmBackend DESTINATION ${PLUGIN_INSTALL_DIR}/org.kde.kwin.waylandbackends/ ) diff --git a/plugins/platforms/drm/drm_buffer_gbm.h b/plugins/platforms/drm/drm_buffer_gbm.h index 2b39952aa..b9b40e8e7 100644 --- a/plugins/platforms/drm/drm_buffer_gbm.h +++ b/plugins/platforms/drm/drm_buffer_gbm.h @@ -1,62 +1,67 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright 2017 Roman Gilg Copyright 2015 Martin Gräßlin 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 2 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. If not, see . *********************************************************************/ #ifndef KWIN_DRM_BUFFER_GBM_H #define KWIN_DRM_BUFFER_GBM_H #include "drm_buffer.h" #include struct gbm_bo; namespace KWin { class GbmSurface; class DrmSurfaceBuffer : public DrmBuffer { public: DrmSurfaceBuffer(int fd, const std::shared_ptr &surface); ~DrmSurfaceBuffer(); bool needsModeChange(DrmBuffer *b) const override { if (DrmSurfaceBuffer *sb = dynamic_cast(b)) { return hasBo() != sb->hasBo(); } else { return true; } } bool hasBo() const { return m_bo != nullptr; } + + gbm_bo* getBo() const { + return m_bo; + } + void releaseGbm() override; private: std::shared_ptr m_surface; gbm_bo *m_bo = nullptr; }; } #endif diff --git a/plugins/platforms/drm/drm_output.h b/plugins/platforms/drm/drm_output.h index 75b5103bc..9e87b2e30 100644 --- a/plugins/platforms/drm/drm_output.h +++ b/plugins/platforms/drm/drm_output.h @@ -1,218 +1,222 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin 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 2 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. If not, see . *********************************************************************/ #ifndef KWIN_DRM_OUTPUT_H #define KWIN_DRM_OUTPUT_H #include "drm_pointer.h" #include "drm_object.h" #include "drm_object_plane.h" #include #include #include #include #include #include #include namespace KWayland { namespace Server { class OutputInterface; class OutputDeviceInterface; class OutputChangeSet; class OutputManagementInterface; } } namespace KWin { class DrmBackend; class DrmBuffer; class DrmDumbBuffer; class DrmPlane; class DrmConnector; class DrmCrtc; class DrmOutput : public QObject { Q_OBJECT public: struct Edid { QByteArray eisaId; QByteArray monitorName; QByteArray serialNumber; QSize physicalSize; }; virtual ~DrmOutput(); void releaseGbm(); void showCursor(DrmDumbBuffer *buffer); void showCursor(); void hideCursor(); void updateCursor(); void moveCursor(const QPoint &globalPos); bool init(drmModeConnector *connector); bool present(DrmBuffer *buffer); void pageFlipped(); /** * Enable or disable the output. * This differs from setDpms as it also * removes the wl_output * The default is on */ void setEnabled(bool enabled); bool isEnabled() const; /** * This sets the changes and tests them against the DRM output */ void setChanges(KWayland::Server::OutputChangeSet *changeset); bool commitChanges(); QSize pixelSize() const; qreal scale() const; /* * The geometry of this output in global compositor co-ordinates (i.e scaled) */ QRect geometry() const; QString name() const; int currentRefreshRate() const; // These values are defined by the kernel enum class DpmsMode { On = DRM_MODE_DPMS_ON, Standby = DRM_MODE_DPMS_STANDBY, Suspend = DRM_MODE_DPMS_SUSPEND, Off = DRM_MODE_DPMS_OFF }; void setDpms(DpmsMode mode); bool isDpmsEnabled() const { // We care for current as well as pending mode in order to allow first present in AMS. return m_dpmsModePending == DpmsMode::On; } QByteArray uuid() const { return m_uuid; } QSize physicalSize() const; bool initCursor(const QSize &cursorSize); bool supportsTransformations() const; bool isInternal() const { return m_internal; } Qt::ScreenOrientation orientation() const { return m_orientation; } + const QPointer getWaylandInterface() const { + return m_waylandOutput; + } + Q_SIGNALS: void dpmsChanged(); void modeChanged(); private: friend class DrmBackend; friend class DrmCrtc; // TODO: For use of setModeLegacy. Remove later when we allow multiple connectors per crtc // and save the connector ids in the DrmCrtc instance. DrmOutput(DrmBackend *backend); bool presentAtomically(DrmBuffer *buffer); enum class AtomicCommitMode { Test, Real }; bool doAtomicCommit(AtomicCommitMode mode); bool presentLegacy(DrmBuffer *buffer); bool setModeLegacy(DrmBuffer *buffer); void initEdid(drmModeConnector *connector); void initDpms(drmModeConnector *connector); void initOutputDevice(drmModeConnector *connector); bool isCurrentMode(const drmModeModeInfo *mode) const; void initUuid(); void setGlobalPos(const QPoint &pos); void setScale(qreal scale); void initOutput(); bool initPrimaryPlane(); bool initCursorPlane(); void dpmsOnHandler(); void dpmsOffHandler(); bool dpmsAtomicOff(); bool atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable); void updateMode(int modeIndex); void transform(KWayland::Server::OutputDeviceInterface::Transform transform); void automaticRotation(); DrmBackend *m_backend; DrmConnector *m_conn = nullptr; DrmCrtc *m_crtc = nullptr; QPoint m_globalPos; qreal m_scale = 1; bool m_lastGbm = false; drmModeModeInfo m_mode; Edid m_edid; QPointer m_waylandOutput; QPointer m_waylandOutputDevice; QPointer m_changeset; KWin::ScopedDrmPointer<_drmModeProperty, &drmModeFreeProperty> m_dpms; DpmsMode m_dpmsMode = DpmsMode::On; DpmsMode m_dpmsModePending = DpmsMode::On; QByteArray m_uuid; uint32_t m_blobId = 0; DrmPlane* m_primaryPlane = nullptr; DrmPlane* m_cursorPlane = nullptr; QVector m_nextPlanesFlipList; bool m_pageFlipPending = false; bool m_dpmsAtomicOffPending = false; bool m_modesetRequested = true; QSize m_physicalSize; Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation; struct { Qt::ScreenOrientation orientation; drmModeModeInfo mode; DrmPlane::Transformations planeTransformations; QPoint globalPos; bool valid = false; } m_lastWorkingState; DrmDumbBuffer *m_cursor[2] = {nullptr, nullptr}; int m_cursorIndex = 0; bool m_hasNewCursor = false; bool m_internal = false; }; } Q_DECLARE_METATYPE(KWin::DrmOutput*) #endif diff --git a/plugins/platforms/drm/egl_gbm_backend.cpp b/plugins/platforms/drm/egl_gbm_backend.cpp index 815f9ea09..3c85868e2 100644 --- a/plugins/platforms/drm/egl_gbm_backend.cpp +++ b/plugins/platforms/drm/egl_gbm_backend.cpp @@ -1,385 +1,402 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin 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 2 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. If not, see . *********************************************************************/ #include "egl_gbm_backend.h" // kwin #include "composite.h" #include "drm_backend.h" #include "drm_output.h" #include "gbm_surface.h" #include "logging.h" #include "options.h" #include "screens.h" // kwin libs #include // Qt #include // system #include namespace KWin { EglGbmBackend::EglGbmBackend(DrmBackend *b) : AbstractEglBackend() , m_backend(b) { // Egl is always direct rendering setIsDirectRendering(true); setSyncsToVBlank(true); connect(m_backend, &DrmBackend::outputAdded, this, &EglGbmBackend::createOutput); connect(m_backend, &DrmBackend::outputRemoved, this, [this] (DrmOutput *output) { auto it = std::find_if(m_outputs.begin(), m_outputs.end(), [output] (const Output &o) { return o.output == output; } ); if (it == m_outputs.end()) { return; } cleanupOutput(*it); m_outputs.erase(it); } ); } EglGbmBackend::~EglGbmBackend() { cleanup(); } void EglGbmBackend::cleanupSurfaces() { for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { cleanupOutput(*it); } m_outputs.clear(); } void EglGbmBackend::cleanupOutput(const Output &o) { o.output->releaseGbm(); if (o.eglSurface != EGL_NO_SURFACE) { eglDestroySurface(eglDisplay(), o.eglSurface); } } bool EglGbmBackend::initializeEgl() { initClientExtensions(); EGLDisplay display = m_backend->sceneEglDisplay(); // Use eglGetPlatformDisplayEXT() to get the display pointer // if the implementation supports it. if (display == EGL_NO_DISPLAY) { const bool hasMesaGBM = hasClientExtension(QByteArrayLiteral("EGL_MESA_platform_gbm")); const bool hasKHRGBM = hasClientExtension(QByteArrayLiteral("EGL_KHR_platform_gbm")); const GLenum platform = hasMesaGBM ? EGL_PLATFORM_GBM_MESA : EGL_PLATFORM_GBM_KHR; if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base")) || (!hasMesaGBM && !hasKHRGBM)) { setFailed("missing one or more extensions between EGL_EXT_platform_base, EGL_MESA_platform_gbm, EGL_KHR_platform_gbm"); return false; } auto device = gbm_create_device(m_backend->fd()); if (!device) { setFailed("Could not create gbm device"); return false; } m_backend->setGbmDevice(device); display = eglGetPlatformDisplayEXT(platform, device, nullptr); } if (display == EGL_NO_DISPLAY) return false; setEglDisplay(display); return initEglAPI(); } void EglGbmBackend::init() { if (!initializeEgl()) { setFailed("Could not initialize egl"); return; } if (!initRenderingContext()) { setFailed("Could not initialize rendering context"); return; } initKWinGL(); initBufferAge(); initWayland(); + initRemotePresent(); } bool EglGbmBackend::initRenderingContext() { initBufferConfigs(); if (!createContext()) { return false; } const auto outputs = m_backend->outputs(); for (DrmOutput *drmOutput: outputs) { createOutput(drmOutput); } if (m_outputs.isEmpty()) { qCCritical(KWIN_DRM) << "Create Window Surfaces failed"; return false; } // set our first surface as the one for the abstract backend, just to make it happy setSurface(m_outputs.first().eglSurface); return makeContextCurrent(m_outputs.first()); } +void EglGbmBackend::initRemotePresent() +{ + if (qEnvironmentVariableIsSet("KWIN_NO_REMOTE")) { + return; + } + + qCDebug(KWIN_DRM) << "Support for remote access enabled"; + m_remoteaccessManager.reset(new RemoteAccessManager); +} + bool EglGbmBackend::resetOutput(Output &o, DrmOutput *drmOutput) { o.output = drmOutput; auto size = drmOutput->pixelSize(); auto gbmSurface = std::make_shared(m_backend->gbmDevice(), size.width(), size.height(), GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); if (!gbmSurface) { qCCritical(KWIN_DRM) << "Create gbm surface failed"; return false; } auto eglSurface = eglCreatePlatformWindowSurfaceEXT(eglDisplay(), config(), (void *)(gbmSurface->surface()), nullptr); if (eglSurface == EGL_NO_SURFACE) { qCCritical(KWIN_DRM) << "Create Window Surface failed"; return false; } else { // destroy previous surface if (o.eglSurface != EGL_NO_SURFACE) { if (surface() == o.eglSurface) { setSurface(eglSurface); } eglDestroySurface(eglDisplay(), o.eglSurface); } o.eglSurface = eglSurface; o.gbmSurface = gbmSurface; } return true; } void EglGbmBackend::createOutput(DrmOutput *drmOutput) { Output o; if (resetOutput(o, drmOutput)) { connect(drmOutput, &DrmOutput::modeChanged, this, [drmOutput, this] { auto it = std::find_if(m_outputs.begin(), m_outputs.end(), [drmOutput] (const auto &o) { return o.output == drmOutput; } ); if (it == m_outputs.end()) { return; } resetOutput(*it, drmOutput); } ); m_outputs << o; } } bool EglGbmBackend::makeContextCurrent(const Output &output) { const EGLSurface surface = output.eglSurface; if (surface == EGL_NO_SURFACE) { return false; } if (eglMakeCurrent(eglDisplay(), surface, surface, context()) == EGL_FALSE) { qCCritical(KWIN_DRM) << "Make Context Current failed"; return false; } EGLint error = eglGetError(); if (error != EGL_SUCCESS) { qCWarning(KWIN_DRM) << "Error occurred while creating context " << error; return false; } // TODO: ensure the viewport is set correctly each time const QSize &overall = screens()->size(); const QRect &v = output.output->geometry(); // TODO: are the values correct? qreal scale = output.output->scale(); glViewport(-v.x() * scale, (v.height() - overall.height() + v.y()) * scale, overall.width() * scale, overall.height() * scale); return true; } bool EglGbmBackend::initBufferConfigs() { const EGLint config_attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, EGL_ALPHA_SIZE, 0, EGL_RENDERABLE_TYPE, isOpenGLES() ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT, EGL_CONFIG_CAVEAT, EGL_NONE, EGL_NONE, }; EGLint count; EGLConfig configs[1024]; if (eglChooseConfig(eglDisplay(), config_attribs, configs, 1, &count) == EGL_FALSE) { qCCritical(KWIN_DRM) << "choose config failed"; return false; } if (count != 1) { qCCritical(KWIN_DRM) << "choose config did not return a config" << count; return false; } setConfig(configs[0]); return true; } void EglGbmBackend::present() { for (auto &o: m_outputs) { makeContextCurrent(o); presentOnOutput(o); } } void EglGbmBackend::presentOnOutput(EglGbmBackend::Output &o) { eglSwapBuffers(eglDisplay(), o.eglSurface); o.buffer = m_backend->createBuffer(o.gbmSurface); + if(m_remoteaccessManager && gbm_surface_has_free_buffers(o.gbmSurface->surface())) { + // GBM surface is released on page flip so + // we should pass the buffer before it's presented + m_remoteaccessManager->passBuffer(o.output, o.buffer); + } m_backend->present(o.buffer, o.output); + if (supportsBufferAge()) { eglQuerySurface(eglDisplay(), o.eglSurface, EGL_BUFFER_AGE_EXT, &o.bufferAge); } } void EglGbmBackend::screenGeometryChanged(const QSize &size) { Q_UNUSED(size) // TODO, create new buffer? } SceneOpenGLTexturePrivate *EglGbmBackend::createBackendTexture(SceneOpenGLTexture *texture) { return new EglGbmTexture(texture, this); } QRegion EglGbmBackend::prepareRenderingFrame() { startRenderTimer(); return QRegion(); } QRegion EglGbmBackend::prepareRenderingForScreen(int screenId) { const Output &o = m_outputs.at(screenId); makeContextCurrent(o); if (supportsBufferAge()) { QRegion region; // Note: An age of zero means the buffer contents are undefined if (o.bufferAge > 0 && o.bufferAge <= o.damageHistory.count()) { for (int i = 0; i < o.bufferAge - 1; i++) region |= o.damageHistory[i]; } else { region = o.output->geometry(); } return region; } return QRegion(); } void EglGbmBackend::endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { Q_UNUSED(renderedRegion) Q_UNUSED(damagedRegion) } void EglGbmBackend::endRenderingFrameForScreen(int screenId, const QRegion &renderedRegion, const QRegion &damagedRegion) { Output &o = m_outputs[screenId]; if (damagedRegion.intersected(o.output->geometry()).isEmpty() && screenId == 0) { // If the damaged region of a window is fully occluded, the only // rendering done, if any, will have been to repair a reused back // buffer, making it identical to the front buffer. // // In this case we won't post the back buffer. Instead we'll just // set the buffer age to 1, so the repaired regions won't be // rendered again in the next frame. if (!renderedRegion.intersected(o.output->geometry()).isEmpty()) glFlush(); for (auto &o: m_outputs) { o.bufferAge = 1; } return; } presentOnOutput(o); // Save the damaged region to history // Note: damage history is only collected for the first screen. For any other screen full repaints // are triggered. This is due to a limitation in Scene::paintGenericScreen which resets the Toplevel's // repaint. So multiple calls to Scene::paintScreen as it's done in multi-output rendering only // have correct damage information for the first screen. If we try to track damage nevertheless, // it creates artifacts. So for the time being we work around the problem by only supporting buffer // age on the first output. To properly support buffer age on all outputs the rendering needs to // be refactored in general. if (supportsBufferAge() && screenId == 0) { if (o.damageHistory.count() > 10) { o.damageHistory.removeLast(); } o.damageHistory.prepend(damagedRegion.intersected(o.output->geometry())); } } bool EglGbmBackend::usesOverlayWindow() const { return false; } bool EglGbmBackend::perScreenRendering() const { return true; } /************************************************ * EglTexture ************************************************/ EglGbmTexture::EglGbmTexture(KWin::SceneOpenGLTexture *texture, EglGbmBackend *backend) : AbstractEglTexture(texture, backend) { } EglGbmTexture::~EglGbmTexture() = default; } // namespace diff --git a/plugins/platforms/drm/egl_gbm_backend.h b/plugins/platforms/drm/egl_gbm_backend.h index 2af408e79..8c596e9d2 100644 --- a/plugins/platforms/drm/egl_gbm_backend.h +++ b/plugins/platforms/drm/egl_gbm_backend.h @@ -1,98 +1,101 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin 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 2 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. If not, see . *********************************************************************/ #ifndef KWIN_EGL_GBM_BACKEND_H #define KWIN_EGL_GBM_BACKEND_H #include "abstract_egl_backend.h" +#include "remoteaccess_manager.h" #include struct gbm_surface; namespace KWin { class DrmBackend; class DrmBuffer; class DrmOutput; class GbmSurface; /** * @brief OpenGL Backend using Egl on a GBM surface. **/ class EglGbmBackend : public AbstractEglBackend { Q_OBJECT public: EglGbmBackend(DrmBackend *b); virtual ~EglGbmBackend(); void screenGeometryChanged(const QSize &size) override; SceneOpenGLTexturePrivate *createBackendTexture(SceneOpenGLTexture *texture) override; QRegion prepareRenderingFrame() override; void endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; void endRenderingFrameForScreen(int screenId, const QRegion &damage, const QRegion &damagedRegion) override; bool usesOverlayWindow() const override; bool perScreenRendering() const override; QRegion prepareRenderingForScreen(int screenId) override; void init() override; protected: void present() override; void cleanupSurfaces() override; private: bool initializeEgl(); bool initBufferConfigs(); bool initRenderingContext(); + void initRemotePresent(); struct Output { DrmOutput *output = nullptr; DrmBuffer *buffer = nullptr; std::shared_ptr gbmSurface; EGLSurface eglSurface = EGL_NO_SURFACE; int bufferAge = 0; /** * @brief The damage history for the past 10 frames. */ QList damageHistory; }; bool resetOutput(Output &output, DrmOutput *drmOutput); bool makeContextCurrent(const Output &output); void presentOnOutput(Output &output); void cleanupOutput(const Output &output); void createOutput(DrmOutput *output); DrmBackend *m_backend; QVector m_outputs; + QScopedPointer m_remoteaccessManager; friend class EglGbmTexture; }; /** * @brief Texture using an EGLImageKHR. **/ class EglGbmTexture : public AbstractEglTexture { public: virtual ~EglGbmTexture(); private: friend class EglGbmBackend; EglGbmTexture(SceneOpenGLTexture *texture, EglGbmBackend *backend); }; } // namespace #endif diff --git a/plugins/platforms/drm/remoteaccess_manager.cpp b/plugins/platforms/drm/remoteaccess_manager.cpp new file mode 100644 index 000000000..3c1edbee6 --- /dev/null +++ b/plugins/platforms/drm/remoteaccess_manager.cpp @@ -0,0 +1,87 @@ +/******************************************************************** + * + KWin - the KDE window manager + 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 2 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. If not, see . +*********************************************************************/ +#include "drm_output.h" +#include "remoteaccess_manager.h" +#include "logging.h" +#include "drm_backend.h" +#include "../../../wayland_server.h" + +// system +#include +#include +#include + +namespace KWin +{ + +RemoteAccessManager::RemoteAccessManager(QObject *parent) + : QObject(parent) +{ + if (waylandServer()) { + m_interface = waylandServer()->display()->createRemoteAccessManager(this); + m_interface->create(); + + connect(m_interface, &RemoteAccessManagerInterface::bufferReleased, + this, &RemoteAccessManager::releaseBuffer); + } +} + +RemoteAccessManager::~RemoteAccessManager() +{ + if (m_interface) { + m_interface->destroy(); + } +} + +void RemoteAccessManager::releaseBuffer(const BufferHandle *buf) +{ + int ret = close(buf->fd()); + if (Q_UNLIKELY(ret)) { + qCWarning(KWIN_DRM) << "Couldn't close released GBM fd:" << strerror(errno); + } + delete buf; +} + +void RemoteAccessManager::passBuffer(DrmOutput *output, DrmBuffer *buffer) +{ + DrmSurfaceBuffer* gbmbuf = static_cast(buffer); + + // no connected RemoteAccess instance + if (!m_interface || !m_interface->isBound()) { + return; + } + + // first buffer may be null + if (!gbmbuf || !gbmbuf->hasBo()) { + return; + } + + auto buf = new BufferHandle; + auto bo = gbmbuf->getBo(); + buf->setFd(gbm_bo_get_fd(bo)); + buf->setSize(gbm_bo_get_width(bo), gbm_bo_get_height(bo)); + buf->setStride(gbm_bo_get_stride(bo)); + buf->setFormat(gbm_bo_get_format(bo)); + + m_interface->sendBufferReady(output->getWaylandInterface().data(), buf); +} + +} // KWin namespace diff --git a/plugins/platforms/drm/drm_buffer_gbm.h b/plugins/platforms/drm/remoteaccess_manager.h similarity index 52% copy from plugins/platforms/drm/drm_buffer_gbm.h copy to plugins/platforms/drm/remoteaccess_manager.h index 2b39952aa..3a1bc698b 100644 --- a/plugins/platforms/drm/drm_buffer_gbm.h +++ b/plugins/platforms/drm/remoteaccess_manager.h @@ -1,62 +1,61 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. -Copyright 2017 Roman Gilg -Copyright 2015 Martin Gräßlin +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 2 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. If not, see . *********************************************************************/ -#ifndef KWIN_DRM_BUFFER_GBM_H -#define KWIN_DRM_BUFFER_GBM_H +#ifndef REMOTEACCESSMANAGER_H +#define REMOTEACCESSMANAGER_H -#include "drm_buffer.h" - -#include +// KWayland +#include +#include +// Qt +#include struct gbm_bo; +struct gbm_surface; namespace KWin { -class GbmSurface; +class DrmOutput; +class DrmBuffer; + +using KWayland::Server::RemoteAccessManagerInterface; +using KWayland::Server::BufferHandle; -class DrmSurfaceBuffer : public DrmBuffer +class RemoteAccessManager : public QObject { + Q_OBJECT public: - DrmSurfaceBuffer(int fd, const std::shared_ptr &surface); - ~DrmSurfaceBuffer(); - - bool needsModeChange(DrmBuffer *b) const override { - if (DrmSurfaceBuffer *sb = dynamic_cast(b)) { - return hasBo() != sb->hasBo(); - } else { - return true; - } - } - - bool hasBo() const { - return m_bo != nullptr; - } - void releaseGbm() override; + explicit RemoteAccessManager(QObject *parent = nullptr); + virtual ~RemoteAccessManager(); + + void passBuffer(DrmOutput *output, DrmBuffer *buffer); + +signals: + void bufferNoLongerNeeded(qint32 gbm_handle); private: - std::shared_ptr m_surface; - gbm_bo *m_bo = nullptr; -}; + void releaseBuffer(const BufferHandle *buf); -} + RemoteAccessManagerInterface *m_interface = nullptr; +}; -#endif +} // KWin namespace +#endif // REMOTEACCESSMANAGER_H