Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:expandtab:shiftwidth=2:tabstop=2:
3 : */
4 : /* This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 :
8 : #ifdef MOZ_ENABLE_DBUS
9 :
10 : #include "WakeLockListener.h"
11 :
12 : #include <dbus/dbus.h>
13 : #include <dbus/dbus-glib-lowlevel.h>
14 :
15 : #include "mozilla/ipc/DBusMessageRefPtr.h"
16 : #include "mozilla/ipc/DBusPendingCallRefPtr.h"
17 :
18 : #define FREEDESKTOP_SCREENSAVER_TARGET "org.freedesktop.ScreenSaver"
19 : #define FREEDESKTOP_SCREENSAVER_OBJECT "/ScreenSaver"
20 : #define FREEDESKTOP_SCREENSAVER_INTERFACE "org.freedesktop.ScreenSaver"
21 :
22 : #define SESSION_MANAGER_TARGET "org.gnome.SessionManager"
23 : #define SESSION_MANAGER_OBJECT "/org/gnome/SessionManager"
24 : #define SESSION_MANAGER_INTERFACE "org.gnome.SessionManager"
25 :
26 : #define DBUS_TIMEOUT (-1)
27 :
28 : using namespace mozilla;
29 :
30 5 : NS_IMPL_ISUPPORTS(WakeLockListener, nsIDOMMozWakeLockListener)
31 :
32 3 : StaticRefPtr<WakeLockListener> WakeLockListener::sSingleton;
33 :
34 :
35 : enum DesktopEnvironment {
36 : FreeDesktop,
37 : GNOME,
38 : Unsupported,
39 : };
40 :
41 0 : class WakeLockTopic
42 : {
43 : public:
44 0 : WakeLockTopic(const nsAString& aTopic, DBusConnection* aConnection)
45 0 : : mTopic(NS_ConvertUTF16toUTF8(aTopic))
46 : , mConnection(aConnection)
47 : , mDesktopEnvironment(FreeDesktop)
48 : , mInhibitRequest(0)
49 : , mShouldInhibit(false)
50 0 : , mWaitingForReply(false)
51 : {
52 0 : }
53 :
54 : nsresult InhibitScreensaver(void);
55 : nsresult UninhibitScreensaver(void);
56 :
57 : private:
58 : bool SendInhibit();
59 : bool SendUninhibit();
60 :
61 : bool SendFreeDesktopInhibitMessage();
62 : bool SendGNOMEInhibitMessage();
63 : bool SendMessage(DBusMessage* aMessage);
64 :
65 : static void ReceiveInhibitReply(DBusPendingCall* aPending, void* aUserData);
66 : void InhibitFailed();
67 : void InhibitSucceeded(uint32_t aInhibitRequest);
68 :
69 : nsCString mTopic;
70 : RefPtr<DBusConnection> mConnection;
71 :
72 : DesktopEnvironment mDesktopEnvironment;
73 :
74 : uint32_t mInhibitRequest;
75 :
76 : bool mShouldInhibit;
77 : bool mWaitingForReply;
78 : };
79 :
80 :
81 : bool
82 0 : WakeLockTopic::SendMessage(DBusMessage* aMessage)
83 : {
84 : // send message and get a handle for a reply
85 0 : RefPtr<DBusPendingCall> reply;
86 0 : dbus_connection_send_with_reply(mConnection, aMessage,
87 : reply.StartAssignment(),
88 0 : DBUS_TIMEOUT);
89 0 : if (!reply) {
90 0 : return false;
91 : }
92 :
93 0 : dbus_pending_call_set_notify(reply, &ReceiveInhibitReply, this, NULL);
94 :
95 0 : return true;
96 : }
97 :
98 : bool
99 0 : WakeLockTopic::SendFreeDesktopInhibitMessage()
100 : {
101 0 : RefPtr<DBusMessage> message = already_AddRefed<DBusMessage>(
102 : dbus_message_new_method_call(FREEDESKTOP_SCREENSAVER_TARGET,
103 : FREEDESKTOP_SCREENSAVER_OBJECT,
104 : FREEDESKTOP_SCREENSAVER_INTERFACE,
105 0 : "Inhibit"));
106 :
107 0 : if (!message) {
108 0 : return false;
109 : }
110 :
111 0 : const char* app = g_get_prgname();
112 0 : const char* topic = mTopic.get();
113 0 : dbus_message_append_args(message,
114 : DBUS_TYPE_STRING, &app,
115 : DBUS_TYPE_STRING, &topic,
116 0 : DBUS_TYPE_INVALID);
117 :
118 0 : return SendMessage(message);
119 : }
120 :
121 : bool
122 0 : WakeLockTopic::SendGNOMEInhibitMessage()
123 : {
124 0 : RefPtr<DBusMessage> message = already_AddRefed<DBusMessage>(
125 : dbus_message_new_method_call(SESSION_MANAGER_TARGET,
126 : SESSION_MANAGER_OBJECT,
127 : SESSION_MANAGER_INTERFACE,
128 0 : "Inhibit"));
129 :
130 0 : if (!message) {
131 0 : return false;
132 : }
133 :
134 : static const uint32_t xid = 0;
135 : static const uint32_t flags = (1 << 3); // Inhibit idle
136 0 : const char* app = g_get_prgname();
137 0 : const char* topic = mTopic.get();
138 0 : dbus_message_append_args(message,
139 : DBUS_TYPE_STRING, &app,
140 : DBUS_TYPE_UINT32, &xid,
141 : DBUS_TYPE_STRING, &topic,
142 : DBUS_TYPE_UINT32, &flags,
143 0 : DBUS_TYPE_INVALID);
144 :
145 0 : return SendMessage(message);
146 : }
147 :
148 :
149 : bool
150 0 : WakeLockTopic::SendInhibit()
151 : {
152 0 : bool sendOk = false;
153 :
154 0 : switch (mDesktopEnvironment)
155 : {
156 : case FreeDesktop:
157 0 : sendOk = SendFreeDesktopInhibitMessage();
158 0 : break;
159 : case GNOME:
160 0 : sendOk = SendGNOMEInhibitMessage();
161 0 : break;
162 : case Unsupported:
163 0 : return false;
164 : }
165 :
166 0 : if (sendOk) {
167 0 : mWaitingForReply = true;
168 : }
169 :
170 0 : return sendOk;
171 : }
172 :
173 : bool
174 0 : WakeLockTopic::SendUninhibit()
175 : {
176 0 : RefPtr<DBusMessage> message;
177 :
178 0 : if (mDesktopEnvironment == FreeDesktop) {
179 0 : message = already_AddRefed<DBusMessage>(
180 : dbus_message_new_method_call(FREEDESKTOP_SCREENSAVER_TARGET,
181 : FREEDESKTOP_SCREENSAVER_OBJECT,
182 : FREEDESKTOP_SCREENSAVER_INTERFACE,
183 0 : "UnInhibit"));
184 0 : } else if (mDesktopEnvironment == GNOME) {
185 0 : message = already_AddRefed<DBusMessage>(
186 : dbus_message_new_method_call(SESSION_MANAGER_TARGET,
187 : SESSION_MANAGER_OBJECT,
188 : SESSION_MANAGER_INTERFACE,
189 0 : "Uninhibit"));
190 : }
191 :
192 0 : if (!message) {
193 0 : return false;
194 : }
195 :
196 0 : dbus_message_append_args(message,
197 : DBUS_TYPE_UINT32, &mInhibitRequest,
198 0 : DBUS_TYPE_INVALID);
199 :
200 0 : dbus_connection_send(mConnection, message, nullptr);
201 0 : dbus_connection_flush(mConnection);
202 :
203 0 : mInhibitRequest = 0;
204 :
205 0 : return true;
206 : }
207 :
208 : nsresult
209 0 : WakeLockTopic::InhibitScreensaver()
210 : {
211 0 : if (mShouldInhibit) {
212 : // Screensaver is inhibited. Nothing to do here.
213 0 : return NS_OK;
214 : }
215 :
216 0 : mShouldInhibit = true;
217 :
218 0 : if (mWaitingForReply) {
219 : // We already have a screensaver inhibit request pending. This can happen
220 : // if InhibitScreensaver is called, then UninhibitScreensaver, then
221 : // InhibitScreensaver again quickly.
222 0 : return NS_OK;
223 : }
224 :
225 0 : return SendInhibit() ? NS_OK : NS_ERROR_FAILURE;
226 : }
227 :
228 : nsresult
229 0 : WakeLockTopic::UninhibitScreensaver()
230 : {
231 0 : if (!mShouldInhibit) {
232 : // Screensaver isn't inhibited. Nothing to do here.
233 0 : return NS_OK;
234 : }
235 :
236 0 : mShouldInhibit = false;
237 :
238 0 : if (mWaitingForReply) {
239 : // If we're still waiting for a response to our inhibit request, we can't
240 : // do anything until we get a dbus message back. The callbacks below will
241 : // check |mShouldInhibit| and act accordingly.
242 0 : return NS_OK;
243 : }
244 :
245 0 : return SendUninhibit() ? NS_OK : NS_ERROR_FAILURE;
246 : }
247 :
248 : void
249 0 : WakeLockTopic::InhibitFailed()
250 : {
251 0 : mWaitingForReply = false;
252 :
253 0 : if (mDesktopEnvironment == FreeDesktop) {
254 0 : mDesktopEnvironment = GNOME;
255 : } else {
256 0 : NS_ASSERTION(mDesktopEnvironment == GNOME, "Unknown desktop environment");
257 0 : mDesktopEnvironment = Unsupported;
258 0 : mShouldInhibit = false;
259 : }
260 :
261 0 : if (!mShouldInhibit) {
262 : // We were interrupted by UninhibitScreensaver() before we could find the
263 : // correct desktop environment.
264 0 : return;
265 : }
266 :
267 0 : SendInhibit();
268 : }
269 :
270 : void
271 0 : WakeLockTopic::InhibitSucceeded(uint32_t aInhibitRequest)
272 : {
273 0 : mWaitingForReply = false;
274 0 : mInhibitRequest = aInhibitRequest;
275 :
276 0 : if (!mShouldInhibit) {
277 : // We successfully inhibited the screensaver, but UninhibitScreensaver()
278 : // was called while we were waiting for a reply.
279 0 : SendUninhibit();
280 : }
281 0 : }
282 :
283 : /* static */ void
284 0 : WakeLockTopic::ReceiveInhibitReply(DBusPendingCall* pending, void* user_data)
285 : {
286 0 : if (!WakeLockListener::GetSingleton(false)) {
287 : // The WakeLockListener (and therefore our topic) was deleted while we were
288 : // waiting for a reply.
289 0 : return;
290 : }
291 :
292 0 : WakeLockTopic* self = static_cast<WakeLockTopic*>(user_data);
293 :
294 0 : RefPtr<DBusMessage> msg = already_AddRefed<DBusMessage>(
295 0 : dbus_pending_call_steal_reply(pending));
296 0 : if (!msg) {
297 0 : return;
298 : }
299 :
300 0 : if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_RETURN) {
301 : uint32_t inhibitRequest;
302 :
303 0 : if (dbus_message_get_args(msg, nullptr, DBUS_TYPE_UINT32,
304 : &inhibitRequest, DBUS_TYPE_INVALID)) {
305 0 : self->InhibitSucceeded(inhibitRequest);
306 : }
307 : } else {
308 0 : self->InhibitFailed();
309 : }
310 : }
311 :
312 :
313 1 : WakeLockListener::WakeLockListener()
314 2 : : mConnection(already_AddRefed<DBusConnection>(
315 3 : dbus_bus_get(DBUS_BUS_SESSION, nullptr)))
316 : {
317 1 : if (mConnection) {
318 1 : dbus_connection_set_exit_on_disconnect(mConnection, false);
319 1 : dbus_connection_setup_with_g_main(mConnection, nullptr);
320 : }
321 1 : }
322 :
323 : /* static */ WakeLockListener*
324 1 : WakeLockListener::GetSingleton(bool aCreate)
325 : {
326 1 : if (!sSingleton && aCreate) {
327 1 : sSingleton = new WakeLockListener();
328 : }
329 :
330 1 : return sSingleton;
331 : }
332 :
333 : /* static */ void
334 0 : WakeLockListener::Shutdown()
335 : {
336 0 : sSingleton = nullptr;
337 0 : }
338 :
339 : nsresult
340 0 : WakeLockListener::Callback(const nsAString& topic, const nsAString& state)
341 : {
342 0 : if (!mConnection) {
343 0 : return NS_ERROR_FAILURE;
344 : }
345 :
346 0 : if(!topic.Equals(NS_LITERAL_STRING("screen")))
347 0 : return NS_OK;
348 :
349 0 : WakeLockTopic* topicLock = mTopics.Get(topic);
350 0 : if (!topicLock) {
351 0 : topicLock = new WakeLockTopic(topic, mConnection);
352 0 : mTopics.Put(topic, topicLock);
353 : }
354 :
355 : // Treat "locked-background" the same as "unlocked" on desktop linux.
356 0 : bool shouldLock = state.EqualsLiteral("locked-foreground");
357 :
358 0 : return shouldLock ?
359 0 : topicLock->InhibitScreensaver() :
360 0 : topicLock->UninhibitScreensaver();
361 : }
362 :
363 : #endif
|