wayland:support Plasma primaryscreen protocol

--use ScreenPool as reference for primary screen.
The new code uses PrimaryOutputWatcher class from
Plasma Shell in order to keep track of primary
screen at all cases.

BUG:448418
FIXED-IN:0.11.0
work/usta_fix_ecm_version_check
Michail Vourlakos 3 years ago
parent 4401fdbb0f
commit b1d57051c2

@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(VERSION 0.10.76)
set(VERSION 0.10.77)
set(AUTHOR "Michail Vourlakos, Smith Ar")
set(EMAIL "mvourlakos@gmail.com, audoban@openmailbox.org")
set(WEBSITE "https://userbase.kde.org/LatteDock")
@ -11,7 +11,7 @@ set(BUG_ADDRESS "https://bugs.kde.org/enter_bug.cgi?product=lattedock")
set(FAQS "https://userbase.kde.org/LatteDock/FAQ")
set(QT_MIN_VERSION "5.15.0")
set(KF5_MIN_VERSION "5.71.0")
set(KF5_MIN_VERSION "5.82.0")
set(KF5_LOCALE_PREFIX "")
@ -46,6 +46,12 @@ if(X11_FOUND AND XCB_XCB_FOUND)
set(HAVE_X11 ON)
endif()
find_package(QtWaylandScanner REQUIRED)
find_package(Qt${QT_MAJOR_VERSION}WaylandClient)
find_package(PlasmaWaylandProtocols 1.6 REQUIRED)
find_package(Wayland REQUIRED COMPONENTS Client)
string(REGEX MATCH "\\.([^]]+)\\." KF5_VERSION_MINOR ${KF5_VERSION})
string(REGEX REPLACE "\\." "" KF5_VERSION_MINOR ${KF5_VERSION_MINOR})

@ -24,7 +24,8 @@ Installation
## Requirements
We recommend to use at least:
**Plasma >= 5.18.0**
**Plasma >= 5.24.0**
**PlasmaWaylandProtocols >= 1.6.0**
**Qt >= 5.15**
@ -41,28 +42,28 @@ Minimum requirements:
Qt5Gui >= 5.15.0
Qt5Dbus >= 5.15.0
KF5Plasma >= 5.71.0
KF5PlasmaQuick >= 5.71.0
KF5Activities >= 5.71.0
KF5CoreAddons >= 5.71.0
KF5GuiAddons >= 5.71.0
KF5DBusAddons >= 5.71.0
KF5Declarative >= 5.71.0
KF5Kirigami2 >= 5.71.0
KF5Wayland >= 5.71.0
KF5Package >= 5.71.0
KF5XmlGui >= 5.71.0
KF5IconThemes >= 5.71.0
KF5KIO >= 5.71.0
KF5I18n >= 5.71.0
KF5Notifications >= 5.71.0
KF5NewStuff >= 5.71.0
KF5Archive >= 5.71.0
KF5GlobalAccel >= 5.71.0
KF5Crash >= 5.71.0
KF5Plasma >= 5.82.0
KF5PlasmaQuick >= 5.82.0
KF5Activities >= 5.82.0
KF5CoreAddons >= 5.82.0
KF5GuiAddons >= 5.82.0
KF5DBusAddons >= 5.82.0
KF5Declarative >= 5.82.0
KF5Kirigami2 >= 5.82.0
KF5Wayland >= 5.82.0
KF5Package >= 5.82.0
KF5XmlGui >= 5.82.0
KF5IconThemes >= 5.82.0
KF5KIO >= 5.82.0
KF5I18n >= 5.82.0
KF5Notifications >= 5.82.0
KF5NewStuff >= 5.82.0
KF5Archive >= 5.82.0
KF5GlobalAccel >= 5.82.0
KF5Crash >= 5.82.0
For X11 support:
KF5WindowSystem >= 5.71.0
KF5WindowSystem >= 5.82.0
Qt5X11Extras >= 5.7.0
libxcb
libxcb-randr

@ -4,7 +4,8 @@ set(lattedock-app_SRCS
infoview.cpp
lattecorona.cpp
screenpool.cpp
main.cpp
primaryoutputwatcher.cpp
main.cpp
coretypes.h
)
@ -49,6 +50,11 @@ ki18n_wrap_ui(lattedock-app_SRCS settings/screensdialog/screensdialog.ui)
ki18n_wrap_ui(lattedock-app_SRCS settings/settingsdialog/settingsdialog.ui)
ki18n_wrap_ui(lattedock-app_SRCS settings/viewsdialog/viewsdialog.ui)
ecm_add_qtwayland_client_protocol(lattedock-app_SRCS
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-primary-output-v1.xml
BASENAME kde-primary-output-v1
)
add_executable(latte-dock ${lattedock-app_SRCS})
include(FakeTarget.cmake)
@ -75,6 +81,7 @@ target_link_libraries(latte-dock
KF5::PlasmaQuick
KF5::WaylandClient
KF5::XmlGui
Wayland::Client
)

@ -32,7 +32,7 @@ class InfoView : public QQuickView
Q_PROPERTY(Plasma::FrameSvg::EnabledBorders enabledBorders READ enabledBorders NOTIFY enabledBordersChanged)
public:
InfoView(Latte::Corona *corona, QString message, QScreen *screen = qGuiApp->primaryScreen(), QWindow *parent = nullptr);
InfoView(Latte::Corona *corona, QString message, QScreen *screen, QWindow *parent = nullptr);
~InfoView() override;
QString validTitle() const;

@ -225,10 +225,9 @@ void Corona::load()
m_templatesManager->init();
m_layoutsManager->init();
connect(this, &Corona::availableScreenRectChangedFrom, this, &Plasma::Corona::availableScreenRectChanged);
connect(this, &Corona::availableScreenRegionChangedFrom, this, &Plasma::Corona::availableScreenRegionChanged);
connect(qGuiApp, &QGuiApplication::primaryScreenChanged, this, &Corona::primaryOutputChanged, Qt::UniqueConnection);
connect(m_screenPool, &ScreenPool::primaryPoolChanged, this, &Corona::screenCountChanged);
connect(this, &Corona::availableScreenRectChangedFrom, this, &Plasma::Corona::availableScreenRectChanged, Qt::UniqueConnection);
connect(this, &Corona::availableScreenRegionChangedFrom, this, &Plasma::Corona::availableScreenRegionChanged, Qt::UniqueConnection);
connect(m_screenPool, &ScreenPool::primaryScreenChanged, this, &Corona::onScreenCountChanged, Qt::UniqueConnection);
QString loadLayoutName = "";
@ -270,7 +269,7 @@ void Corona::load()
//! load screens signals such screenGeometryChanged in order to support
//! plasmoid.screenGeometry properly
for (QScreen *screen : qGuiApp->screens()) {
addOutput(screen);
onScreenAdded(screen);
}
connect(m_layoutsManager->synchronizer(), &Layouts::Synchronizer::initializationFinished, [this]() {
@ -284,8 +283,8 @@ void Corona::load()
m_inStartup = false;
connect(qGuiApp, &QGuiApplication::screenAdded, this, &Corona::addOutput, Qt::UniqueConnection);
connect(qGuiApp, &QGuiApplication::screenRemoved, this, &Corona::screenRemoved, Qt::UniqueConnection);
connect(qGuiApp, &QGuiApplication::screenAdded, this, &Corona::onScreenAdded, Qt::UniqueConnection);
connect(qGuiApp, &QGuiApplication::screenRemoved, this, &Corona::onScreenRemoved, Qt::UniqueConnection);
}
}
@ -494,7 +493,7 @@ int Corona::numScreens() const
QRect Corona::screenGeometry(int id) const
{
const auto screens = qGuiApp->screens();
const QScreen *screen{qGuiApp->primaryScreen()};
const QScreen *screen{m_screenPool->primaryScreen()};
QString screenName;
@ -851,7 +850,7 @@ QRect Corona::availableScreenRectWithCriteria(int id,
return available;
}
void Corona::addOutput(QScreen *screen)
void Corona::onScreenAdded(QScreen *screen)
{
Q_ASSERT(screen);
@ -861,35 +860,42 @@ void Corona::addOutput(QScreen *screen)
m_screenPool->insertScreenMapping(screen->name());
}
connect(screen, &QScreen::geometryChanged, this, [ = ]() {
const int id = m_screenPool->id(screen->name());
if (id >= 0) {
emit screenGeometryChanged(id);
emit availableScreenRegionChanged();
emit availableScreenRectChanged();
}
});
connect(screen, &QScreen::geometryChanged, this, &Corona::onScreenGeometryChanged);
emit availableScreenRectChanged();
emit screenAdded(m_screenPool->id(screen->name()));
screenCountChanged();
onScreenCountChanged();
}
void Corona::primaryOutputChanged()
void Corona::onScreenRemoved(QScreen *screen)
{
m_viewsScreenSyncTimer.start();
disconnect(screen, &QScreen::geometryChanged, this, &Corona::onScreenGeometryChanged);
onScreenCountChanged();
}
void Corona::screenRemoved(QScreen *screen)
void Corona::onScreenCountChanged()
{
screenCountChanged();
m_viewsScreenSyncTimer.start();
}
void Corona::screenCountChanged()
void Corona::onScreenGeometryChanged(const QRect &geometry)
{
m_viewsScreenSyncTimer.start();
Q_UNUSED(geometry);
QScreen *screen = qobject_cast<QScreen *>(sender());
if (!screen) {
return;
}
const int id = m_screenPool->id(screen->name());
if (id >= 0) {
emit screenGeometryChanged(id);
emit availableScreenRegionChanged();
emit availableScreenRectChanged();
}
}
//! the central functions that updates loading/unloading latteviews
@ -901,7 +907,7 @@ void Corona::syncLatteViewsToScreens()
int Corona::primaryScreenId() const
{
return m_screenPool->id(qGuiApp->primaryScreen()->name());
return m_screenPool->primaryScreenId();
}
void Corona::quitApplication()

@ -189,10 +189,10 @@ private slots:
void onAboutToQuit();
void addOutput(QScreen *screen);
void primaryOutputChanged();
void screenRemoved(QScreen *screen);
void screenCountChanged();
void onScreenAdded(QScreen *screen);
void onScreenRemoved(QScreen *screen);
void onScreenCountChanged();
void onScreenGeometryChanged(const QRect &geometry);
void syncLatteViewsToScreens();
private:

@ -482,10 +482,11 @@ QList<Latte::View *> GenericLayout::onlyOriginalViews()
QList<Latte::View *> GenericLayout::sortedLatteViews()
{
return sortedLatteViews(latteViews());
QScreen *primaryScreen = (m_corona ? m_corona->screenPool()->primaryScreen() : nullptr);
return sortedLatteViews(latteViews(), primaryScreen);
}
QList<Latte::View *> GenericLayout::sortedLatteViews(QList<Latte::View *> views)
QList<Latte::View *> GenericLayout::sortedLatteViews(QList<Latte::View *> views, QScreen *primaryScreen)
{
QList<Latte::View *> sortedViews = views;
@ -501,7 +502,7 @@ QList<Latte::View *> GenericLayout::sortedLatteViews(QList<Latte::View *> views)
//! Bottom,Left,Top,Right
for (int i = 0; i < sortedViews.size(); ++i) {
for (int j = 0; j < sortedViews.size() - i - 1; ++j) {
if (viewAtLowerScreenPriority(sortedViews[j], sortedViews[j + 1])
if (viewAtLowerScreenPriority(sortedViews[j], sortedViews[j + 1], primaryScreen)
|| (sortedViews[j]->screen() == sortedViews[j + 1]->screen()
&& viewAtLowerEdgePriority(sortedViews[j], sortedViews[j + 1]))) {
Latte::View *temp = sortedViews[j + 1];
@ -534,7 +535,7 @@ QList<Latte::View *> GenericLayout::sortedLatteViews(QList<Latte::View *> views)
return sortedViews;
}
bool GenericLayout::viewAtLowerScreenPriority(Latte::View *test, Latte::View *base)
bool GenericLayout::viewAtLowerScreenPriority(Latte::View *test, Latte::View *base, QScreen *primaryScreen)
{
if (!base || ! test) {
return true;
@ -542,9 +543,9 @@ bool GenericLayout::viewAtLowerScreenPriority(Latte::View *test, Latte::View *ba
if (base->screen() == test->screen()) {
return false;
} else if (base->screen() != qGuiApp->primaryScreen() && test->screen() == qGuiApp->primaryScreen()) {
} else if (base->screen() != primaryScreen && test->screen() == primaryScreen) {
return false;
} else if (base->screen() == qGuiApp->primaryScreen() && test->screen() != qGuiApp->primaryScreen()) {
} else if (base->screen() == primaryScreen && test->screen() != primaryScreen) {
return true;
} else {
int basePriority = -1;
@ -873,7 +874,7 @@ void GenericLayout::addView(Plasma::Containment *containment)
qDebug() << "Adding View:" << containment->id() << "- Step 3...";
QScreen *nextScreen{qGuiApp->primaryScreen()};
QScreen *nextScreen{m_corona->screenPool()->primaryScreen()};
Data::View viewdata = Layouts::Storage::self()->view(this, containment);
viewdata.screen = Layouts::Storage::self()->expectedViewScreenId(m_corona, viewdata);
@ -953,7 +954,7 @@ void GenericLayout::toggleHiddenState(QString viewName, QString screenName, Plas
return;
}
QString validScreenName = qGuiApp->primaryScreen()->name();
QString validScreenName = m_corona->screenPool()->primaryScreen()->name();
if (!screenName.isEmpty()) {
validScreenName = screenName;
}
@ -1276,7 +1277,7 @@ Layout::ViewsMap GenericLayout::validViewsMap()
return map;
}
QString prmScreenName = qGuiApp->primaryScreen()->name();
QString prmScreenName = m_corona->screenPool()->primaryScreen()->name();
for (const auto containment : m_containments) {
if (Layouts::Storage::self()->isLatteContainment(containment)
@ -1338,7 +1339,7 @@ void GenericLayout::syncLatteViewsToScreens()
//! use valid views map based on active screens
Layout::ViewsMap viewsMap = validViewsMap();
QString prmScreenName = qGuiApp->primaryScreen()->name();
QString prmScreenName = m_corona->screenPool()->primaryScreen()->name();
qDebug() << "PRIMARY SCREEN :: " << prmScreenName;
qDebug() << "LATTEVIEWS MAP :: " << viewsMap;

@ -88,9 +88,9 @@ public:
Plasma::Containment *containmentForId(uint id) const;
QList<Plasma::Containment *> subContainmentsOf(uint id) const;
static bool viewAtLowerScreenPriority(Latte::View *test, Latte::View *base);
static bool viewAtLowerScreenPriority(Latte::View *test, Latte::View *base, QScreen *primaryScreen);
static bool viewAtLowerEdgePriority(Latte::View *test, Latte::View *base);
static QList<Latte::View *> sortedLatteViews(QList<Latte::View *> views);
static QList<Latte::View *> sortedLatteViews(QList<Latte::View *> views, QScreen *primaryScreen);
QList<Latte::View *> sortedLatteViews();
virtual QList<Latte::View *> viewsWithPlasmaShortcuts();

@ -10,6 +10,7 @@
#include "importer.h"
#include "manager.h"
#include "../apptypes.h"
#include "../screenpool.h"
#include "../data/layoutdata.h"
#include "../lattecorona.h"
#include "../layout/centrallayout.h"
@ -383,12 +384,12 @@ QList<Latte::View *> Synchronizer::currentViewsWithPlasmaShortcuts() const
QList<Latte::View *> Synchronizer::sortedCurrentViews() const
{
return Layout::GenericLayout::sortedLatteViews(currentViews());
return Layout::GenericLayout::sortedLatteViews(currentViews(), m_manager->corona()->screenPool()->primaryScreen());
}
QList<Latte::View *> Synchronizer::sortedCurrentOriginalViews() const
{
return Layout::GenericLayout::sortedLatteViews(currentOriginalViews());
return Layout::GenericLayout::sortedLatteViews(currentOriginalViews(), m_manager->corona()->screenPool()->primaryScreen());
}
QList<Latte::View *> Synchronizer::viewsBasedOnActivityId(const QString &id) const

@ -6,6 +6,7 @@
#include "screenpool.h"
// local
#include "../../primaryoutputwatcher.h"
#include "../../tools/commontools.h"
// Qt
@ -25,7 +26,8 @@ namespace Latte {
namespace PlasmaExtended {
ScreenPool::ScreenPool(QObject *parent)
: QObject(parent)
: QObject(parent),
m_primaryWatcher(new PrimaryOutputWatcher(this))
{
m_plasmarcConfig = KSharedConfig::openConfig(PLASMARC);
m_screensGroup = KConfigGroup(m_plasmarcConfig, "ScreenConnectors");
@ -108,7 +110,7 @@ int ScreenPool::id(const QString &connector) const
{
if (!m_idForConnector.contains(connector)) {
//! return 0 for primary screen, -1 for not found
return qGuiApp->primaryScreen()->name() == connector ? 0 : -1;
return m_primaryWatcher->primaryScreen()->name() == connector ? 0 : -1;
}
return m_idForConnector.value(connector);

@ -15,6 +15,8 @@
#include <KConfigGroup>
#include <KSharedConfig>
class PrimaryOutputWatcher;
namespace Latte {
namespace PlasmaExtended {
@ -45,6 +47,8 @@ private:
KSharedConfig::Ptr m_plasmarcConfig;
KConfigGroup m_screensGroup;
PrimaryOutputWatcher *m_primaryWatcher;
};
}

@ -0,0 +1,166 @@
/*
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "primaryoutputwatcher.h"
#include <KWindowSystem>
#include <QDebug>
#include <QGuiApplication>
#include <QScreen>
#include "qwayland-kde-primary-output-v1.h"
#include <KWayland/Client/connection_thread.h>
#include <KWayland/Client/registry.h>
#include <config-latte.h>
#if HAVE_X11
#include <QTimer> //Used only in x11 case
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <private/qtx11extras_p.h>
#else
#include <QX11Info>
#endif
#include <xcb/randr.h>
#include <xcb/xcb.h>
#include <xcb/xcb_event.h>
#endif
class WaylandPrimaryOutput : public QObject, public QtWayland::kde_primary_output_v1
{
Q_OBJECT
public:
WaylandPrimaryOutput(struct ::wl_registry *registry, int id, int version, QObject *parent)
: QObject(parent)
, QtWayland::kde_primary_output_v1(registry, id, version)
{
}
void kde_primary_output_v1_primary_output(const QString &outputName) override
{
Q_EMIT primaryOutputChanged(outputName);
}
Q_SIGNALS:
void primaryOutputChanged(const QString &outputName);
};
PrimaryOutputWatcher::PrimaryOutputWatcher(QObject *parent)
: QObject(parent)
{
#if HAVE_X11
if (KWindowSystem::isPlatformX11()) {
m_primaryOutputName = qGuiApp->primaryScreen()->name();
qGuiApp->installNativeEventFilter(this);
const xcb_query_extension_reply_t *reply = xcb_get_extension_data(QX11Info::connection(), &xcb_randr_id);
m_xrandrExtensionOffset = reply->first_event;
setPrimaryOutputName(qGuiApp->primaryScreen()->name());
connect(qGuiApp, &QGuiApplication::primaryScreenChanged, this, [this](QScreen *newPrimary) {
setPrimaryOutputName(newPrimary->name());
});
}
#endif
if (KWindowSystem::isPlatformWayland()) {
setupRegistry();
}
}
void PrimaryOutputWatcher::setPrimaryOutputName(const QString &newOutputName)
{
if (newOutputName != m_primaryOutputName) {
const QString oldOutputName = m_primaryOutputName;
m_primaryOutputName = newOutputName;
Q_EMIT primaryOutputNameChanged(oldOutputName, newOutputName);
}
}
void PrimaryOutputWatcher::setupRegistry()
{
auto m_connection = KWayland::Client::ConnectionThread::fromApplication(this);
if (!m_connection) {
return;
}
// Asking for primaryOutputName() before this happened, will return qGuiApp->primaryScreen()->name() anyways, so set it so the primaryOutputNameChange will
// have parameters that are coherent
m_primaryOutputName = qGuiApp->primaryScreen()->name();
m_registry = new KWayland::Client::Registry(this);
connect(m_registry, &KWayland::Client::Registry::interfaceAnnounced, this, [this](const QByteArray &interface, quint32 name, quint32 version) {
if (interface == WaylandPrimaryOutput::interface()->name) {
auto m_outputManagement = new WaylandPrimaryOutput(m_registry->registry(), name, version, this);
connect(m_outputManagement, &WaylandPrimaryOutput::primaryOutputChanged, this, [this](const QString &outputName) {
m_primaryOutputWayland = outputName;
// Only set the outputName when there's a QScreen attached to it
if (screenForName(outputName)) {
setPrimaryOutputName(outputName);
}
});
}
});
// In case the outputName was received before Qt reported the screen
connect(qGuiApp, &QGuiApplication::screenAdded, this, [this](QScreen *screen) {
if (screen->name() == m_primaryOutputWayland) {
setPrimaryOutputName(m_primaryOutputWayland);
}
});
m_registry->create(m_connection);
m_registry->setup();
}
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
bool PrimaryOutputWatcher::nativeEventFilter(const QByteArray &eventType, void *message, long int *result)
#else
bool PrimaryOutputWatcher::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)
#endif
{
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 primaryOutputName will be the same by then
if (eventType[0] != 'x') {
return false;
}
xcb_generic_event_t *ev = static_cast<xcb_generic_event_t *>(message);
const auto responseType = XCB_EVENT_RESPONSE_TYPE(ev);
if (responseType == m_xrandrExtensionOffset + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
QTimer::singleShot(0, this, [this]() {
setPrimaryOutputName(qGuiApp->primaryScreen()->name());
});
}
#endif
return false;
}
QScreen *PrimaryOutputWatcher::screenForName(const QString &outputName) const
{
const auto screens = qGuiApp->screens();
for (auto screen : screens) {
if (screen->name() == outputName) {
return screen;
}
}
return nullptr;
}
QScreen *PrimaryOutputWatcher::primaryScreen() const
{
auto screen = screenForName(m_primaryOutputName);
if (!screen) {
qDebug() << "PrimaryOutputWatcher: Could not find primary screen:" << m_primaryOutputName;
return qGuiApp->primaryScreen();
}
return screen;
}
#include "primaryoutputwatcher.moc"

@ -0,0 +1,58 @@
/*
SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef PRIMARYOUTPUTWATCHER_H
#define PRIMARYOUTPUTWATCHER_H
#include <QAbstractNativeEventFilter>
#include <QObject>
namespace KWayland
{
namespace Client
{
class Registry;
class ConnectionThread;
}
}
class QScreen;
class PrimaryOutputWatcher : public QObject, public QAbstractNativeEventFilter
{
Q_OBJECT
public:
PrimaryOutputWatcher(QObject *parent);
QScreen *primaryScreen() const;
QScreen *screenForName(const QString &outputName) const;
Q_SIGNALS:
void primaryOutputNameChanged(const QString &oldOutputName, const QString &newOutputName);
protected:
friend class WaylandOutputDevice;
void setPrimaryOutputName(const QString &outputName);
private:
void setupRegistry();
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override;
#else
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
#endif
// All
QString m_primaryOutputName;
// Wayland
KWayland::Client::Registry *m_registry = nullptr;
QString m_primaryOutputWayland;
// Xrandr
int m_xrandrExtensionOffset;
};
#endif // PRIMARYOUTPUTWATCHER_H

@ -8,6 +8,7 @@
// local
#include <config-latte.h>
#include "primaryoutputwatcher.h"
// Qt
#include <QDebug>
@ -17,6 +18,7 @@
// KDE
#include <KLocalizedString>
#include <KWindowSystem>
// X11
#if HAVE_X11
@ -32,10 +34,9 @@ const int ScreenPool::FIRSTSCREENID;
ScreenPool::ScreenPool(KSharedConfig::Ptr config, QObject *parent)
: QObject(parent),
m_configGroup(KConfigGroup(config, QStringLiteral("ScreenConnectors")))
m_configGroup(KConfigGroup(config, QStringLiteral("ScreenConnectors"))),
m_primaryWatcher(new PrimaryOutputWatcher(this))
{
qApp->installNativeEventFilter(this);
m_configSaveTimer.setSingleShot(true);
connect(&m_configSaveTimer, &QTimer::timeout, this, [this]() {
m_configGroup.sync();
@ -52,15 +53,8 @@ ScreenPool::~ScreenPool()
void ScreenPool::load()
{
m_lastPrimaryConnector = QString();
m_screensTable.clear();
QScreen *primary = qGuiApp->primaryScreen();
if (primary) {
m_lastPrimaryConnector = primary->name();
}
//restore the known ids to connector mappings
for (const QString &key : m_configGroup.keyList()) {
if (key.toInt() <= 0) {
@ -94,6 +88,20 @@ void ScreenPool::load()
onScreenAdded(screen);
}
if (KWindowSystem::isPlatformX11()) {
connect(qGuiApp, &QGuiApplication::primaryScreenChanged, this, &ScreenPool::primaryScreenChanged, Qt::UniqueConnection);
}
connect(m_primaryWatcher, &PrimaryOutputWatcher::primaryOutputNameChanged, this, &ScreenPool::onPrimaryOutputNameChanged, Qt::UniqueConnection);
}
void ScreenPool::onPrimaryOutputNameChanged(const QString &oldOutputName, const QString &newOutputName)
{
Q_UNUSED(oldOutputName);
Q_UNUSED(newOutputName);
emit primaryScreenChanged(m_primaryWatcher->primaryScreen());
}
void ScreenPool::onScreenAdded(const QScreen *screen)
@ -165,15 +173,17 @@ void ScreenPool::removeScreens(const Latte::Data::ScreensTable &obsoleteScreens)
int ScreenPool::primaryScreenId() const
{
return id(qGuiApp->primaryScreen()->name());
return id(primaryScreen()->name());
}
QList<int> ScreenPool::secondaryScreenIds() const
{
QList<int> secondaryscreens;
QScreen *primary{primaryScreen()};
for (const auto scr : qGuiApp->screens()) {
if (scr == qGuiApp->primaryScreen()) {
if (scr == primary) {
continue;
}
@ -274,10 +284,15 @@ bool ScreenPool::isScreenActive(int screenId) const
return false;
}
QScreen *ScreenPool::primaryScreen() const
{
return m_primaryWatcher->primaryScreen();
}
QScreen *ScreenPool::screenForId(int id)
{
const auto screens = qGuiApp->screens();
QScreen *screen{qGuiApp->primaryScreen()};
QScreen *screen{primaryScreen()};
if (hasScreenId(id)) {
QString scrName = connector(id);
@ -292,41 +307,4 @@ QScreen *ScreenPool::screenForId(int id)
return screen;
}
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<xcb_generic_event_t *>(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() != m_lastPrimaryConnector) {
//new screen?
if (id(qGuiApp->primaryScreen()->name()) < 0) {
insertScreenMapping(qGuiApp->primaryScreen()->name());
}
m_lastPrimaryConnector = qGuiApp->primaryScreen()->name();
emit primaryPoolChanged();
}
}
#endif
return false;
}
}
#include "moc_screenpool.cpp"

@ -16,15 +16,16 @@
#include <QScreen>
#include <QString>
#include <QTimer>
#include <QAbstractNativeEventFilter>
// KDE
#include <KConfigGroup>
#include <KSharedConfig>
class PrimaryOutputWatcher;
namespace Latte {
class ScreenPool : public QObject, public QAbstractNativeEventFilter
class ScreenPool : public QObject
{
Q_OBJECT
@ -33,9 +34,10 @@ public:
static const int NOSCREENID = -1;
ScreenPool(KSharedConfig::Ptr config, QObject *parent = nullptr);
void load();
~ScreenPool() override;
void load();
bool hasScreenId(int screenId) const;
bool isScreenActive(int screenId) const;
int primaryScreenId() const;
@ -50,20 +52,20 @@ public:
QString connector(int id) const;
QScreen *screenForId(int id);
QScreen *primaryScreen() const;
Latte::Data::ScreensTable screensTable();
signals:
void primaryPoolChanged();
void primaryScreenChanged(QScreen *screen);
void screenGeometryChanged();
protected:
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) Q_DECL_OVERRIDE;
int firstAvailableId() const;
private slots:
void updateScreenGeometry(const QScreen *screen);
void onPrimaryOutputNameChanged(const QString &oldOutputName, const QString &newOutputName);
void onScreenAdded(const QScreen *screen);
void onScreenRemoved(const QScreen *screen);
@ -75,10 +77,10 @@ private:
Latte::Data::ScreensTable m_screensTable;
KConfigGroup m_configGroup;
//! used to workaround a bug under X11 when primary screen changes and no screenChanged signal is emitted
QString m_lastPrimaryConnector;
QTimer m_configSaveTimer;
PrimaryOutputWatcher *m_primaryWatcher;
};
}

@ -209,7 +209,7 @@ void Positioner::init()
});
connect(qGuiApp, &QGuiApplication::screenAdded, this, &Positioner::onScreenChanged);
connect(qGuiApp, &QGuiApplication::primaryScreenChanged, this, &Positioner::onScreenChanged);
connect(m_corona->screenPool(), &ScreenPool::primaryScreenChanged, this, &Positioner::onScreenChanged);
connect(m_view, &Latte::View::visibilityChanged, this, &Positioner::initDelayedSignals);
@ -435,11 +435,12 @@ void Positioner::reconsiderScreen()
}
bool screenExists{false};
QScreen *primaryScreen{m_corona->screenPool()->primaryScreen()};
//!check if the associated screen is running
for (const auto scr : qGuiApp->screens()) {
if (m_screenNameToFollow == scr->name()
|| (m_view->onPrimary() && scr == qGuiApp->primaryScreen())) {
|| (m_view->onPrimary() && scr == primaryScreen)) {
screenExists = true;
}
}
@ -447,12 +448,12 @@ void Positioner::reconsiderScreen()
qDebug() << "dock screen exists ::: " << screenExists;
//! 1.a primary dock must be always on the primary screen
if (m_view->onPrimary() && (m_screenNameToFollow != qGuiApp->primaryScreen()->name()
|| m_screenToFollow != qGuiApp->primaryScreen()
|| m_view->screen() != qGuiApp->primaryScreen())) {
if (m_view->onPrimary() && (m_screenNameToFollow != primaryScreen->name()
|| m_screenToFollow != primaryScreen
|| m_view->screen() != primaryScreen)) {
//! case 1
qDebug() << "reached case 1: of updating dock primary screen...";
setScreenToFollow(qGuiApp->primaryScreen());
setScreenToFollow(primaryScreen);
} else if (!m_view->onPrimary()) {
//! 2.an explicit dock must be always on the correct associated screen
//! there are cases that window manager misplaces the dock, this function
@ -1028,7 +1029,7 @@ void Positioner::initSignalingForLocationChangeSliding()
//! SCREEN
if (!m_nextScreenName.isEmpty()) {
bool nextonprimary = (m_nextScreenName == Latte::Data::Screen::ONPRIMARYNAME);
m_nextScreen = qGuiApp->primaryScreen();
m_nextScreen = m_corona->screenPool()->primaryScreen();
if (!nextonprimary) {
for (const auto scr : qGuiApp->screens()) {
@ -1210,7 +1211,7 @@ void Positioner::setNextLocation(const QString layoutName, const int screensGrou
|| (!m_view->onPrimary() && nextonprimary) /*explicit -> primary*/
|| (!m_view->onPrimary() && !nextonprimary && screenName != currentScreenName()) ) { /*explicit -> new_explicit*/
QString nextscreenname = nextonprimary ? qGuiApp->primaryScreen()->name() : screenName;
QString nextscreenname = nextonprimary ? m_corona->screenPool()->primaryScreen()->name() : screenName;
if (currentScreenName() == nextscreenname) {
m_view->setOnPrimary(nextonprimary);

@ -76,6 +76,8 @@ View::View(Plasma::Corona *corona, QScreen *targetScreen, bool byPassX11WM)
//this is disabled because under wayland breaks Views positioning
//setVisible(false);
m_corona = qobject_cast<Latte::Corona *>(corona);
//! needs to be created after Effects because it catches some of its signals
//! and avoid a crash from View::winId() at the same time
m_positioner = new ViewPart::Positioner(this);
@ -110,7 +112,8 @@ View::View(Plasma::Corona *corona, QScreen *targetScreen, bool byPassX11WM)
if (targetScreen) {
m_positioner->setScreenToFollow(targetScreen);
} else {
m_positioner->setScreenToFollow(qGuiApp->primaryScreen());
qDebug() << "org.kde.view :::: corona was found properly!!!";
m_positioner->setScreenToFollow(m_corona->screenPool()->primaryScreen());
}
m_releaseGrabTimer.setInterval(400);
@ -206,8 +209,6 @@ View::View(Plasma::Corona *corona, QScreen *targetScreen, bool byPassX11WM)
emit containmentActionsChanged();
}, Qt::DirectConnection);
m_corona = qobject_cast<Latte::Corona *>(this->corona());
if (m_corona) {
connect(m_corona, &Latte::Corona::viewLocationChanged, this, &View::dockLocationChanged);
}

@ -63,7 +63,7 @@ NoDisplay=true
X-KDE-PluginInfo-Author=Michail Vourlakos, Smith Ar
X-KDE-PluginInfo-Email=mvourlakos@gmail.com, audoban@openmailbox.org
X-KDE-PluginInfo-Name=org.kde.latte.containment
X-KDE-PluginInfo-Version=0.10.76
X-KDE-PluginInfo-Version=0.10.77
X-KDE-PluginInfo-Website=https://userbase.kde.org/LatteDock
X-KDE-PluginInfo-Category=
X-KDE-PluginInfo-Depends=

@ -70,7 +70,7 @@ X-Plasma-Provides=org.kde.plasma.multitasking
X-KDE-PluginInfo-Author=Michail Vourlakos, Smith Ar
X-KDE-PluginInfo-Email=mvourlakos@gmail.com, audoban@openmailbox.org
X-KDE-PluginInfo-Name=org.kde.latte.plasmoid
X-KDE-PluginInfo-Version=0.10.76
X-KDE-PluginInfo-Version=0.10.77
X-KDE-PluginInfo-Website=https://userbase.kde.org/LatteDock
X-KDE-PluginInfo-Category=Windows and Tasks
X-KDE-PluginInfo-License=GPL v2+

@ -107,5 +107,5 @@ X-KDE-PluginInfo-Author=Michail Vourlakos, Smith Ar
X-KDE-PluginInfo-Email=mvourlakos@gmail.com, audoban@openmailbox.org
X-KDE-PluginInfo-License=GPLv3+
X-KDE-PluginInfo-Name=org.kde.latte.shell
X-KDE-PluginInfo-Version=0.10.76
X-KDE-PluginInfo-Version=0.10.77
X-KDE-PluginInfo-Website=https://userbase.kde.org/LatteDock

Loading…
Cancel
Save