diff --git a/plugins/platforms/drm/drm_output.h b/plugins/platforms/drm/drm_output.h --- a/plugins/platforms/drm/drm_output.h +++ b/plugins/platforms/drm/drm_output.h @@ -109,6 +109,7 @@ Q_SIGNALS: void dpmsChanged(); + void modeChanged(); private: friend class DrmBackend; @@ -139,14 +140,16 @@ void dpmsOffHandler(); bool dpmsAtomicOff(); bool atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable); + void updateMode(int modeIndex); 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; + drmModeModeInfo m_previousMode; Edid m_edid; QPointer m_waylandOutput; QPointer m_waylandOutputDevice; diff --git a/plugins/platforms/drm/drm_output.cpp b/plugins/platforms/drm/drm_output.cpp --- a/plugins/platforms/drm/drm_output.cpp +++ b/plugins/platforms/drm/drm_output.cpp @@ -696,6 +696,7 @@ if (m_changeset->modeChanged()) { qCDebug(KWIN_DRM) << "Setting new mode:" << m_changeset->mode(); m_waylandOutputDevice->setCurrentMode(m_changeset->mode()); + updateMode(m_changeset->mode()); // FIXME: implement for wl_output } if (m_changeset->transformChanged()) { @@ -717,6 +718,24 @@ return true; } +void DrmOutput::updateMode(int modeIndex) +{ + // get all modes on the connector + ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> connector(drmModeGetConnector(m_backend->fd(), m_conn->id())); + if (connector->count_modes <= modeIndex) { + // TODO: error? + return; + } + if (isCurrentMode(&connector->modes[modeIndex])) { + // nothing to do + return; + } + m_previousMode = m_mode; + m_mode = connector->modes[modeIndex]; + m_modesetRequested = true; + emit modeChanged(); +} + void DrmOutput::pageFlipped() { m_pageFlipPending = false; diff --git a/plugins/platforms/drm/egl_gbm_backend.h b/plugins/platforms/drm/egl_gbm_backend.h --- a/plugins/platforms/drm/egl_gbm_backend.h +++ b/plugins/platforms/drm/egl_gbm_backend.h @@ -70,6 +70,7 @@ */ QList damageHistory; }; + bool resetOutput(Output &output, DrmOutput *drmOutput); bool makeContextCurrent(const Output &output); void presentOnOutput(Output &output); void cleanupOutput(const Output &output); diff --git a/plugins/platforms/drm/egl_gbm_backend.cpp b/plugins/platforms/drm/egl_gbm_backend.cpp --- a/plugins/platforms/drm/egl_gbm_backend.cpp +++ b/plugins/platforms/drm/egl_gbm_backend.cpp @@ -150,25 +150,51 @@ return makeContextCurrent(m_outputs.first()); } -void EglGbmBackend::createOutput(DrmOutput *drmOutput) +bool EglGbmBackend::resetOutput(Output &o, DrmOutput *drmOutput) { - Output o; o.output = drmOutput; auto size = drmOutput->pixelSize(); - o.gbmSurface = std::make_shared(m_backend->gbmDevice(), size.width(), size.height(), + auto gbmSurface = std::make_shared(m_backend->gbmDevice(), size.width(), size.height(), GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); - if (!o.gbmSurface) { + if (!gbmSurface) { qCCritical(KWIN_DRM) << "Create gbm surface failed"; - return; + return false; } - o.eglSurface = eglCreatePlatformWindowSurfaceEXT(eglDisplay(), config(), (void *)(o.gbmSurface->surface()), nullptr); - if (o.eglSurface == EGL_NO_SURFACE) { + auto eglSurface = eglCreatePlatformWindowSurfaceEXT(eglDisplay(), config(), (void *)(gbmSurface->surface()), nullptr); + if (eglSurface == EGL_NO_SURFACE) { qCCritical(KWIN_DRM) << "Create Window Surface failed"; - o.gbmSurface.reset(); - return; + return false; + } else { + // destroy previous surface + if (o.eglSurface != EGL_NO_SURFACE) { + eglDestroySurface(eglDisplay(), 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; } - m_outputs << o; } bool EglGbmBackend::makeContextCurrent(const Output &output) diff --git a/plugins/platforms/drm/scene_qpainter_drm_backend.cpp b/plugins/platforms/drm/scene_qpainter_drm_backend.cpp --- a/plugins/platforms/drm/scene_qpainter_drm_backend.cpp +++ b/plugins/platforms/drm/scene_qpainter_drm_backend.cpp @@ -68,6 +68,27 @@ o.buffer[index]->map(); o.buffer[index]->image()->fill(Qt::black); }; + connect(output, &DrmOutput::modeChanged, this, + [output, this] { + auto it = std::find_if(m_outputs.begin(), m_outputs.end(), + [output] (const auto &o) { + return o.output == output; + } + ); + if (it == m_outputs.end()) { + return; + } + delete (*it).buffer[0]; + delete (*it).buffer[1]; + auto initBuffer = [it, output, this] (int index) { + it->buffer[index] = m_backend->createBuffer(output->pixelSize()); + it->buffer[index]->map(); + it->buffer[index]->image()->fill(Qt::black); + }; + initBuffer(0); + initBuffer(1); + } + ); initBuffer(0); initBuffer(1); o.output = output;