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:
parent
ab55c0276f
commit
6576a83aee
7 changed files with 225 additions and 0 deletions
|
@ -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)
|
||||
|
|
13
src/effects/outputlocator/CMakeLists.txt
Normal file
13
src/effects/outputlocator/CMakeLists.txt
Normal 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)
|
14
src/effects/outputlocator/main.cpp
Normal file
14
src/effects/outputlocator/main.cpp
Normal 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"
|
17
src/effects/outputlocator/metadata.json
Normal file
17
src/effects/outputlocator/metadata.json
Normal 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
|
||||
}
|
||||
}
|
100
src/effects/outputlocator/outputlocator.cpp
Normal file
100
src/effects/outputlocator/outputlocator.cpp
Normal 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 ®ion, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
37
src/effects/outputlocator/outputlocator.h
Normal file
37
src/effects/outputlocator/outputlocator.h
Normal 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 ®ion, 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;
|
||||
};
|
||||
}
|
43
src/effects/outputlocator/qml/OutputLabel.qml
Normal file
43
src/effects/outputlocator/qml/OutputLabel.qml
Normal 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;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue