mirror of
https://github.com/KDE/latte-dock.git
synced 2025-03-09 16:58:16 +03:00
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
This commit is contained in:
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})
|
||||
|
||||
|
43
README.md
43
README.md
@ -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,6 +4,7 @@ set(lattedock-app_SRCS
|
||||
infoview.cpp
|
||||
lattecorona.cpp
|
||||
screenpool.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)
|
||||
{
|
||||
disconnect(screen, &QScreen::geometryChanged, this, &Corona::onScreenGeometryChanged);
|
||||
onScreenCountChanged();
|
||||
}
|
||||
|
||||
void Corona::onScreenCountChanged()
|
||||
{
|
||||
m_viewsScreenSyncTimer.start();
|
||||
}
|
||||
|
||||
void Corona::screenRemoved(QScreen *screen)
|
||||
void Corona::onScreenGeometryChanged(const QRect &geometry)
|
||||
{
|
||||
screenCountChanged();
|
||||
}
|
||||
Q_UNUSED(geometry);
|
||||
|
||||
void Corona::screenCountChanged()
|
||||
{
|
||||
m_viewsScreenSyncTimer.start();
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
|
166
app/primaryoutputwatcher.cpp
Normal file
166
app/primaryoutputwatcher.cpp
Normal file
@ -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"
|
||||
|
58
app/primaryoutputwatcher.h
Normal file
58
app/primaryoutputwatcher.h
Normal file
@ -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…
x
Reference in New Issue
Block a user