diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,7 +86,7 @@ PURPOSE "Required for building the X11 based workspace") if(X11_FOUND) - find_package(XCB MODULE REQUIRED COMPONENTS XCB) + find_package(XCB MODULE REQUIRED COMPONENTS XCB RANDR) set_package_properties(XCB PROPERTIES TYPE REQUIRED) if(NOT X11_SM_FOUND) message(FATAL_ERROR "\nThe X11 Session Management (SM) development package could not be found.\nPlease install libSM.\n") diff --git a/shell/CMakeLists.txt b/shell/CMakeLists.txt --- a/shell/CMakeLists.txt +++ b/shell/CMakeLists.txt @@ -99,7 +99,7 @@ target_compile_definitions(plasmashell PRIVATE -DPROJECT_VERSION="${PROJECT_VERSION}") if(HAVE_X11) - target_link_libraries(plasmashell ${X11_LIBRARIES} XCB::XCB ) + target_link_libraries(plasmashell ${X11_LIBRARIES} ${XCB_LIBRARIES} ) target_link_libraries(plasmashell Qt5::X11Extras) endif() diff --git a/shell/autotests/CMakeLists.txt b/shell/autotests/CMakeLists.txt --- a/shell/autotests/CMakeLists.txt +++ b/shell/autotests/CMakeLists.txt @@ -10,6 +10,10 @@ Qt5::Gui KF5::Service ) + if(HAVE_X11) + target_link_libraries(${_testname} ${X11_LIBRARIES} ${XCB_LIBRARIES} ) + target_link_libraries(${_testname} Qt5::X11Extras) + endif() if(QT_QTOPENGL_FOUND) target_link_libraries(${_testname} Qt5::OpenGL) endif() diff --git a/shell/desktopview.h b/shell/desktopview.h --- a/shell/desktopview.h +++ b/shell/desktopview.h @@ -88,14 +88,12 @@ Q_SIGNALS: void stayBehindChanged(); void windowTypeChanged(); - void screenRenamed(); private: void coronaPackageChanged(const KPackage::Package &package); void ensureWindowType(); void setupWaylandIntegration(); - QString m_screenName; QPointer m_configView; QPointer m_oldScreen; QPointer m_screenToFollow; diff --git a/shell/desktopview.cpp b/shell/desktopview.cpp --- a/shell/desktopview.cpp +++ b/shell/desktopview.cpp @@ -85,7 +85,6 @@ return; } - m_screenName = screen->name(); m_screenToFollow = screen; setScreen(screen); adaptToScreen(); @@ -199,17 +198,7 @@ bool DesktopView::event(QEvent *e) { - //NOTE: we need this heuristic for the case when there is an - //internal laptop screen that gets disabled upon connection of an external one. the only QCreen * pointer gets recycled and there is no dedicated signal to discover this at all - //after being moved, the view will get an expose event, so we can check there if the screen has been renamed - //see https://bugs.kde.org/show_bug.cgi?id=373880 - //https://bugreports.qt.io/browse/QTBUG-57785 - if (e->type() == QEvent::Expose) { - if (m_screenToFollow && m_screenToFollow->name() != m_screenName) { - m_screenName = m_screenToFollow->name(); - emit screenRenamed(); - } - } else if (e->type() == QEvent::KeyRelease) { + if (e->type() == QEvent::KeyRelease) { QKeyEvent *ke = static_cast(e); if (KWindowSystem::showingDesktop() && ke->key() == Qt::Key_Escape) { ShellCorona *c = qobject_cast(corona()); diff --git a/shell/screenpool.h b/shell/screenpool.h --- a/shell/screenpool.h +++ b/shell/screenpool.h @@ -24,11 +24,13 @@ #include #include #include +#include #include #include -class ScreenPool : public QObject { +class ScreenPool : public QObject, public QAbstractNativeEventFilter +{ Q_OBJECT public: @@ -50,6 +52,9 @@ //all ids that are known, included screens not enabled at the moment QList knownIds() const; +protected: + bool nativeEventFilter(const QByteArray & eventType, void * message, long * result) Q_DECL_OVERRIDE; + private: void save(); diff --git a/shell/screenpool.cpp b/shell/screenpool.cpp --- a/shell/screenpool.cpp +++ b/shell/screenpool.cpp @@ -18,14 +18,24 @@ */ #include "screenpool.h" +#include #include #include +#if HAVE_X11 +#include +#include +#include +#include +#endif + ScreenPool::ScreenPool(KSharedConfig::Ptr config, QObject *parent) : QObject(parent), m_configGroup(KConfigGroup(config, QStringLiteral("ScreenConnectors"))) { + qApp->installNativeEventFilter(this); + m_configSaveTimer.setSingleShot(true); connect(&m_configSaveTimer, &QTimer::timeout, this, [this](){ m_configGroup.sync(); @@ -159,5 +169,38 @@ return m_connectorForId.keys(); } +bool ScreenPool::nativeEventFilter(const QByteArray& eventType, void* message, long int* result) +{ + Q_UNUSED(result); +#if HAVE_X11 + // a particular edge case: when we switch the only enabled screen + // we don't have any signal about it, the primary screen changes but we have the same old QScreen* getting recycled + // see https://bugs.kde.org/show_bug.cgi?id=373880 + // if this slot will be invoked many times, their//second time on will do nothing as name and primaryconnector will be the same by then + if (eventType != "xcb_generic_event_t") { + return false; + } + + xcb_generic_event_t *ev = static_cast(message); + + const auto responseType = XCB_EVENT_RESPONSE_TYPE(ev); + + const xcb_query_extension_reply_t* reply = xcb_get_extension_data(QX11Info::connection(), &xcb_randr_id); + + if (responseType == reply->first_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY) { + if (qGuiApp->primaryScreen()->name() != primaryConnector()) { + //new screen? + if (id(qGuiApp->primaryScreen()->name()) < 0) { + insertScreenMapping(firstAvailableId(), qGuiApp->primaryScreen()->name()); + } + //switch the primary screen in the pool + setPrimaryConnector(qGuiApp->primaryScreen()->name()); + } + } +#endif + return false; +} + + #include "moc_screenpool.cpp" diff --git a/shell/shellcorona.h b/shell/shellcorona.h --- a/shell/shellcorona.h +++ b/shell/shellcorona.h @@ -240,6 +240,7 @@ QSet m_redundantOutputs; QList m_alternativesObjects; KDeclarative::QmlObject *m_interactiveConsole; + int m_eventBase; QTimer m_waitingPanelsTimer; QTimer m_appConfigSyncTimer; diff --git a/shell/shellcorona.cpp b/shell/shellcorona.cpp --- a/shell/shellcorona.cpp +++ b/shell/shellcorona.cpp @@ -79,6 +79,7 @@ #if HAVE_X11 #include #include +#include #endif @@ -1144,20 +1145,6 @@ DesktopView *view = new DesktopView(this, screen); connect(view, &QQuickWindow::sceneGraphError, this, &ShellCorona::showOpenGLNotCompatibleWarning); - // a particular edge case: when we switch the only enabled screen - // we don't have any signal about it, the primary screen changes but we have the same old QScreen* getting recycled - // see https://bugs.kde.org/show_bug.cgi?id=373880 - // if this slot will be invoked many times, their//second time on will do nothing as name and primaryconnector will be the same by then - connect(view, &DesktopView::screenRenamed, this, [=](){ - if (qGuiApp->primaryScreen()->name() != m_screenPool->primaryConnector()) { - //new screen? - if (m_screenPool->id(qGuiApp->primaryScreen()->name()) < 0) { - m_screenPool->insertScreenMapping(m_screenPool->firstAvailableId(), qGuiApp->primaryScreen()->name()); - } - //switch the primary screen in the pool - m_screenPool->setPrimaryConnector(qGuiApp->primaryScreen()->name()); - } - }); Plasma::Containment *containment = createContainmentForActivity(m_activityController->currentActivity(), insertPosition); Q_ASSERT(containment);