diff --git a/drm_backend.cpp b/drm_backend.cpp index 2c74d0c02e..64f312d4c1 100644 --- a/drm_backend.cpp +++ b/drm_backend.cpp @@ -59,6 +59,7 @@ namespace KWin DrmBackend::DrmBackend(QObject *parent) : AbstractBackend(parent) , m_udev(new Udev) + , m_udevMonitor(m_udev->monitor()) { m_cursor[0] = nullptr; m_cursor[1] = nullptr; @@ -197,6 +198,34 @@ void DrmBackend::openDrm() ); m_drmId = device->sysNum(); queryResources(); + + // setup udevMonitor + if (m_udevMonitor) { + m_udevMonitor->filterSubsystemDevType("drm"); + const int fd = m_udevMonitor->fd(); + if (fd != -1) { + QSocketNotifier *notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); + connect(notifier, &QSocketNotifier::activated, this, + [this] { + auto device = m_udevMonitor->getDevice(); + if (!device) { + return; + } + if (device->sysNum() != m_drmId) { + return; + } + if (device->hasProperty("HOTPLUG", "1")) { + qCDebug(KWIN_CORE()) << "Received hot plug event for monitored drm device"; + queryResources(); + m_cursorIndex = (m_cursorIndex + 1) % 2; + updateCursor(); + } + } + ); + m_udevMonitor->enable(); + } + } + emit screensQueried(); initCursor(); @@ -222,6 +251,8 @@ void DrmBackend::queryResources() qCWarning(KWIN_CORE) << "drmModeGetResources failed"; return; } + + QVector connectedOutputs; for (int i = 0; i < resources->count_connectors; ++i) { const auto id = resources->connectors[i]; ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> connector(drmModeGetConnector(m_fd, id)); @@ -231,6 +262,13 @@ void DrmBackend::queryResources() if (connector->connection != DRM_MODE_CONNECTED) { continue; } + if (connector->count_modes == 0) { + continue; + } + if (DrmOutput *o = findOutput(connector->connector_id)) { + connectedOutputs << o; + continue; + } bool crtcFound = false; const quint32 crtcId = findCrtc(resources.data(), connector.data(), &crtcFound); if (!crtcFound) { @@ -242,14 +280,47 @@ void DrmBackend::queryResources() } DrmOutput *drmOutput = new DrmOutput(this); drmOutput->m_crtcId = crtcId; - drmOutput->m_mode = crtc->mode; + if (crtc->mode_valid) { + drmOutput->m_mode = crtc->mode; + } else { + drmOutput->m_mode = connector->modes[0]; + } drmOutput->m_connector = connector->connector_id; drmOutput->init(); - m_outputs << drmOutput; + connectedOutputs << drmOutput; } + // check for outputs which got removed + auto it = m_outputs.begin(); + while (it != m_outputs.end()) { + if (connectedOutputs.contains(*it)) { + it++; + continue; + } + DrmOutput *removed = *it; + it = m_outputs.erase(it); + emit outputRemoved(removed); + delete removed; + } + for (auto it = connectedOutputs.constBegin(); it != connectedOutputs.constEnd(); ++it) { + if (!m_outputs.contains(*it)) { + emit outputAdded(*it); + } + } + m_outputs = connectedOutputs; // TODO: install global space } +DrmOutput *DrmBackend::findOutput(quint32 connector) +{ + auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(), [connector] (DrmOutput *o) { + return o->m_connector == connector; + }); + if (it != m_outputs.constEnd()) { + return *it; + } + return nullptr; +} + quint32 DrmBackend::findCrtc(drmModeRes *res, drmModeConnector *connector, bool *ok) { if (ok) { diff --git a/drm_backend.h b/drm_backend.h index b57efc44e0..eb4ddcfc30 100644 --- a/drm_backend.h +++ b/drm_backend.h @@ -32,6 +32,7 @@ namespace KWin { class Udev; +class UdevMonitor; class DrmBuffer; class DrmOutput; @@ -64,6 +65,8 @@ public: Q_SIGNALS: void screensQueried(); + void outputRemoved(KWin::DrmOutput *output); + void outputAdded(KWin::DrmOutput *output); private: static void pageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data); @@ -79,7 +82,9 @@ private: void initCursor(); quint32 findCrtc(drmModeRes *res, drmModeConnector *connector, bool *ok = nullptr); bool crtcIsUsed(quint32 crtc); + DrmOutput *findOutput(quint32 connector); QScopedPointer m_udev; + QScopedPointer m_udevMonitor; int m_fd = -1; int m_drmId = 0; QVector m_outputs; @@ -168,5 +173,7 @@ private: } +Q_DECLARE_METATYPE(KWin::DrmOutput*) + #endif