1 /*
2 SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org>
3 SPDX-FileCopyrightText: 2016 Sebastian Kügler <sebas@kde.org>
4 SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@broulik.de>
5 SPDX-FileCopyrightText: 2020 Roman Gilg <subdiff@gmail.com>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 #include "daemon.h"
10
11 #include "../../common/orientation_sensor.h"
12 #include "config.h"
13 #include "generator.h"
14 #include "kdisplay_daemon_debug.h"
15 #include "kdisplayadaptor.h"
16 #include "osdmanager.h"
17
18 #include <disman/configmonitor.h>
19 #include <disman/getconfigoperation.h>
20 #include <disman/log.h>
21 #include <disman/output.h>
22 #include <disman/setconfigoperation.h>
23
24 #include <KActionCollection>
25 #include <KGlobalAccel>
26 #include <KLocalizedString>
27 #include <KPluginFactory>
28
29 #include <QAction>
30 #include <QOrientationReading>
31
32 K_PLUGIN_CLASS_WITH_JSON(KDisplayDaemon, "kdisplayd.json")
33
KDisplayDaemon(QObject * parent,const QList<QVariant> &)34 KDisplayDaemon::KDisplayDaemon(QObject* parent, const QList<QVariant>&)
35 : KDEDModule(parent)
36 , m_monitoring{false}
37 , m_orientationSensor(new OrientationSensor(this))
38 {
39 Disman::Log::instance();
40
41 connect(new Disman::GetConfigOperation,
42 &Disman::GetConfigOperation::finished,
43 this,
44 &KDisplayDaemon::init);
45 }
46
init(Disman::ConfigOperation * op)47 void KDisplayDaemon::init(Disman::ConfigOperation* op)
48 {
49 if (op->has_error()) {
50 qCWarning(KDISPLAY_KDED) << "Initial config has error.";
51 return;
52 }
53
54 m_monitoredConfig = qobject_cast<Disman::GetConfigOperation*>(op)->config();
55 auto cfg = m_monitoredConfig.get();
56
57 qCDebug(KDISPLAY_KDED) << "Config" << cfg << "is ready";
58 Disman::ConfigMonitor::instance()->add_config(m_monitoredConfig);
59
60 update_auto_rotate();
61 setMonitorForChanges(true);
62
63 KActionCollection* coll = new KActionCollection(this);
64 QAction* action = coll->addAction(QStringLiteral("display"));
65 action->setText(i18n("Switch Display"));
66 QList<QKeySequence> switchDisplayShortcuts({Qt::Key_Display, Qt::MetaModifier + Qt::Key_P});
67 KGlobalAccel::self()->setGlobalShortcut(action, switchDisplayShortcuts);
68 connect(action, &QAction::triggered, this, &KDisplayDaemon::displayButton);
69
70 new KdisplayAdaptor(this);
71 // Initialize OSD manager to register its dbus interface
72 m_osdManager = new OsdManager(this);
73
74 connect(cfg, &Disman::Config::output_added, this, &KDisplayDaemon::applyConfig);
75 connect(cfg, &Disman::Config::output_removed, this, &KDisplayDaemon::applyConfig);
76
77 connect(m_orientationSensor,
78 &OrientationSensor::availableChanged,
79 this,
80 &KDisplayDaemon::updateOrientation);
81 connect(m_orientationSensor,
82 &OrientationSensor::valueChanged,
83 this,
84 &KDisplayDaemon::updateOrientation);
85
86 applyConfig();
87
88 m_startingUp = false;
89 }
90
update_auto_rotate()91 void KDisplayDaemon::update_auto_rotate()
92 {
93 assert(m_monitoredConfig);
94 m_orientationSensor->setEnabled(Config(m_monitoredConfig).autoRotationRequested());
95 }
96
updateOrientation()97 void KDisplayDaemon::updateOrientation()
98 {
99 assert(m_monitoredConfig);
100
101 const auto features = m_monitoredConfig->supported_features();
102 if (!features.testFlag(Disman::Config::Feature::AutoRotation)
103 || !features.testFlag(Disman::Config::Feature::TabletMode)) {
104 return;
105 }
106
107 if (!m_orientationSensor->available() || !m_orientationSensor->enabled()) {
108 return;
109 }
110
111 const auto orientation = m_orientationSensor->value();
112 if (orientation == QOrientationReading::Undefined) {
113 // Orientation sensor went off. Do not change current orientation.
114 return;
115 }
116 if (orientation == QOrientationReading::FaceUp
117 || orientation == QOrientationReading::FaceDown) {
118 // We currently don't do anything with FaceUp/FaceDown, but in the future we could use them
119 // to shut off and switch on again a display when display is facing downwards/upwards.
120 return;
121 }
122
123 Config(m_monitoredConfig).setDeviceOrientation(orientation);
124 if (m_monitoring) {
125 doApplyConfig(m_monitoredConfig);
126 } else {
127 m_configDirty = true;
128 }
129 }
130
doApplyConfig(Disman::ConfigPtr const & config)131 void KDisplayDaemon::doApplyConfig(Disman::ConfigPtr const& config)
132 {
133 qCDebug(KDISPLAY_KDED) << "Do set and apply specific config";
134
135 m_monitoredConfig->apply(config);
136 refreshConfig();
137 }
138
refreshConfig()139 void KDisplayDaemon::refreshConfig()
140 {
141 setMonitorForChanges(false);
142 m_configDirty = false;
143 Disman::ConfigMonitor::instance()->add_config(m_monitoredConfig);
144
145 connect(new Disman::SetConfigOperation(m_monitoredConfig),
146 &Disman::SetConfigOperation::finished,
147 this,
148 [this]() {
149 qCDebug(KDISPLAY_KDED) << "Config applied";
150 if (m_configDirty) {
151 // Config changed in the meantime again, apply.
152 doApplyConfig(m_monitoredConfig);
153 } else {
154 setMonitorForChanges(true);
155 }
156 });
157 }
158
applyConfig()159 void KDisplayDaemon::applyConfig()
160 {
161 qCDebug(KDISPLAY_KDED) << "Applying config";
162
163 const bool showOsd = m_monitoredConfig->outputs().size() > 1 && !m_startingUp
164 && m_monitoredConfig->cause() == Disman::Config::Cause::generated;
165
166 if (showOsd) {
167 qCDebug(KDISPLAY_KDED) << "Getting ideal config from user via OSD...";
168 auto action = m_osdManager->showActionSelector();
169 connect(action, &OsdAction::selected, this, &KDisplayDaemon::applyOsdAction);
170 } else {
171 m_osdManager->hideOsd();
172 }
173 }
174
applyLayoutPreset(const QString & presetName)175 void KDisplayDaemon::applyLayoutPreset(const QString& presetName)
176 {
177 const QMetaEnum actionEnum = QMetaEnum::fromType<OsdAction::Action>();
178 Q_ASSERT(actionEnum.isValid());
179
180 bool ok;
181 auto action
182 = static_cast<OsdAction::Action>(actionEnum.keyToValue(qPrintable(presetName), &ok));
183 if (!ok) {
184 qCWarning(KDISPLAY_KDED) << "Cannot apply unknown screen layout preset named" << presetName;
185 return;
186 }
187 applyOsdAction(action);
188 }
189
getAutoRotate()190 bool KDisplayDaemon::getAutoRotate()
191 {
192 return Config(m_monitoredConfig).getAutoRotate();
193 }
194
setAutoRotate(bool value)195 void KDisplayDaemon::setAutoRotate(bool value)
196 {
197 if (!m_monitoredConfig || !m_orientationSensor->available()) {
198 return;
199 }
200 Config(m_monitoredConfig).setAutoRotate(value);
201 refreshConfig();
202 }
203
applyOsdAction(OsdAction::Action action)204 void KDisplayDaemon::applyOsdAction(OsdAction::Action action)
205 {
206 qCDebug(KDISPLAY_KDED) << "Applying OSD action:" << action;
207
208 if (auto config = Generator::displaySwitch(action, m_monitoredConfig)) {
209 doApplyConfig(config);
210 }
211 }
212
configChanged()213 void KDisplayDaemon::configChanged()
214 {
215 qCDebug(KDISPLAY_KDED) << "Change detected" << m_monitoredConfig;
216
217 update_auto_rotate();
218 updateOrientation();
219 }
220
showOsd(const QString & icon,const QString & text)221 void KDisplayDaemon::showOsd(const QString& icon, const QString& text)
222 {
223 QDBusMessage msg = QDBusMessage::createMethodCall(QLatin1String("org.kde.plasmashell"),
224 QLatin1String("/org/kde/osdService"),
225 QLatin1String("org.kde.osdService"),
226 QLatin1String("showText"));
227 msg << icon << text;
228 QDBusConnection::sessionBus().asyncCall(msg);
229 }
230
showOutputIdentifier()231 void KDisplayDaemon::showOutputIdentifier()
232 {
233 m_osdManager->showOutputIdentifiers();
234 }
235
displayButton()236 void KDisplayDaemon::displayButton()
237 {
238 qCDebug(KDISPLAY_KDED) << "displayBtn triggered";
239
240 auto action = m_osdManager->showActionSelector();
241 connect(action, &OsdAction::selected, this, &KDisplayDaemon::applyOsdAction);
242 }
243
setMonitorForChanges(bool enabled)244 void KDisplayDaemon::setMonitorForChanges(bool enabled)
245 {
246 if (m_monitoring == enabled) {
247 return;
248 }
249
250 qCDebug(KDISPLAY_KDED) << "Monitor for changes: " << enabled;
251 m_monitoring = enabled;
252
253 if (m_monitoring) {
254 connect(Disman::ConfigMonitor::instance(),
255 &Disman::ConfigMonitor::configuration_changed,
256 this,
257 &KDisplayDaemon::configChanged,
258 Qt::UniqueConnection);
259 } else {
260 disconnect(Disman::ConfigMonitor::instance(),
261 &Disman::ConfigMonitor::configuration_changed,
262 this,
263 &KDisplayDaemon::configChanged);
264 }
265 }
266
267 #include "daemon.moc"
268