Commit 6576a83a authored by David Redondo's avatar David Redondo 🏎
Browse files

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.
parent ab55c027
Pipeline #206993 passed with stage
in 14 minutes and 8 seconds
......@@ -76,6 +76,7 @@ add_subdirectory(highlightwindow)
add_subdirectory(kscreen)
add_subdirectory(screentransform)
add_subdirectory(magiclamp)
add_subdirectory(outputlocator)
add_subdirectory(overview)
add_subdirectory(screenedge)
add_subdirectory(showfps)
......
# 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)
/*
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"
{
"KPlugin": {
"Id": "outputlocator",
"License": "GPL",
"Category": "Appearance",
"EnabledByDefault": true,
"Authors": [
{
"Email": "kde@david-redondo.de",
"Name": "David Redondo"
}
]
},
"org.kde.kwin.effect": {
"internal": true
}
}
/*
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);
}
}
}
}
/*
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;
};
}
/*
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;
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment