[plasma-workspace/plasmashell+libkscreen] /: Adopt libkscreen to get information of the screens

Aleix Pol aleixpol at kde.org
Thu May 1 01:42:55 UTC 2014


Git commit 168c15a463746cf9039f2f9e188b1827ed809f97 by Aleix Pol.
Committed on 01/05/2014 at 01:41.
Pushed by apol into branch 'plasmashell+libkscreen'.

Adopt libkscreen to get information of the screens

Qt subsystem has deficiencies on their approximation to multiple
screens. [1]
This patch adopts libkscreen to figure out screen additions and primary
modifications.
I'm not merging to master yet as I'm experiencing some issues but it's
already far more comfortable to work with than QGuiApplication::screens().

[1] https://bugreports.qt-project.org/browse/QTBUG-38404

CCMAIL: plasma-devel at kde.org

M  +1    -0    CMakeLists.txt
M  +1    -0    shell/CMakeLists.txt
M  +151  -42   shell/shellcorona.cpp
M  +11   -2    shell/shellcorona.h

http://commits.kde.org/plasma-workspace/168c15a463746cf9039f2f9e188b1827ed809f97

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 31e29b2..8078b0b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,6 +13,7 @@ find_package(KF5 REQUIRED COMPONENTS
                     IdleTime ThreadWeaver Declarative PlasmaQuick WebKit KDELibs4Support)
 
 find_package(KF5 REQUIRED COMPONENTS SysGuard)
+find_package(KF5 REQUIRED COMPONENTS Screen)
 find_package(KWinDBusInterface CONFIG REQUIRED)
 
 include(KDEInstallDirs)
diff --git a/shell/CMakeLists.txt b/shell/CMakeLists.txt
index 5f58d8c..5b2a290 100644
--- a/shell/CMakeLists.txt
+++ b/shell/CMakeLists.txt
@@ -87,6 +87,7 @@ target_link_libraries(plasma-shell
  KF5::Activities
  KF5::GlobalAccel
  KF5::DBusAddons
+ KF5::Screen
 )
 if (TARGET KF5::TextEditor)
  target_link_libraries(plasma-shell KF5::TextEditor)
diff --git a/shell/shellcorona.cpp b/shell/shellcorona.cpp
index 2f82132..0670b43 100644
--- a/shell/shellcorona.cpp
+++ b/shell/shellcorona.cpp
@@ -42,6 +42,9 @@
 #include <KAuthorized>
 #include <KWindowSystem>
 
+#include <KScreen/Config>
+#include <kscreen/configmonitor.h>
+
 #include "config-ktexteditor.h" // HAVE_KTEXTEDITOR
 
 
@@ -59,8 +62,6 @@
 
 #include "plasmashelladaptor.h"
 
-
-
 static const int s_configSyncDelay = 10000; // 10 seconds
 
 class ShellCorona::Private {
@@ -70,7 +71,8 @@ public:
           activityController(new KActivities::Controller(q)),
           activityConsumer(new KActivities::Consumer(q)),
           addPanelAction(nullptr),
-          addPanelsMenu(nullptr)
+          addPanelsMenu(nullptr),
+          screenConfiguration(nullptr)
     {
         appConfigSyncTimer.setSingleShot(true);
         appConfigSyncTimer.setInterval(s_configSyncDelay);
@@ -99,10 +101,20 @@ public:
     QWeakPointer<InteractiveConsole> console;
 #endif
 
+    KScreen::Config* screenConfiguration;
     QTimer waitingPanelsTimer;
     QTimer appConfigSyncTimer;
 };
 
+static QScreen* outputToScreen(KScreen::Output* output)
+{
+    foreach(QScreen* screen, QGuiApplication::screens()) {
+        if(screen->name() == output->name()) {
+            return screen;
+        }
+    }
+    return 0;
+}
 
 WorkspaceScripting::DesktopScriptEngine * ShellCorona::scriptEngine() const
 {
@@ -233,6 +245,20 @@ QString ShellCorona::shell() const
     return d->shell;
 }
 
+static QList<KScreen::Output*> sortOutputs(const QHash<int, KScreen::Output*> &outputs)
+{
+    QList<KScreen::Output*> ret;
+    foreach(KScreen::Output* output, outputs) {
+        if(!output->isEnabled())
+            ;
+        else if(output->isPrimary())
+            ret.prepend(output);
+        else
+            ret.append(output);
+    }
+    return ret;
+}
+
 void ShellCorona::load()
 {
     if (d->shell.isEmpty() ||
@@ -269,17 +295,73 @@ void ShellCorona::load()
         }
     }
 
-    for (QScreen *screen : QGuiApplication::screens()) {
-        screenAdded(screen);
+    d->screenConfiguration = KScreen::Config::current();
+    KScreen::ConfigMonitor::instance()->addConfig(d->screenConfiguration);
+    for (KScreen::Output *output : sortOutputs(d->screenConfiguration->connectedOutputs())) {
+        outputAdded(output);
     }
-    connect(qApp, &QGuiApplication::screenAdded,
-            this, &ShellCorona::screenAdded);
+    connect(d->screenConfiguration, &KScreen::Config::outputAdded,
+            this, &ShellCorona::outputAdded);
+    connect(d->screenConfiguration, &KScreen::Config::primaryOutputChanged,
+            this, &ShellCorona::primaryOutputChanged);
 
     if (!d->waitingPanels.isEmpty()) {
         d->waitingPanelsTimer.start();
     }
 }
 
+void ShellCorona::primaryOutputChanged()
+{
+    KScreen::Config* current = d->screenConfiguration;
+    QScreen* newPrimary = outputToScreen(current->primaryOutput());
+    int i=0;
+    foreach(DesktopView* view, d->views) {
+        if(view->screen() == newPrimary)
+            break;
+        i++;
+    }
+    QScreen* oldPrimary = d->views.first()->screen();
+    qDebug() << "primary changed!" << oldPrimary->name() << newPrimary->name() << i;
+//
+//     //if it was not found, it means that outputAdded hasn't been called yet
+    if (i>=d->views.count() || i==0)
+        return;
+//
+    Q_ASSERT(oldPrimary != newPrimary);
+    Q_ASSERT(d->views[0]->screen() != d->views[i]->screen());
+    Q_ASSERT(d->views[0]->screen() == oldPrimary);
+    Q_ASSERT(d->views[0]->screen() != newPrimary);
+    Q_ASSERT(d->views[0]->geometry() == oldPrimary->geometry());
+    qDebug() << "adapting" << newPrimary->geometry() << d->views[0]->containment()->wallpaper()
+                           << oldPrimary->geometry() << d->views[i]->containment()->wallpaper() << i;
+
+    d->views[0]->setScreen(newPrimary);
+    d->views[i]->setScreen(oldPrimary);
+    screenInvariants();
+
+    QList<Plasma::Containment*> panels;
+    foreach(PanelView* panel, d->panelViews) {
+        if(panel->screen() == oldPrimary)
+            panel->setScreen(newPrimary);
+        else if(panel->screen() == newPrimary)
+            panel->setScreen(oldPrimary);
+    }
+}
+
+void ShellCorona::screenInvariants()
+{
+    QScreen* s = outputToScreen(d->screenConfiguration->primaryOutput());
+    Q_ASSERT(d->views[0]->screen()->name() == s->name());
+    Q_ASSERT(d->views[0]->geometry() == s->geometry());
+
+    QSet<QScreen*> screens;
+    foreach(DesktopView* view, d->views) {
+        Q_ASSERT(!screens.contains(view->screen()));
+        screens.insert(view->screen());
+    }
+}
+
+
 void ShellCorona::unload()
 {
     if (d->shell.isEmpty()) {
@@ -356,7 +438,7 @@ QRegion ShellCorona::availableScreenRegion(int id) const
 
     if (view) {
         QRegion r = view->geometry();
-        foreach (PanelView *v, d->panelViews.values()) {
+        foreach (PanelView *v, d->panelViews) {
             if (v->containment()->screen() == id && v->visibilityMode() != PanelView::AutoHide) {
                 r -= v->geometry();
             }
@@ -375,7 +457,7 @@ QRect ShellCorona::availableScreenRect(int id) const
 
     QRect r(screenGeometry(id));
 
-    foreach (PanelView *view, d->panelViews.values()) {
+    foreach (PanelView *view, d->panelViews) {
         if (view->containment()->screen() == id && view->visibilityMode() != PanelView::AutoHide) {
             QRect v = view->geometry();
             switch (view->location()) {
@@ -417,11 +499,13 @@ PanelView *ShellCorona::panelView(Plasma::Containment *containment) const
     return d->panelViews.value(containment);
 }
 
-
 ///// SLOTS
 
-void ShellCorona::screenAdded(QScreen *screen)
+void ShellCorona::outputAdded(KScreen::Output* output)
 {
+    QScreen* screen = outputToScreen(output);
+    Q_ASSERT(screen);
+
     //FIXME: QScreen doesn't have any idea of "this qscreen is clone of this other one
     //so this ultra inefficient heuristic has to stay until we have a slightly better api
     foreach (QScreen *s, QGuiApplication::screens()) {
@@ -433,16 +517,14 @@ void ShellCorona::screenAdded(QScreen *screen)
     }
 
     DesktopView *view = new DesktopView(this, screen);
-    connect(screen, &QObject::destroyed, [=](){
-        d->views.removeAll(view);
-        view->containment()->reactToScreenChange();
-        view->deleteLater();
-    });
 
+    //We have to do it in a lambda,
+    connect(screen, &QObject::destroyed, this, [=]() { removeDesktop(view); });
 
     const QString currentActivity = d->activityController->currentActivity();
 
-    if (!d->views.isEmpty() && screen == QGuiApplication::primaryScreen()) {
+    qDebug() << "adding screen" << output->name() << output->isPrimary();
+    if (!d->views.isEmpty() && output->isPrimary()) {
         DesktopView* oldPrimaryView = d->views.first();
         QScreen* oldPrimaryScreen = oldPrimaryView->screen();
 
@@ -462,6 +544,7 @@ void ShellCorona::screenAdded(QScreen *screen)
         d->views.prepend(view);
         view = oldPrimaryView;
     } else {
+        Q_ASSERT(d->views.isEmpty() || !output->isPrimary());
         d->views.append(view);
     }
 
@@ -488,8 +571,30 @@ void ShellCorona::screenAdded(QScreen *screen)
 
     emit availableScreenRectChanged();
     emit availableScreenRegionChanged();
+
+    screenInvariants();
+}
+
+void ShellCorona::removeDesktop(DesktopView* view)
+{
+    int idx = d->views.indexOf(view);
+    DesktopView* lastView = d->views.takeAt(d->views.count()-1);
+    lastView->containment()->reactToScreenChange();
+    lastView->deleteLater();
+
+    for(int i = idx; i<d->views.count()-1; ++i) {
+        d->views[i]->setScreen(d->views[i+1]->screen());
+    }
+
+    screenInvariants();
 }
 
+void ShellCorona::removePanel(Plasma::Containment* cont)
+{
+    d->panelViews[cont]->deleteLater();
+    d->waitingPanels << cont;
+    d->panelViews.remove(cont);
+}
 
 Plasma::Containment* ShellCorona::createContainmentForActivity(const QString& activity, int screenNum)
 {
@@ -517,30 +622,32 @@ void ShellCorona::createWaitingPanels()
 
     foreach (Plasma::Containment *cont, d->waitingPanels) {
         //ignore non existing (yet?) screens
-        if (cont->lastScreen() > (QGuiApplication::screens().size() - 1)) {
+        int requestedScreen = cont->lastScreen();
+        if (requestedScreen < 0)
+            ++requestedScreen;
+
+        if (requestedScreen > (d->views.size() - 1)) {
             stillWaitingPanels << cont;
             continue;
         }
 
         d->panelViews[cont] = new PanelView(cont);
 
-        //keep screen suggestions within bounds of screens we actually have
-        int screen = qBound(0, cont->lastScreen(), QGuiApplication::screens().size() -1);
+        Q_ASSERT(qBound(0, requestedScreen, d->views.size() -1) == requestedScreen);
+        QScreen* screen = d->views[requestedScreen]->screen();
 
-        d->panelViews[cont]->setScreen(QGuiApplication::screens()[screen]);
+        d->panelViews[cont]->setScreen(screen);
         cont->reactToScreenChange();
         connect(cont, &QObject::destroyed,
-                [=](QObject *obj) {
+                [=](QObject* ) {
                     d->panelViews.remove(cont);
                     emit availableScreenRectChanged();
                     emit availableScreenRegionChanged();
                 });
 
-        connect(QGuiApplication::screens()[screen], &QObject::destroyed,
-                [=](QObject *obj) {
-                    d->panelViews[cont]->deleteLater();
-                    d->waitingPanels << cont;
-                    d->panelViews.remove(cont);
+        connect(screen, &QObject::destroyed,
+                [=](QObject*) {
+                    removePanel(cont);
                 });
     }
     d->waitingPanels.clear();
@@ -549,6 +656,8 @@ void ShellCorona::createWaitingPanels()
     emit availableScreenRegionChanged();
 }
 
+
+
 void ShellCorona::handleContainmentAdded(Plasma::Containment* c)
 {
     connect(c, &Plasma::Containment::showAddWidgetsInterface,
@@ -883,34 +992,34 @@ void ShellCorona::addPanel(const QString &plugin)
 
     d->waitingPanels << panel;
     createWaitingPanels();
-    if (d->panelViews.contains(panel)) {
-        const QPoint cursorPos(QCursor::pos());
-        foreach (QScreen *screen, QGuiApplication::screens()) {
-            if (screen->geometry().contains(cursorPos)) {
-                d->panelViews[panel]->setScreen(screen);
-                break;
-            }
+    Q_ASSERT(d->panelViews.contains(panel));
+    const QPoint cursorPos(QCursor::pos());
+    foreach (QScreen *screen, QGuiApplication::screens()) {
+        if (screen->geometry().contains(cursorPos)) {
+            d->panelViews[panel]->setScreen(screen);
+            break;
         }
     }
 }
 
 int ShellCorona::screenForContainment(const Plasma::Containment *containment) const
 {
-    QScreen *screen = nullptr;
     for (int i = 0; i < d->views.size(); i++) {
         if (d->views[i]->containment() == containment) {
-            screen = d->views[i]->screen();
+            return i;
         }
     }
 
-    if (!screen) {
-        PanelView *view = d->panelViews.value(containment);
-        if (view) {
-            screen = view->screen();
+    PanelView *view = d->panelViews.value(containment);
+    if (view) {
+        QScreen *screen = view->screen();
+        for (int i = 0; i < d->views.size(); i++) {
+            if (d->views[i]->screen() == screen) {
+                return i;
+            }
         }
     }
-
-    return screen ? qApp->screens().indexOf(screen) : -1;
+    return -1;
 }
 
 void ShellCorona::activityOpened()
diff --git a/shell/shellcorona.h b/shell/shellcorona.h
index d71f648..dc74500 100644
--- a/shell/shellcorona.h
+++ b/shell/shellcorona.h
@@ -27,6 +27,9 @@
 
 #include <Plasma/Package>
 
+namespace KScreen {
+class Output;
+}
 
 namespace Plasma
 {
@@ -34,6 +37,7 @@ namespace Plasma
 } // namespace Plasma
 
 class Activity;
+class DesktopView;
 class PanelView;
 class QScreen;
 namespace WorkspaceScripting {
@@ -102,8 +106,6 @@ public Q_SLOTS:
     void loadScriptInInteractiveConsole(const QString &script);
 
 protected Q_SLOTS:
-    void screenAdded(QScreen *screen);
-
     /**
      * Loads the layout and performs the needed checks
      */
@@ -141,11 +143,18 @@ private Q_SLOTS:
     void addPanel(QAction *action);
     void addPanel(const QString &plugin);
 
+    void removePanel(Plasma::Containment* cont);
+    void removeDesktop(DesktopView* screen);
+    void outputAdded(KScreen::Output* output);
+    void primaryOutputChanged();
+
     void activityOpened();
     void activityClosed();
     void activityRemoved();
 
 private:
+    void screenInvariants();
+
     /**
      * @returns a new containment associated with the specified @p activity and @p screen.
      */



More information about the Plasma-devel mailing list