add a screen pool for multi-screens

v0.6
Michail Vourlakos 8 years ago
parent d555eacb13
commit a6b8ecc4b8

@ -11,6 +11,7 @@ set(lattedock-app_SRCS
packageplugins/shell/dockpackage.cpp
panelshadows.cpp
alternativeshelper.cpp
screenpool.cpp
main.cpp
)

@ -23,6 +23,7 @@
#include "packageplugins/shell/dockpackage.h"
#include "abstractwindowinterface.h"
#include "alternativeshelper.h"
#include "screenpool.h"
#include <QAction>
#include <QScreen>
@ -44,6 +45,7 @@ namespace Latte {
DockCorona::DockCorona(QObject *parent)
: Plasma::Corona(parent),
m_screenPool(new ScreenPool(KSharedConfig::openConfig(), this)),
m_activityConsumer(new KActivities::Consumer(this))
{
KPackage::Package package(new DockPackage(this));
@ -89,7 +91,15 @@ DockCorona::~DockCorona()
void DockCorona::load()
{
if (m_activityConsumer && (m_activityConsumer->serviceStatus() == KActivities::Consumer::Running) && m_activitiesStarting) {
disconnect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &DockCorona::load);
m_screenPool->load();
m_activitiesStarting = false;
connect(qGuiApp, &QGuiApplication::screenAdded, this, &DockCorona::addOutput, Qt::UniqueConnection);
connect(qGuiApp, &QGuiApplication::primaryScreenChanged, this, &DockCorona::primaryOutputChanged, Qt::UniqueConnection);
connect(qGuiApp, &QGuiApplication::screenRemoved, this, &DockCorona::screenRemoved, Qt::UniqueConnection);
loadLayout();
}
}
@ -160,6 +170,11 @@ bool DockCorona::appletExists(uint containmentId, uint appletId) const
return false;
}
ScreenPool *DockCorona::screenPool() const
{
return m_screenPool;
}
int DockCorona::numScreens() const
{
return qGuiApp->screens().count();
@ -222,12 +237,26 @@ QRect DockCorona::availableScreenRect(int id) const
return available;
}
void DockCorona::addOutput(QScreen *screen)
{
Q_ASSERT(screen);
}
void DockCorona::primaryOutputChanged()
{
}
void DockCorona::screenRemoved(QScreen *screen)
{
Q_ASSERT(screen);
}
int DockCorona::primaryScreenId() const
{
//this is not the proper way because kwin probably uses a different
//index of screens...
//This needs a lot of testing...
return qGuiApp->screens().indexOf(qGuiApp->primaryScreen());
return m_screenPool->id(qGuiApp->primaryScreen()->name());
}
int DockCorona::docksCount(int screen) const
@ -299,7 +328,39 @@ int DockCorona::screenForContainment(const Plasma::Containment *containment) con
// startup (catch-up race) between
// screen:0 and primaryScreen
return primaryScreenId();
//case in which this containment is child of an applet, hello systray :)
if (Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent())) {
if (Plasma::Containment *cont = parentApplet->containment()) {
return screenForContainment(cont);
} else {
return -1;
}
}
//if the panel views already exist, base upon them
DockView *view = m_dockViews.value(containment);
if (view && view->screen()) {
return m_screenPool->id(view->screen()->name());
}
//Failed? fallback on lastScreen()
//lastScreen() is the correct screen for panels
//It is also correct for desktops *that have the correct activity()*
//a containment with lastScreen() == 0 but another activity,
//won't be associated to a screen
// qDebug() << "ShellCorona screenForContainment: " << containment << " Last screen is " << containment->lastScreen();
for (auto screen : qGuiApp->screens()) {
// containment->lastScreen() == m_screenPool->id(screen->name()) to check if the lastScreen refers to a screen that exists/it's known
if (containment->lastScreen() == m_screenPool->id(screen->name()) &&
(containment->activity() == m_activityConsumer->currentActivity() ||
containment->containmentType() == Plasma::Types::PanelContainment || containment->containmentType() == Plasma::Types::CustomPanelContainment)) {
return containment->lastScreen();
}
}
return -1;
}
void DockCorona::addDock(Plasma::Containment *containment)
@ -366,6 +427,7 @@ void DockCorona::dockContainmentDestroyed(QObject *cont)
void DockCorona::showAlternativesForApplet(Plasma::Applet *applet)
{
const QString alternativesQML = kPackage().filePath("appletalternativesui");
if (alternativesQML.isEmpty()) {
return;
}
@ -382,13 +444,16 @@ void DockCorona::showAlternativesForApplet(Plasma::Applet *applet)
connect(qmlObj->rootObject(), SIGNAL(visibleChanged(bool)),
this, SLOT(alternativesVisibilityChanged(bool)));
connect(applet, &Plasma::Applet::destroyedChanged, this, [this, qmlObj] (bool destroyed) {
connect(applet, &Plasma::Applet::destroyedChanged, this, [this, qmlObj](bool destroyed) {
if (!destroyed) {
return;
}
QMutableListIterator<KDeclarative::QmlObject *> it(m_alternativesObjects);
while (it.hasNext()) {
KDeclarative::QmlObject *obj = it.next();
if (obj == qmlObj) {
it.remove();
obj->deleteLater();
@ -406,8 +471,10 @@ void DockCorona::alternativesVisibilityChanged(bool visible)
QObject *root = sender();
QMutableListIterator<KDeclarative::QmlObject *> it(m_alternativesObjects);
while (it.hasNext()) {
KDeclarative::QmlObject *obj = it.next();
if (obj->rootObject() == root) {
it.remove();
obj->deleteLater();

@ -34,6 +34,8 @@ class Containment;
class Types;
}
class ScreenPool;
namespace KActivities {
class Consumer;
}
@ -62,6 +64,8 @@ public:
void aboutApplication();
void closeApplication();
ScreenPool *screenPool() const;
public slots:
void loadDefaultLayout() override;
void dockContainmentDestroyed(QObject *cont);
@ -77,6 +81,10 @@ private slots:
void alternativesVisibilityChanged(bool visible);
void load();
void addOutput(QScreen *screen);
void primaryOutputChanged();
void screenRemoved(QScreen *screen);
private:
bool appletExists(uint containmentId, uint appletId) const;
void cleanConfig();
@ -92,6 +100,8 @@ private:
KActivities::Consumer *m_activityConsumer;
QPointer<KAboutApplicationDialog> aboutDialog;
ScreenPool *m_screenPool;
};
}

@ -0,0 +1,170 @@
/*
* Copyright 2016 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "screenpool.h"
#include <QGuiApplication>
#include <QScreen>
ScreenPool::ScreenPool(KSharedConfig::Ptr config, QObject *parent)
: QObject(parent),
m_configGroup(KConfigGroup(config, QStringLiteral("ScreenConnectors")))
{
m_configSaveTimer.setSingleShot(true);
connect(&m_configSaveTimer, &QTimer::timeout, this, [this]() {
m_configGroup.sync();
});
}
void ScreenPool::load()
{
m_primaryConnector = QString();
m_connectorForId.clear();
m_idForConnector.clear();
QScreen *primary = qGuiApp->primaryScreen();
if (primary) {
m_primaryConnector = primary->name();
if (!m_primaryConnector.isEmpty()) {
m_connectorForId[0] = m_primaryConnector;
m_idForConnector[m_primaryConnector] = 0;
}
}
//restore the known ids to connector mappings
foreach (const QString &key, m_configGroup.keyList()) {
QString connector = m_configGroup.readEntry(key, QString());
if (!key.isEmpty() && !connector.isEmpty() &&
!m_connectorForId.contains(key.toInt()) &&
!m_idForConnector.contains(connector)) {
m_connectorForId[key.toInt()] = connector;
m_idForConnector[connector] = key.toInt();
} else if (m_idForConnector.value(connector) != key.toInt()) {
m_configGroup.deleteEntry(key);
}
}
// if there are already connected unknown screens, map those
// all needs to be populated as soon as possible, otherwise
// containment->screen() will return an incorrect -1
// at startup, if it' asked before corona::addOutput()
// is performed, driving to the creation of a new containment
for (QScreen *screen : qGuiApp->screens()) {
if (!m_idForConnector.contains(screen->name())) {
insertScreenMapping(firstAvailableId(), screen->name());
}
}
}
ScreenPool::~ScreenPool()
{
m_configGroup.sync();
}
QString ScreenPool::primaryConnector() const
{
return m_primaryConnector;
}
void ScreenPool::setPrimaryConnector(const QString &primary)
{
if (m_primaryConnector == primary) {
return;
}
Q_ASSERT(m_idForConnector.contains(primary));
int oldIdForPrimary = m_idForConnector.value(primary);
m_idForConnector[primary] = 0;
m_connectorForId[0] = primary;
m_idForConnector[m_primaryConnector] = oldIdForPrimary;
m_connectorForId[oldIdForPrimary] = m_primaryConnector;
m_primaryConnector = primary;
save();
}
void ScreenPool::save()
{
QMap<int, QString>::const_iterator i;
for (i = m_connectorForId.constBegin(); i != m_connectorForId.constEnd(); ++i) {
m_configGroup.writeEntry(QString::number(i.key()), i.value());
}
//write to disck every 30 seconds at most
m_configSaveTimer.start(30000);
}
void ScreenPool::insertScreenMapping(int id, const QString &connector)
{
Q_ASSERT(!m_connectorForId.contains(id) || m_connectorForId.value(id) == connector);
Q_ASSERT(!m_idForConnector.contains(connector) || m_idForConnector.value(connector) == id);
if (id == 0) {
m_primaryConnector = connector;
}
m_connectorForId[id] = connector;
m_idForConnector[connector] = id;
save();
}
int ScreenPool::id(const QString &connector) const
{
if (!m_idForConnector.contains(connector)) {
return -1;
}
return m_idForConnector.value(connector);
}
QString ScreenPool::connector(int id) const
{
Q_ASSERT(m_connectorForId.contains(id));
return m_connectorForId.value(id);
}
int ScreenPool::firstAvailableId() const
{
int i = 0;
//find the first integer not stored in m_connectorForId
//m_connectorForId is the only map, so the ids are sorted
foreach (int existingId, m_connectorForId.keys()) {
if (i != existingId) {
return i;
}
++i;
}
return i;
}
QList <int> ScreenPool::knownIds() const
{
return m_connectorForId.keys();
}
#include "moc_screenpool.cpp"

@ -0,0 +1,65 @@
/*
* Copyright 2016 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef SCREENPOOL_H
#define SCREENPOOL_H
#include <QObject>
#include <QHash>
#include <QString>
#include <QTimer>
#include <KConfigGroup>
#include <KSharedConfig>
class ScreenPool : public QObject {
Q_OBJECT
public:
ScreenPool(KSharedConfig::Ptr config, QObject *parent = nullptr);
void load();
~ScreenPool() override;
QString primaryConnector() const;
void setPrimaryConnector(const QString &primary);
void insertScreenMapping(int id, const QString &connector);
int id(const QString &connector) const;
QString connector(int id) const;
int firstAvailableId() const;
//all ids that are known, included screens not enabled at the moment
QList <int> knownIds() const;
private:
void save();
KConfigGroup m_configGroup;
QString m_primaryConnector;
//order is important
QMap<int, QString> m_connectorForId;
QHash<QString, int> m_idForConnector;
QTimer m_configSaveTimer;
};
#endif // SCREENPOOL_H
Loading…
Cancel
Save