Add outputlocator effect

An effect that implements the "identify" functionality of
the screen configuration kcm. It displays a label on each
screen that identifies the screen.
Doing this as a kwin effect allows to correctly handle
the case when outputs are mirrored (on wayland) compared to
absolute positioning of windows which end up on top of each other.
This commit is contained in:
David Redondo 2022-06-29 13:52:52 +02:00
parent ab55c0276f
commit 6576a83aee
7 changed files with 225 additions and 0 deletions

View file

@ -76,6 +76,7 @@ add_subdirectory(highlightwindow)
add_subdirectory(kscreen) add_subdirectory(kscreen)
add_subdirectory(screentransform) add_subdirectory(screentransform)
add_subdirectory(magiclamp) add_subdirectory(magiclamp)
add_subdirectory(outputlocator)
add_subdirectory(overview) add_subdirectory(overview)
add_subdirectory(screenedge) add_subdirectory(screenedge)
add_subdirectory(showfps) add_subdirectory(showfps)

View file

@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: 2022 David Redondo <kde@david-redondo.de>
#
# SPDX-License-Identifier: BSD-3-Clause
kwin4_add_effect_module(kwin4_effect_outputlocator main.cpp outputlocator.cpp)
target_link_libraries(kwin4_effect_outputlocator PRIVATE
kwineffects
Qt::DBus
Qt::Quick
)
install(DIRECTORY qml DESTINATION ${KDE_INSTALL_DATADIR}/kwin/effects/outputlocator)

View file

@ -0,0 +1,14 @@
/*
SPDX-FileCopyrightText: 2022 David Redondo <kde@david-redondo.de>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#include "outputlocator.h"
namespace KWin
{
KWIN_EFFECT_FACTORY(OutputLocatorEffect, "metadata.json.stripped");
}
#include "main.moc"

View file

@ -0,0 +1,17 @@
{
"KPlugin": {
"Id": "outputlocator",
"License": "GPL",
"Category": "Appearance",
"EnabledByDefault": true,
"Authors": [
{
"Email": "kde@david-redondo.de",
"Name": "David Redondo"
}
]
},
"org.kde.kwin.effect": {
"internal": true
}
}

View file

@ -0,0 +1,100 @@
/*
SPDX-FileCopyrightText: 2022 David Redondo <kde@david-redondo.de>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#include "outputlocator.h"
#include <kwinoffscreenquickview.h>
#include <main.h>
#include <QApplication>
#include <QDBusConnection>
#include <QQuickItem>
namespace KWin
{
static QString outputName(const EffectScreen *screen)
{
const auto screens = effects->screens();
const bool shouldShowSerialNumber = std::any_of(screens.cbegin(), screens.cend(), [screen](const EffectScreen *other) {
return other != screen && other->manufacturer() == screen->manufacturer() && other->model() == screen->model();
});
QString name = screen->manufacturer() + QLatin1Char(' ') + screen->model();
if (shouldShowSerialNumber) {
name += QLatin1Char(' ') + screen->serialNumber();
}
return name;
}
OutputLocatorEffect::OutputLocatorEffect(QObject *parent)
: Effect(parent)
, m_qmlUrl(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kwin/effects/outputlocator/qml/OutputLabel.qml")))
{
QDBusConnection::sessionBus().registerObject(QStringLiteral("/org/kde/KWin/Effect/OutputLocator1"),
QStringLiteral("org.kde.KWin.Effect.OutputLocator1"),
this,
QDBusConnection::ExportAllSlots);
connect(&m_showTimer, &QTimer::timeout, this, &OutputLocatorEffect::hide);
}
bool OutputLocatorEffect::isActive() const
{
return m_showTimer.isActive();
}
void OutputLocatorEffect::show()
{
if (isActive()) {
m_showTimer.start(std::chrono::milliseconds(2500));
return;
}
// Needed until Qt6 https://codereview.qt-project.org/c/qt/qtdeclarative/+/361506
m_dummyWindow = std::make_unique<QWindow>();
m_dummyWindow->create();
const auto screens = effects->screens();
for (const auto screen : screens) {
auto scene = new OffscreenQuickScene(this, m_dummyWindow.get());
scene->setSource(m_qmlUrl, {{QStringLiteral("outputName"), outputName(screen)}, {QStringLiteral("resolution"), screen->geometry().size()}, {QStringLiteral("scale"), screen->devicePixelRatio()}});
QRectF geometry(0, 0, scene->rootItem()->implicitWidth(), scene->rootItem()->implicitHeight());
geometry.moveCenter(screen->geometry().center());
scene->setGeometry(geometry.toRect());
connect(scene, &OffscreenQuickView::repaintNeeded, this, [scene] {
effects->addRepaint(scene->geometry());
});
m_scenesByScreens.insert(screen, scene);
}
m_showTimer.start(std::chrono::milliseconds(2500));
}
void OutputLocatorEffect::hide()
{
m_showTimer.stop();
const QRegion repaintRegion = std::accumulate(m_scenesByScreens.cbegin(), m_scenesByScreens.cend(), QRegion(), [](QRegion region, OffscreenQuickScene *scene) {
return region |= scene->geometry();
});
qDeleteAll(m_scenesByScreens);
m_scenesByScreens.clear();
effects->addRepaint(repaintRegion);
}
void OutputLocatorEffect::paintScreen(int mask, const QRegion &region, KWin::ScreenPaintData &data)
{
effects->paintScreen(mask, region, data);
// On X11 all screens are painted at once
if (effects->waylandDisplay()) {
if (auto scene = m_scenesByScreens.value(data.screen())) {
effects->renderOffscreenQuickView(scene);
}
} else {
for (auto scene : m_scenesByScreens) {
effects->renderOffscreenQuickView(scene);
}
}
}
}

View file

@ -0,0 +1,37 @@
/*
SPDX-FileCopyrightText: 2022 David Redondo <kde@david-redondo.de>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#pragma once
#include <kwineffects.h>
#include <QTimer>
#include <QWindow>
namespace KWin
{
class OffscreenQuickScene;
class OutputLocatorEffect : public KWin::Effect
{
Q_OBJECT
public:
explicit OutputLocatorEffect(QObject *parent = nullptr);
void paintScreen(int mask, const QRegion &region, KWin::ScreenPaintData &data) override;
bool isActive() const override;
public Q_SLOTS:
void show();
void hide();
private:
QUrl m_qmlUrl;
QTimer m_showTimer;
std::unique_ptr<QWindow> m_dummyWindow;
QMap<EffectScreen *, OffscreenQuickScene *> m_scenesByScreens;
};
}

View file

@ -0,0 +1,43 @@
/*
SPDX-FileCopyrightText: 2012 Dan Vratil <dvratil@redhat.com>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
import QtQuick 2.1
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents3
Rectangle {
id: root;
property string outputName;
property size resolution;
property double scale;
color: theme.backgroundColor
implicitWidth: childrenRect.width + 2 * childrenRect.x
implicitHeight: childrenRect.height + 2 * childrenRect.y
PlasmaComponents3.Label {
id: displayName
x: units.largeSpacing * 2
y: units.largeSpacing
font.pointSize: theme.defaultFont.pointSize * 3
text: root.outputName;
wrapMode: Text.WordWrap;
horizontalAlignment: Text.AlignHCenter;
}
PlasmaComponents3.Label {
id: modeLabel;
anchors {
horizontalCenter: displayName.horizontalCenter
top: displayName.bottom
}
text: resolution.width + "x" + resolution.height +
(root.scale !== 1 ? "@" + Math.round(root.scale * 100.0) + "%": "")
horizontalAlignment: Text.AlignHCenter;
}
}