1 /*
2 SPDX-FileCopyrightText: 2014 Martin Klapetek <mklapetek@kde.org>
3 SPDX-FileCopyrightText: 2016 Sebastian Kügler <sebas@kde.org>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "osd.h"
9
10 #include "kscreen_daemon_debug.h"
11
12 #include "../common/utils.h"
13
14 #include <KScreen/Mode>
15
16 #include <QCursor>
17 #include <QGuiApplication>
18 #include <QScreen>
19 #include <QStandardPaths>
20 #include <QTimer>
21
22 #include <KDeclarative/QmlObjectSharedEngine>
23
24 using namespace KScreen;
25
Osd(const KScreen::OutputPtr & output,QObject * parent)26 Osd::Osd(const KScreen::OutputPtr &output, QObject *parent)
27 : QObject(parent)
28 , m_output(output)
29 {
30 connect(output.data(), &KScreen::Output::isConnectedChanged, this, &Osd::onOutputAvailabilityChanged);
31 connect(output.data(), &KScreen::Output::isEnabledChanged, this, &Osd::onOutputAvailabilityChanged);
32 connect(output.data(), &KScreen::Output::currentModeIdChanged, this, &Osd::updatePosition);
33 connect(output.data(), &KScreen::Output::destroyed, this, &Osd::hideOsd);
34 }
35
~Osd()36 Osd::~Osd()
37 {
38 }
39
initOsd()40 bool Osd::initOsd()
41 {
42 if (m_osdObject) {
43 return true;
44 }
45
46 const QString osdPath = QStandardPaths::locate(QStandardPaths::QStandardPaths::GenericDataLocation, QStringLiteral("kded_kscreen/qml/Osd.qml"));
47 if (osdPath.isEmpty()) {
48 qCWarning(KSCREEN_KDED) << "Failed to find OSD QML file" << osdPath;
49 return false;
50 }
51
52 m_osdObject = new KDeclarative::QmlObjectSharedEngine(this);
53 m_osdObject->setSource(QUrl::fromLocalFile(osdPath));
54
55 if (m_osdObject->status() != QQmlComponent::Ready) {
56 qCWarning(KSCREEN_KDED) << "Failed to load OSD QML file" << osdPath;
57 delete m_osdObject;
58 m_osdObject = nullptr;
59 return false;
60 }
61
62 m_timeout = m_osdObject->rootObject()->property("timeout").toInt();
63 m_osdTimer = new QTimer(this);
64 m_osdTimer->setSingleShot(true);
65 connect(m_osdTimer, &QTimer::timeout, this, &Osd::hideOsd);
66 return true;
67 }
68
showGenericOsd(const QString & icon,const QString & text)69 void Osd::showGenericOsd(const QString &icon, const QString &text)
70 {
71 if (!initOsd()) {
72 return;
73 }
74
75 m_outputGeometry = m_output->geometry();
76 auto *rootObject = m_osdObject->rootObject();
77 rootObject->setProperty("itemSource", QStringLiteral("OsdItem.qml"));
78 rootObject->setProperty("infoText", text);
79 rootObject->setProperty("icon", icon);
80
81 showOsd();
82 }
83
showOutputIdentifier(const KScreen::OutputPtr & output)84 void Osd::showOutputIdentifier(const KScreen::OutputPtr &output)
85 {
86 if (!initOsd()) {
87 return;
88 }
89
90 m_outputGeometry = output->geometry();
91
92 auto *rootObject = m_osdObject->rootObject();
93 auto mode = output->currentMode();
94 QSize realSize = mode->size();
95 if (!output->isHorizontal()) {
96 realSize.transpose();
97 }
98 rootObject->setProperty("itemSource", QStringLiteral("OutputIdentifier.qml"));
99 rootObject->setProperty("modeName", Utils::sizeToString(realSize));
100 rootObject->setProperty("outputName", Utils::outputName(output));
101 showOsd();
102 }
103
showActionSelector()104 void Osd::showActionSelector()
105 {
106 if (!m_osdActionSelector) {
107 const QString osdPath = QStandardPaths::locate(QStandardPaths::QStandardPaths::GenericDataLocation, QStringLiteral("kded_kscreen/qml/OsdSelector.qml"));
108 if (osdPath.isEmpty()) {
109 qCWarning(KSCREEN_KDED) << "Failed to find action selector OSD QML file" << osdPath;
110 return;
111 }
112 m_osdActionSelector = new KDeclarative::QmlObjectSharedEngine(this);
113 m_osdActionSelector->setSource(QUrl::fromLocalFile(osdPath));
114
115 if (m_osdActionSelector->status() != QQmlComponent::Ready) {
116 qCWarning(KSCREEN_KDED) << "Failed to load OSD QML file" << osdPath;
117 delete m_osdActionSelector;
118 m_osdActionSelector = nullptr;
119 return;
120 }
121
122 auto *rootObject = m_osdActionSelector->rootObject();
123 connect(rootObject, SIGNAL(clicked(int)), this, SLOT(onOsdActionSelected(int)));
124 }
125 if (auto *rootObject = m_osdActionSelector->rootObject()) {
126 // On wayland, we use m_output to set an action on OSD position
127 if (qGuiApp->platformName() == QLatin1String("wayland")) {
128 rootObject->setProperty("screenGeometry", m_output->geometry());
129 }
130 rootObject->setProperty("visible", true);
131 } else {
132 qCWarning(KSCREEN_KDED) << "Could not get root object for action selector.";
133 }
134 }
135
onOsdActionSelected(int action)136 void Osd::onOsdActionSelected(int action)
137 {
138 Q_EMIT osdActionSelected(static_cast<OsdAction::Action>(action));
139 hideOsd();
140 }
141
onOutputAvailabilityChanged()142 void Osd::onOutputAvailabilityChanged()
143 {
144 if (!m_output || !m_output->isConnected() || !m_output->isEnabled() || !m_output->currentMode()) {
145 hideOsd();
146 }
147 }
148
updatePosition()149 void Osd::updatePosition()
150 {
151 if (!initOsd()) {
152 return;
153 }
154
155 const auto geometry = m_output->geometry();
156 if (!geometry.isValid()) {
157 hideOsd();
158 }
159
160 auto *rootObject = m_osdObject->rootObject();
161
162 const int dialogWidth = rootObject->property("width").toInt();
163 const int dialogHeight = rootObject->property("height").toInt();
164 const int relx = geometry.x();
165 const int rely = geometry.y();
166 const int pos_x = relx + (geometry.width() - dialogWidth) / 2;
167 const int pos_y = rely + (geometry.height() - dialogHeight) / 2;
168
169 rootObject->setProperty("x", pos_x);
170 rootObject->setProperty("y", pos_y);
171 }
172
showOsd()173 void Osd::showOsd()
174 {
175 m_osdTimer->stop();
176
177 auto *rootObject = m_osdObject->rootObject();
178
179 // only animate on X11, wayland plugin doesn't support this and
180 // pukes loads of warnings into our logs
181 if (qGuiApp->platformName() == QLatin1String("xcb")) {
182 if (rootObject->property("timeout").toInt() > 0) {
183 rootObject->setProperty("animateOpacity", false);
184 rootObject->setProperty("opacity", 1);
185 rootObject->setProperty("animateOpacity", true);
186 rootObject->setProperty("opacity", 0);
187 }
188 }
189 rootObject->setProperty("visible", true);
190 QTimer::singleShot(0, this, &Osd::updatePosition);
191 if (m_timeout > 0) {
192 m_osdTimer->start(m_timeout);
193 }
194 }
195
hideOsd()196 void Osd::hideOsd()
197 {
198 if (m_osdActionSelector) {
199 if (auto *rootObject = m_osdActionSelector->rootObject()) {
200 rootObject->setProperty("visible", false);
201 }
202 }
203 if (m_osdObject) {
204 if (auto *rootObject = m_osdObject->rootObject()) {
205 rootObject->setProperty("visible", false);
206 }
207 }
208 }
209