Music Hub ..
A session-wide music playback service
Loading...
Searching...
No Matches
state_controller.cpp
Go to the documentation of this file.
1/*
2 * Copyright © 2014 Canonical Ltd.
3 * Copyright © 2022 UBports Foundation.
4 *
5 * Contact: Alberto Mardegan <mardy@users.sourceforge.net>
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License version 3,
9 * as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Authored by: Thomas Voß <thomas.voss@canonical.com>
20 */
21
23
24#include "logging.h"
25
26#include <QDBusInterface>
27#include <QDBusPendingCall>
28#include <QDBusPendingCallWatcher>
29#include <QDBusPendingReply>
30#include <QTimer>
31#include <QWeakPointer>
32
33#include <functional>
34
35// We allow the tests to reconfigure this value
36#ifndef DISPLAY_RELEASE_INTERVAL
37#define DISPLAY_RELEASE_INTERVAL 4000
38#endif
39
40using namespace lomiri::MediaHubService::power;
41
43
44namespace lomiri {
45namespace MediaHubService {
46namespace power {
47
48uint qHash(SystemState state, uint seed) {
49 return ::qHash(static_cast<uint>(state), seed);
50}
51
53public:
55 m_interface(QStringLiteral("com.canonical.Unity.Screen"),
56 QStringLiteral("/com/canonical/Unity/Screen"),
57 QStringLiteral("com.canonical.Unity.Screen"),
58 QDBusConnection::systemBus()),
59 m_requestId(-1)
60 {
61 }
62
63 using Callback = std::function<void(void)>;
64
65 void requestDisplayOn(const Callback &cb) {
66 if (m_requestId >= 0) {
67 MH_ERROR("Display ON was already requested!");
68 return;
69 }
70
71 QDBusPendingCall call = m_interface.asyncCall("keepDisplayOn");
72 auto watcher = new QDBusPendingCallWatcher(call);
73 QObject::connect(watcher, &QDBusPendingCallWatcher::finished,
74 [this, cb](QDBusPendingCallWatcher *watcher) {
75 QDBusPendingReply<int32_t> reply = *watcher;
76 if (reply.isError()) {
77 MH_ERROR() << "Error requesting display ON:" << reply.error().message();
78 } else {
79 m_requestId = reply.value();
80 cb();
81 }
82 watcher->deleteLater();
83 });
84 }
85
86 void releaseDisplayOn(const Callback &cb) {
87 if (m_requestId < 0) {
88 MH_WARNING("Display ON was not requested!");
89 return;
90 }
91
92 QDBusPendingCall call = m_interface.asyncCall("removeDisplayOnRequest", m_requestId);
93 auto watcher = new QDBusPendingCallWatcher(call);
94 QObject::connect(watcher, &QDBusPendingCallWatcher::finished,
95 [this, cb](QDBusPendingCallWatcher *watcher) {
96 QDBusPendingReply<void> reply = *watcher;
97 if (reply.isError()) {
98 MH_ERROR() << "Error releasing display ON:" << reply.error().message();
99 } else {
100 m_requestId = -1;
101 cb();
102 }
103 watcher->deleteLater();
104 });
105 }
106
107private:
108 QDBusInterface m_interface;
109 int32_t m_requestId;
110};
111
113public:
115 m_interface(QStringLiteral("com.lomiri.Repowerd"),
116 QStringLiteral("/com/lomiri/Repowerd"),
117 QStringLiteral("com.lomiri.Repowerd"),
118 QDBusConnection::systemBus())
119 {
120 }
121
122 using Callback = std::function<void(SystemState state)>;
123
125 const Callback &cb)
126 {
127 MH_TRACE("");
128
130 return;
131 }
132
133 QDBusPendingCall call =
134 m_interface.asyncCall("requestSysState",
135 QStringLiteral("media-hub-playback_lock"),
136 static_cast<int32_t>(state));
137 auto watcher = new QDBusPendingCallWatcher(call);
138 QObject::connect(watcher, &QDBusPendingCallWatcher::finished,
139 [this, state, cb](QDBusPendingCallWatcher *watcher) {
140 QDBusPendingReply<QString> reply = *watcher;
141 if (reply.isError()) {
142 MH_ERROR() << "Error requesting system state:" << reply.error().message();
143 } else {
144 m_cookieStore.insert(state, reply.value());
145 cb(state);
146 }
147 watcher->deleteLater();
148 });
149 }
150
152 const Callback &cb)
153 {
155 return;
156 }
157 const auto i = m_cookieStore.find(state);
158 if (i == m_cookieStore.end()) {
159 return; // state was never requested
160 }
161
162 QDBusPendingCall call = m_interface.asyncCall("clearSysState", i.value());
163 auto watcher = new QDBusPendingCallWatcher(call);
164 QObject::connect(watcher, &QDBusPendingCallWatcher::finished,
165 [this, state, cb](QDBusPendingCallWatcher *watcher) {
166 QDBusPendingReply<void> reply = *watcher;
167 if (reply.isError()) {
168 MH_ERROR() << "Error releasing system state:" << reply.error().message();
169 } else {
170 m_cookieStore.remove(state);
171 cb(state);
172 }
173 watcher->deleteLater();
174 });
175 }
176
177private:
178 QDBusInterface m_interface;
179 QHash<SystemState, QString> m_cookieStore;
180};
181
183public:
185
186private:
187 friend class StateController;
188 DisplayInterface m_display;
189 int m_displayLockCount = 0;
190 SystemStateInterface m_system;
191 int m_systemLockCount = 0;
192 QTimer m_displayReleaseTimer;
193};
194
199
200}}} // namespace
201
203{
204 m_displayReleaseTimer.setSingleShot(true);
205 m_displayReleaseTimer.setTimerType(Qt::VeryCoarseTimer);
206 m_displayReleaseTimer.setInterval(DISPLAY_RELEASE_INTERVAL);
207 m_displayReleaseTimer.callOnTimeout(q, [this, q]() {
208 auto emitReleaseSignal = [q]() {
209 Q_EMIT q->displayOnReleased();
210 };
211 m_display.releaseDisplayOn(emitReleaseSignal);
212 });
213}
214
216 QObject(),
217 d_ptr(new StateControllerPrivate(this))
218{
219}
220
222
223QSharedPointer<StateController> StateController::instance()
224{
225 static QWeakPointer<CreatableStateController> weakRef;
226
227 QSharedPointer<CreatableStateController> strong = weakRef.toStrongRef();
228 if (!strong) {
229 strong = QSharedPointer<CreatableStateController>::create();
230 weakRef = strong;
231 }
232 return strong;
233}
234
236{
237 Q_D(StateController);
238 if (++d->m_displayLockCount == 1) {
239 MH_INFO("Requesting new display wakelock.");
240 d->m_display.requestDisplayOn([this]() {
241 Q_EMIT displayOnAcquired();
242 });
243 }
244}
245
247{
248 Q_D(StateController);
249 if (--d->m_displayLockCount == 0) {
250 MH_INFO("Clearing display wakelock.");
251 d->m_displayReleaseTimer.start();
252 }
253}
254
256{
257 Q_D(StateController);
258 if (++d->m_systemLockCount == 1) {
259 MH_INFO("Requesting new system wakelock.");
260 d->m_system.requestSystemState(state, [this](SystemState state) {
261 Q_EMIT systemStateAcquired(state);
262 });
263 }
264}
265
267{
268 Q_D(StateController);
269 if (--d->m_systemLockCount == 0) {
270 MH_INFO("Clearing system wakelock.");
271 d->m_system.releaseSystemState(state, [this](SystemState state) {
272 Q_EMIT systemStateReleased(state);
273 });
274 }
275}
static QSharedPointer< StateController > instance()
void releaseSystemState(media::power::SystemState state, const Callback &cb)
std::function< void(SystemState state)> Callback
void requestSystemState(SystemState state, const Callback &cb)
#define MH_TRACE(...)
Definition logging.h:37
#define MH_ERROR(...)
Definition logging.h:41
#define MH_INFO(...)
Definition logging.h:39
#define MH_WARNING(...)
Definition logging.h:40
uint qHash(SystemState state, uint seed)
#define DISPLAY_RELEASE_INTERVAL