Merge autoland to mozilla-central. a=merge
[gecko.git] / widget / gtk / WakeLockListener.cpp
blob33bb374cefe253a37de1d524dc96df7d1b708249
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/. */
8 #include "WakeLockListener.h"
9 #include "WidgetUtilsGtk.h"
10 #include "mozilla/ScopeExit.h"
12 #ifdef MOZ_ENABLE_DBUS
13 # include <gio/gio.h>
14 # include "AsyncDBus.h"
15 #endif
17 #if defined(MOZ_X11)
18 # include "prlink.h"
19 # include <gdk/gdk.h>
20 # include <gdk/gdkx.h>
21 # include "X11UndefineNone.h"
22 #endif
24 #if defined(MOZ_WAYLAND)
25 # include "mozilla/widget/nsWaylandDisplay.h"
26 # include "nsWindow.h"
27 #endif
29 #ifdef MOZ_ENABLE_DBUS
30 # define FREEDESKTOP_PORTAL_DESKTOP_TARGET "org.freedesktop.portal.Desktop"
31 # define FREEDESKTOP_PORTAL_DESKTOP_OBJECT "/org/freedesktop/portal/desktop"
32 # define FREEDESKTOP_PORTAL_DESKTOP_INTERFACE "org.freedesktop.portal.Inhibit"
33 # define FREEDESKTOP_PORTAL_DESKTOP_INHIBIT_IDLE_FLAG 8
35 # define FREEDESKTOP_SCREENSAVER_TARGET "org.freedesktop.ScreenSaver"
36 # define FREEDESKTOP_SCREENSAVER_OBJECT "/ScreenSaver"
37 # define FREEDESKTOP_SCREENSAVER_INTERFACE "org.freedesktop.ScreenSaver"
39 # define FREEDESKTOP_POWER_TARGET "org.freedesktop.PowerManagement"
40 # define FREEDESKTOP_POWER_OBJECT "/org/freedesktop/PowerManagement/Inhibit"
41 # define FREEDESKTOP_POWER_INTERFACE "org.freedesktop.PowerManagement.Inhibit"
43 # define SESSION_MANAGER_TARGET "org.gnome.SessionManager"
44 # define SESSION_MANAGER_OBJECT "/org/gnome/SessionManager"
45 # define SESSION_MANAGER_INTERFACE "org.gnome.SessionManager"
47 # define DBUS_TIMEOUT (-1)
48 #endif
50 using namespace mozilla;
51 using namespace mozilla::widget;
53 NS_IMPL_ISUPPORTS(WakeLockListener, nsIDOMMozWakeLockListener)
55 #define WAKE_LOCK_LOG(str, ...) \
56 MOZ_LOG(gLinuxWakeLockLog, mozilla::LogLevel::Debug, \
57 ("[%p] " str, this, ##__VA_ARGS__))
58 static mozilla::LazyLogModule gLinuxWakeLockLog("LinuxWakeLock");
60 enum WakeLockType {
61 Initial = 0,
62 #if defined(MOZ_ENABLE_DBUS)
63 FreeDesktopScreensaver = 1,
64 FreeDesktopPower = 2,
65 FreeDesktopPortal = 3,
66 GNOME = 4,
67 #endif
68 #if defined(MOZ_X11)
69 XScreenSaver = 5,
70 #endif
71 #if defined(MOZ_WAYLAND)
72 WaylandIdleInhibit = 6,
73 #endif
74 Unsupported = 7,
77 #if defined(MOZ_ENABLE_DBUS)
78 bool IsDBusWakeLock(int aWakeLockType) {
79 switch (aWakeLockType) {
80 case FreeDesktopScreensaver:
81 case FreeDesktopPower:
82 case GNOME:
83 case FreeDesktopPortal:
84 return true;
85 default:
86 return false;
89 #endif
91 #ifdef MOZ_LOGGING
92 const char* WakeLockTypeNames[] = {
93 "Initial",
94 "FreeDesktopScreensaver",
95 "FreeDesktopPower",
96 "FreeDesktopPortal",
97 "GNOME",
98 "XScreenSaver",
99 "WaylandIdleInhibit",
100 "Unsupported",
102 #endif
104 class WakeLockTopic {
105 public:
106 NS_INLINE_DECL_REFCOUNTING(WakeLockTopic)
108 explicit WakeLockTopic(const nsAString& aTopic) {
109 CopyUTF16toUTF8(aTopic, mTopic);
110 WAKE_LOCK_LOG("WakeLockTopic::WakeLockTopic() created %s", mTopic.get());
111 if (sWakeLockType == Initial) {
112 SwitchToNextWakeLockType();
114 #ifdef MOZ_ENABLE_DBUS
115 mCancellable = dont_AddRef(g_cancellable_new());
116 #endif
119 nsresult InhibitScreensaver();
120 nsresult UninhibitScreensaver();
122 void Shutdown();
124 private:
125 bool SendInhibit();
126 bool SendUninhibit();
128 #if defined(MOZ_X11)
129 bool CheckXScreenSaverSupport();
130 bool InhibitXScreenSaver(bool inhibit);
131 #endif
133 #if defined(MOZ_WAYLAND)
134 zwp_idle_inhibitor_v1* mWaylandInhibitor = nullptr;
135 static bool CheckWaylandIdleInhibitSupport();
136 bool InhibitWaylandIdle();
137 bool UninhibitWaylandIdle();
138 #endif
140 bool IsNativeWakeLock(int aWakeLockType);
141 bool IsWakeLockTypeAvailable(int aWakeLockType);
142 bool SwitchToNextWakeLockType();
144 #ifdef MOZ_ENABLE_DBUS
145 void DBusInhibitScreensaver(const char* aName, const char* aPath,
146 const char* aCall, const char* aMethod,
147 RefPtr<GVariant> aArgs);
148 void DBusUninhibitScreensaver(const char* aName, const char* aPath,
149 const char* aCall, const char* aMethod);
151 void InhibitFreeDesktopPortal();
152 void InhibitFreeDesktopScreensaver();
153 void InhibitFreeDesktopPower();
154 void InhibitGNOME();
156 void UninhibitFreeDesktopPortal();
157 void UninhibitFreeDesktopScreensaver();
158 void UninhibitFreeDesktopPower();
159 void UninhibitGNOME();
161 void DBusInhibitSucceeded(uint32_t aInhibitRequestID);
162 void DBusInhibitFailed(bool aFatal);
163 void DBusUninhibitSucceeded();
164 void DBusUninhibitFailed();
165 void ClearDBusInhibitToken();
166 #endif
167 ~WakeLockTopic() = default;
169 // Why is screensaver inhibited
170 nsCString mTopic;
172 // Our desired state
173 bool mShouldInhibit = false;
175 // Our actual sate
176 bool mInhibited = false;
178 #ifdef MOZ_ENABLE_DBUS
179 // We're waiting for DBus reply (inhibit/uninhibit calls).
180 bool mWaitingForDBusInhibit = false;
181 bool mWaitingForDBusUninhibit = false;
183 // mInhibitRequestID is received from success screen saver inhibit call
184 // and it's needed for screen saver enablement.
185 Maybe<uint32_t> mInhibitRequestID;
187 RefPtr<GCancellable> mCancellable;
188 // Used to uninhibit org.freedesktop.portal.Inhibit request
189 nsCString mRequestObjectPath;
190 #endif
192 static int sWakeLockType;
195 int WakeLockTopic::sWakeLockType = Initial;
197 #ifdef MOZ_ENABLE_DBUS
198 void WakeLockTopic::DBusInhibitSucceeded(uint32_t aInhibitRequestID) {
199 mWaitingForDBusInhibit = false;
200 mInhibitRequestID = Some(aInhibitRequestID);
201 mInhibited = true;
203 WAKE_LOCK_LOG(
204 "WakeLockTopic::DBusInhibitSucceeded(), mInhibitRequestID %u "
205 "mShouldInhibit %d",
206 *mInhibitRequestID, mShouldInhibit);
208 // Uninhibit was requested before inhibit request was finished.
209 // So ask for it now.
210 if (!mShouldInhibit) {
211 UninhibitScreensaver();
215 void WakeLockTopic::DBusInhibitFailed(bool aFatal) {
216 WAKE_LOCK_LOG("WakeLockTopic::DBusInhibitFailed(%d)", aFatal);
218 mWaitingForDBusInhibit = false;
219 ClearDBusInhibitToken();
221 // Non-recoverable DBus error. Switch to another wake lock type.
222 if (aFatal && SwitchToNextWakeLockType()) {
223 SendInhibit();
227 void WakeLockTopic::DBusUninhibitSucceeded() {
228 WAKE_LOCK_LOG("WakeLockTopic::DBusUninhibitSucceeded() mShouldInhibit %d",
229 mShouldInhibit);
231 mWaitingForDBusUninhibit = false;
232 mInhibited = false;
233 ClearDBusInhibitToken();
235 // Inhibit was requested before uninhibit request was finished.
236 // So ask for it now.
237 if (mShouldInhibit) {
238 InhibitScreensaver();
242 void WakeLockTopic::DBusUninhibitFailed() {
243 WAKE_LOCK_LOG("WakeLockTopic::DBusUninhibitFailed()");
244 mWaitingForDBusUninhibit = false;
245 mInhibitRequestID = Nothing();
248 void WakeLockTopic::ClearDBusInhibitToken() {
249 mRequestObjectPath.Truncate();
250 mInhibitRequestID = Nothing();
253 void WakeLockTopic::DBusInhibitScreensaver(const char* aName, const char* aPath,
254 const char* aCall,
255 const char* aMethod,
256 RefPtr<GVariant> aArgs) {
257 WAKE_LOCK_LOG(
258 "WakeLockTopic::DBusInhibitScreensaver() mWaitingForDBusInhibit %d "
259 "mWaitingForDBusUninhibit %d",
260 mWaitingForDBusInhibit, mWaitingForDBusUninhibit);
261 if (mWaitingForDBusInhibit) {
262 WAKE_LOCK_LOG(" already waiting to inihibit, return");
263 return;
265 if (mWaitingForDBusUninhibit) {
266 WAKE_LOCK_LOG(" cancel un-inihibit request");
267 g_cancellable_cancel(mCancellable);
268 mWaitingForDBusUninhibit = false;
270 mWaitingForDBusInhibit = true;
272 widget::CreateDBusProxyForBus(
273 G_BUS_TYPE_SESSION,
274 GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
275 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
276 /* aInterfaceInfo = */ nullptr, aName, aPath, aCall, mCancellable)
277 ->Then(
278 GetCurrentSerialEventTarget(), __func__,
279 [self = RefPtr{this}, this, args = RefPtr{aArgs},
280 aMethod](RefPtr<GDBusProxy>&& aProxy) {
281 WAKE_LOCK_LOG(
282 "WakeLockTopic::DBusInhibitScreensaver() proxy created");
283 DBusProxyCall(aProxy.get(), aMethod, args.get(),
284 G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
285 ->Then(
286 GetCurrentSerialEventTarget(), __func__,
287 [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
288 if (!g_variant_is_of_type(aResult.get(),
289 G_VARIANT_TYPE_TUPLE) ||
290 g_variant_n_children(aResult.get()) != 1) {
291 WAKE_LOCK_LOG(
292 "WakeLockTopic::DBusInhibitScreensaver() wrong "
293 "reply type %s\n",
294 g_variant_get_type_string(aResult.get()));
295 DBusInhibitFailed(/* aFatal */ true);
296 return;
298 RefPtr<GVariant> variant = dont_AddRef(
299 g_variant_get_child_value(aResult.get(), 0));
300 if (!g_variant_is_of_type(variant,
301 G_VARIANT_TYPE_UINT32)) {
302 WAKE_LOCK_LOG(
303 "WakeLockTopic::DBusInhibitScreensaver() wrong "
304 "reply type %s\n",
305 g_variant_get_type_string(aResult.get()));
306 DBusInhibitFailed(/* aFatal */ true);
307 return;
309 DBusInhibitSucceeded(g_variant_get_uint32(variant));
311 [s = RefPtr{this}, this,
312 aMethod](GUniquePtr<GError>&& aError) {
313 // Failed to send inhibit request over proxy.
314 // Switch to another wake lock type.
315 WAKE_LOCK_LOG(
316 "WakeLockTopic::DBusInhibitFailed() %s call failed : "
317 "%s\n",
318 aMethod, aError->message);
319 DBusInhibitFailed(
320 /* aFatal */ !IsCancelledGError(aError.get()));
323 [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
324 // We failed to create DBus proxy. Switch to another
325 // wake lock type.
326 WAKE_LOCK_LOG(
327 "WakeLockTopic::DBusInhibitScreensaver() Proxy creation "
328 "failed: %s\n",
329 aError->message);
330 DBusInhibitFailed(/* aFatal */ !IsCancelledGError(aError.get()));
334 void WakeLockTopic::DBusUninhibitScreensaver(const char* aName,
335 const char* aPath,
336 const char* aCall,
337 const char* aMethod) {
338 WAKE_LOCK_LOG(
339 "WakeLockTopic::DBusUninhibitScreensaver() mWaitingForDBusInhibit %d "
340 "mWaitingForDBusUninhibit %d request id %d",
341 mWaitingForDBusInhibit, mWaitingForDBusUninhibit,
342 mInhibitRequestID ? *mInhibitRequestID : -1);
344 if (mWaitingForDBusUninhibit) {
345 WAKE_LOCK_LOG(" already waiting to uninihibit, return");
346 return;
349 if (mWaitingForDBusInhibit) {
350 WAKE_LOCK_LOG(" cancel inihibit request");
351 g_cancellable_cancel(mCancellable);
352 mWaitingForDBusInhibit = false;
355 if (!mInhibitRequestID.isSome()) {
356 WAKE_LOCK_LOG(" missing inihibit token, quit.");
357 // missing uninhibit token, just quit.
358 return;
360 mWaitingForDBusUninhibit = true;
362 RefPtr<GVariant> variant =
363 dont_AddRef(g_variant_ref_sink(g_variant_new("(u)", *mInhibitRequestID)));
364 nsCOMPtr<nsISerialEventTarget> target = GetCurrentSerialEventTarget();
365 widget::CreateDBusProxyForBus(
366 G_BUS_TYPE_SESSION,
367 GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
368 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
369 /* aInterfaceInfo = */ nullptr, aName, aPath, aCall, mCancellable)
370 ->Then(
371 target, __func__,
372 [self = RefPtr{this}, this, args = std::move(variant), target,
373 aMethod](RefPtr<GDBusProxy>&& aProxy) {
374 WAKE_LOCK_LOG(
375 "WakeLockTopic::DBusUninhibitScreensaver() proxy created");
376 DBusProxyCall(aProxy.get(), aMethod, args.get(),
377 G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
378 ->Then(
379 target, __func__,
380 [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
381 DBusUninhibitSucceeded();
383 [s = RefPtr{this}, this,
384 aMethod](GUniquePtr<GError>&& aError) {
385 WAKE_LOCK_LOG(
386 "WakeLockTopic::DBusUninhibitFailed() %s call failed "
387 ": %s\n",
388 aMethod, aError->message);
389 DBusUninhibitFailed();
392 [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
393 WAKE_LOCK_LOG(
394 "WakeLockTopic::DBusUninhibitFailed() Proxy creation failed: "
395 "%s\n",
396 aError->message);
397 DBusUninhibitFailed();
401 void WakeLockTopic::InhibitFreeDesktopPortal() {
402 WAKE_LOCK_LOG(
403 "WakeLockTopic::InhibitFreeDesktopPortal() mWaitingForDBusInhibit %d "
404 "mWaitingForDBusUninhibit %d",
405 mWaitingForDBusInhibit, mWaitingForDBusUninhibit);
406 if (mWaitingForDBusInhibit) {
407 WAKE_LOCK_LOG(" already waiting to inihibit, return");
408 return;
410 if (mWaitingForDBusUninhibit) {
411 WAKE_LOCK_LOG(" cancel un-inihibit request");
412 g_cancellable_cancel(mCancellable);
413 mWaitingForDBusUninhibit = false;
415 mWaitingForDBusInhibit = true;
417 CreateDBusProxyForBus(
418 G_BUS_TYPE_SESSION,
419 GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
420 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
421 nullptr, FREEDESKTOP_PORTAL_DESKTOP_TARGET,
422 FREEDESKTOP_PORTAL_DESKTOP_OBJECT, FREEDESKTOP_PORTAL_DESKTOP_INTERFACE,
423 mCancellable)
424 ->Then(
425 GetCurrentSerialEventTarget(), __func__,
426 [self = RefPtr{this}, this](RefPtr<GDBusProxy>&& aProxy) {
427 GVariantBuilder b;
428 g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT);
429 g_variant_builder_add(&b, "{sv}", "reason",
430 g_variant_new_string(self->mTopic.get()));
432 // From
433 // https://flatpak.github.io/xdg-desktop-portal/docs/#gdbus-org.freedesktop.portal.Inhibit
434 DBusProxyCall(
435 aProxy.get(), "Inhibit",
436 g_variant_new("(sua{sv})", g_get_prgname(),
437 FREEDESKTOP_PORTAL_DESKTOP_INHIBIT_IDLE_FLAG, &b),
438 G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
439 ->Then(
440 GetCurrentSerialEventTarget(), __func__,
441 [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
442 gchar* requestObjectPath = nullptr;
443 g_variant_get(aResult, "(o)", &requestObjectPath);
444 if (!requestObjectPath) {
445 WAKE_LOCK_LOG(
446 "WakeLockTopic::InhibitFreeDesktopPortal(): Unable "
447 "to get requestObjectPath\n");
448 DBusInhibitFailed(/* aFatal */ true);
449 return;
451 WAKE_LOCK_LOG(
452 "WakeLockTopic::InhibitFreeDesktopPortal(): "
453 "inhibited, objpath to unihibit: %s\n",
454 requestObjectPath);
455 mRequestObjectPath.Adopt(requestObjectPath);
456 DBusInhibitSucceeded(0);
458 [s = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
459 DBusInhibitFailed(
460 /* aFatal */ !IsCancelledGError(aError.get()));
461 WAKE_LOCK_LOG(
462 "Failed to create DBus proxy for "
463 "org.freedesktop.portal.Desktop: %s\n",
464 aError->message);
467 [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
468 WAKE_LOCK_LOG(
469 "Failed to create DBus proxy for "
470 "org.freedesktop.portal.Desktop: %s\n",
471 aError->message);
472 DBusInhibitFailed(/* aFatal */ !IsCancelledGError(aError.get()));
476 void WakeLockTopic::InhibitFreeDesktopScreensaver() {
477 WAKE_LOCK_LOG("InhibitFreeDesktopScreensaver()");
478 DBusInhibitScreensaver(FREEDESKTOP_SCREENSAVER_TARGET,
479 FREEDESKTOP_SCREENSAVER_OBJECT,
480 FREEDESKTOP_SCREENSAVER_INTERFACE, "Inhibit",
481 dont_AddRef(g_variant_ref_sink(g_variant_new(
482 "(ss)", g_get_prgname(), mTopic.get()))));
485 void WakeLockTopic::InhibitFreeDesktopPower() {
486 WAKE_LOCK_LOG("InhibitFreeDesktopPower()");
487 DBusInhibitScreensaver(FREEDESKTOP_POWER_TARGET, FREEDESKTOP_POWER_OBJECT,
488 FREEDESKTOP_POWER_INTERFACE, "Inhibit",
489 dont_AddRef(g_variant_ref_sink(g_variant_new(
490 "(ss)", g_get_prgname(), mTopic.get()))));
493 void WakeLockTopic::InhibitGNOME() {
494 WAKE_LOCK_LOG("InhibitGNOME()");
495 static const uint32_t xid = 0;
496 static const uint32_t flags = (1 << 3); // Inhibit idle
497 DBusInhibitScreensaver(
498 SESSION_MANAGER_TARGET, SESSION_MANAGER_OBJECT, SESSION_MANAGER_INTERFACE,
499 "Inhibit",
500 dont_AddRef(g_variant_ref_sink(
501 g_variant_new("(susu)", g_get_prgname(), xid, mTopic.get(), flags))));
504 void WakeLockTopic::UninhibitFreeDesktopPortal() {
505 WAKE_LOCK_LOG(
506 "WakeLockTopic::UninhibitFreeDesktopPortal() mWaitingForDBusInhibit %d "
507 "mWaitingForDBusUninhibit %d object path: %s",
508 mWaitingForDBusInhibit, mWaitingForDBusUninhibit,
509 mRequestObjectPath.get());
511 if (mWaitingForDBusUninhibit) {
512 WAKE_LOCK_LOG(" already waiting to uninihibit, return");
513 return;
516 if (mWaitingForDBusInhibit) {
517 WAKE_LOCK_LOG(" cancel inihibit request");
518 g_cancellable_cancel(mCancellable);
519 mWaitingForDBusInhibit = false;
521 if (mRequestObjectPath.IsEmpty()) {
522 WAKE_LOCK_LOG("UninhibitFreeDesktopPortal() failed: unknown object path\n");
523 return;
525 mWaitingForDBusUninhibit = true;
527 nsCOMPtr<nsISerialEventTarget> target = GetCurrentSerialEventTarget();
528 CreateDBusProxyForBus(
529 G_BUS_TYPE_SESSION,
530 GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
531 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
532 nullptr, FREEDESKTOP_PORTAL_DESKTOP_TARGET, mRequestObjectPath.get(),
533 "org.freedesktop.portal.Request", mCancellable)
534 ->Then(
535 target, __func__,
536 [self = RefPtr{this}, target, this](RefPtr<GDBusProxy>&& aProxy) {
537 DBusProxyCall(aProxy.get(), "Close", nullptr,
538 G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
539 ->Then(
540 target, __func__,
541 [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
542 DBusUninhibitSucceeded();
543 WAKE_LOCK_LOG(
544 "WakeLockTopic::UninhibitFreeDesktopPortal() Inhibit "
545 "removed\n");
547 [s = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
548 DBusUninhibitFailed();
549 WAKE_LOCK_LOG(
550 "WakeLockTopic::UninhibitFreeDesktopPortal() "
551 "Removing inhibit failed: %s\n",
552 aError->message);
555 [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
556 WAKE_LOCK_LOG(
557 "WakeLockTopic::UninhibitFreeDesktopPortal() Proxy creation "
558 "failed: %s\n",
559 aError->message);
560 DBusUninhibitFailed();
564 void WakeLockTopic::UninhibitFreeDesktopScreensaver() {
565 WAKE_LOCK_LOG("UninhibitFreeDesktopScreensaver()");
566 DBusUninhibitScreensaver(FREEDESKTOP_SCREENSAVER_TARGET,
567 FREEDESKTOP_SCREENSAVER_OBJECT,
568 FREEDESKTOP_SCREENSAVER_INTERFACE, "UnInhibit");
571 void WakeLockTopic::UninhibitFreeDesktopPower() {
572 WAKE_LOCK_LOG("UninhibitFreeDesktopPower()");
573 DBusUninhibitScreensaver(FREEDESKTOP_POWER_TARGET, FREEDESKTOP_POWER_OBJECT,
574 FREEDESKTOP_POWER_INTERFACE, "UnInhibit");
577 void WakeLockTopic::UninhibitGNOME() {
578 WAKE_LOCK_LOG("UninhibitGNOME()");
579 DBusUninhibitScreensaver(SESSION_MANAGER_TARGET, SESSION_MANAGER_OBJECT,
580 SESSION_MANAGER_INTERFACE, "Uninhibit");
582 #endif
584 #if defined(MOZ_X11)
585 // TODO: Merge with Idle service?
586 typedef Bool (*_XScreenSaverQueryExtension_fn)(Display* dpy, int* event_base,
587 int* error_base);
588 typedef Bool (*_XScreenSaverQueryVersion_fn)(Display* dpy, int* major,
589 int* minor);
590 typedef void (*_XScreenSaverSuspend_fn)(Display* dpy, Bool suspend);
592 static PRLibrary* sXssLib = nullptr;
593 static _XScreenSaverQueryExtension_fn _XSSQueryExtension = nullptr;
594 static _XScreenSaverQueryVersion_fn _XSSQueryVersion = nullptr;
595 static _XScreenSaverSuspend_fn _XSSSuspend = nullptr;
597 /* static */
598 bool WakeLockTopic::CheckXScreenSaverSupport() {
599 if (!sXssLib) {
600 sXssLib = PR_LoadLibrary("libXss.so.1");
601 if (!sXssLib) {
602 return false;
606 _XSSQueryExtension = (_XScreenSaverQueryExtension_fn)PR_FindFunctionSymbol(
607 sXssLib, "XScreenSaverQueryExtension");
608 _XSSQueryVersion = (_XScreenSaverQueryVersion_fn)PR_FindFunctionSymbol(
609 sXssLib, "XScreenSaverQueryVersion");
610 _XSSSuspend = (_XScreenSaverSuspend_fn)PR_FindFunctionSymbol(
611 sXssLib, "XScreenSaverSuspend");
612 if (!_XSSQueryExtension || !_XSSQueryVersion || !_XSSSuspend) {
613 return false;
616 GdkDisplay* gDisplay = gdk_display_get_default();
617 if (!GdkIsX11Display(gDisplay)) {
618 return false;
620 Display* display = GDK_DISPLAY_XDISPLAY(gDisplay);
622 int throwaway;
623 if (!_XSSQueryExtension(display, &throwaway, &throwaway)) return false;
625 int major, minor;
626 if (!_XSSQueryVersion(display, &major, &minor)) return false;
627 // Needs to be compatible with version 1.1
628 if (major != 1) return false;
629 if (minor < 1) return false;
631 WAKE_LOCK_LOG("XScreenSaver supported.");
632 return true;
635 /* static */
636 bool WakeLockTopic::InhibitXScreenSaver(bool inhibit) {
637 WAKE_LOCK_LOG("InhibitXScreenSaver %d", inhibit);
639 // Should only be called if CheckXScreenSaverSupport returns true.
640 // There's a couple of safety checks here nonetheless.
641 if (!_XSSSuspend) {
642 return false;
644 GdkDisplay* gDisplay = gdk_display_get_default();
645 if (!GdkIsX11Display(gDisplay)) {
646 return false;
648 Display* display = GDK_DISPLAY_XDISPLAY(gDisplay);
649 _XSSSuspend(display, inhibit);
651 WAKE_LOCK_LOG("InhibitXScreenSaver %d succeeded", inhibit);
652 mInhibited = inhibit;
653 return true;
655 #endif
657 #if defined(MOZ_WAYLAND)
658 /* static */
659 bool WakeLockTopic::CheckWaylandIdleInhibitSupport() {
660 nsWaylandDisplay* waylandDisplay = WaylandDisplayGet();
661 return waylandDisplay && waylandDisplay->GetIdleInhibitManager() != nullptr;
664 bool WakeLockTopic::InhibitWaylandIdle() {
665 WAKE_LOCK_LOG("InhibitWaylandIdle()");
667 nsWaylandDisplay* waylandDisplay = WaylandDisplayGet();
668 if (!waylandDisplay) {
669 return false;
672 nsWindow* focusedWindow = nsWindow::GetFocusedWindow();
673 if (!focusedWindow) {
674 return false;
677 UninhibitWaylandIdle();
679 MozContainerSurfaceLock lock(focusedWindow->GetMozContainer());
680 struct wl_surface* waylandSurface = lock.GetSurface();
681 if (waylandSurface) {
682 mWaylandInhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor(
683 waylandDisplay->GetIdleInhibitManager(), waylandSurface);
684 mInhibited = true;
687 WAKE_LOCK_LOG("InhibitWaylandIdle() %s",
688 !!mWaylandInhibitor ? "succeeded" : "failed");
689 return !!mWaylandInhibitor;
692 bool WakeLockTopic::UninhibitWaylandIdle() {
693 WAKE_LOCK_LOG("UninhibitWaylandIdle() mWaylandInhibitor %p",
694 mWaylandInhibitor);
696 mInhibited = false;
697 if (!mWaylandInhibitor) {
698 return false;
700 zwp_idle_inhibitor_v1_destroy(mWaylandInhibitor);
701 mWaylandInhibitor = nullptr;
702 return true;
704 #endif
706 bool WakeLockTopic::SendInhibit() {
707 WAKE_LOCK_LOG("WakeLockTopic::SendInhibit() WakeLockType %s",
708 WakeLockTypeNames[sWakeLockType]);
709 MOZ_ASSERT(sWakeLockType != Initial);
711 switch (sWakeLockType) {
712 #if defined(MOZ_ENABLE_DBUS)
713 case FreeDesktopPortal:
714 InhibitFreeDesktopPortal();
715 break;
716 case FreeDesktopScreensaver:
717 InhibitFreeDesktopScreensaver();
718 break;
719 case FreeDesktopPower:
720 InhibitFreeDesktopPower();
721 break;
722 case GNOME:
723 InhibitGNOME();
724 break;
725 #endif
726 #if defined(MOZ_X11)
727 case XScreenSaver:
728 return InhibitXScreenSaver(true);
729 #endif
730 #if defined(MOZ_WAYLAND)
731 case WaylandIdleInhibit:
732 return InhibitWaylandIdle();
733 #endif
734 default:
735 return false;
737 return true;
740 bool WakeLockTopic::SendUninhibit() {
741 WAKE_LOCK_LOG("WakeLockTopic::SendUninhibit() WakeLockType %s",
742 WakeLockTypeNames[sWakeLockType]);
743 MOZ_ASSERT(sWakeLockType != Initial);
744 switch (sWakeLockType) {
745 #if defined(MOZ_ENABLE_DBUS)
746 case FreeDesktopPortal:
747 UninhibitFreeDesktopPortal();
748 break;
749 case FreeDesktopScreensaver:
750 UninhibitFreeDesktopScreensaver();
751 break;
752 case FreeDesktopPower:
753 UninhibitFreeDesktopPower();
754 break;
755 case GNOME:
756 UninhibitGNOME();
757 break;
758 #endif
759 #if defined(MOZ_X11)
760 case XScreenSaver:
761 return InhibitXScreenSaver(false);
762 #endif
763 #if defined(MOZ_WAYLAND)
764 case WaylandIdleInhibit:
765 return UninhibitWaylandIdle();
766 #endif
767 default:
768 return false;
770 return true;
773 nsresult WakeLockTopic::InhibitScreensaver() {
774 WAKE_LOCK_LOG("WakeLockTopic::InhibitScreensaver() Inhibited %d", mInhibited);
776 if (mInhibited) {
777 // Screensaver is inhibited. Nothing to do here.
778 return NS_OK;
780 mShouldInhibit = true;
782 // Iterate through wake lock types in case of failure.
783 while (!SendInhibit()) {
784 // We don't switch away from native locks. Just try again.
785 if (IsNativeWakeLock(sWakeLockType)) {
786 return NS_ERROR_FAILURE;
788 if (!SwitchToNextWakeLockType()) {
789 return NS_ERROR_FAILURE;
793 return (sWakeLockType != Unsupported) ? NS_OK : NS_ERROR_FAILURE;
796 void WakeLockTopic::Shutdown() {
797 WAKE_LOCK_LOG("WakeLockTopic::Shutdown() state %d", mInhibited);
798 #ifdef MOZ_ENABLE_DBUS
799 if (mWaitingForDBusUninhibit) {
800 return;
802 g_cancellable_cancel(mCancellable);
803 #endif
804 if (mInhibited) {
805 UninhibitScreensaver();
809 nsresult WakeLockTopic::UninhibitScreensaver() {
810 WAKE_LOCK_LOG("WakeLockTopic::UninhibitScreensaver() Inhibited %d",
811 mInhibited);
813 if (!mInhibited) {
814 // Screensaver isn't inhibited. Nothing to do here.
815 return NS_OK;
817 mShouldInhibit = false;
819 // Don't switch wake lock type in case of failure.
820 // We need to use the same lock/unlock type.
821 return SendUninhibit() ? NS_OK : NS_ERROR_FAILURE;
824 bool WakeLockTopic::IsWakeLockTypeAvailable(int aWakeLockType) {
825 switch (aWakeLockType) {
826 #if defined(MOZ_ENABLE_DBUS)
827 case FreeDesktopPortal:
828 case FreeDesktopScreensaver:
829 case FreeDesktopPower:
830 case GNOME:
831 return true;
832 #endif
833 #if defined(MOZ_X11)
834 case XScreenSaver:
835 if (!GdkIsX11Display()) {
836 return false;
838 if (!CheckXScreenSaverSupport()) {
839 WAKE_LOCK_LOG(" XScreenSaverSupport is missing!");
840 return false;
842 return true;
843 #endif
844 #if defined(MOZ_WAYLAND)
845 case WaylandIdleInhibit:
846 if (!GdkIsWaylandDisplay()) {
847 return false;
849 if (!CheckWaylandIdleInhibitSupport()) {
850 WAKE_LOCK_LOG(" WaylandIdleInhibitSupport is missing!");
851 return false;
853 return true;
854 #endif
855 default:
856 return false;
860 bool WakeLockTopic::IsNativeWakeLock(int aWakeLockType) {
861 switch (aWakeLockType) {
862 #if defined(MOZ_X11)
863 case XScreenSaver:
864 return true;
865 #endif
866 #if defined(MOZ_WAYLAND)
867 case WaylandIdleInhibit:
868 return true;
869 #endif
870 default:
871 return false;
875 bool WakeLockTopic::SwitchToNextWakeLockType() {
876 WAKE_LOCK_LOG("WakeLockTopic::SwitchToNextWakeLockType() WakeLockType %s",
877 WakeLockTypeNames[sWakeLockType]);
879 if (sWakeLockType == Unsupported) {
880 return false;
883 #ifdef MOZ_LOGGING
884 auto printWakeLocktype = MakeScopeExit([&] {
885 WAKE_LOCK_LOG(" switched to WakeLockType %s",
886 WakeLockTypeNames[sWakeLockType]);
888 #endif
890 #if defined(MOZ_ENABLE_DBUS)
891 if (IsDBusWakeLock(sWakeLockType)) {
892 // We're switching out of DBus wakelock - clear our recent DBus states.
893 mWaitingForDBusInhibit = false;
894 mWaitingForDBusUninhibit = false;
895 mInhibited = false;
896 ClearDBusInhibitToken();
898 #endif
900 while (sWakeLockType != Unsupported) {
901 sWakeLockType++;
902 if (IsWakeLockTypeAvailable(sWakeLockType)) {
903 return true;
906 return false;
909 WakeLockListener::WakeLockListener() = default;
911 WakeLockListener::~WakeLockListener() {
912 for (const auto& topic : mTopics.Values()) {
913 topic->Shutdown();
917 nsresult WakeLockListener::Callback(const nsAString& topic,
918 const nsAString& state) {
919 if (!topic.Equals(u"screen"_ns) && !topic.Equals(u"video-playing"_ns) &&
920 !topic.Equals(u"autoscroll"_ns)) {
921 return NS_OK;
924 RefPtr<WakeLockTopic> topicLock = mTopics.LookupOrInsertWith(
925 topic, [&] { return MakeRefPtr<WakeLockTopic>(topic); });
927 // Treat "locked-background" the same as "unlocked" on desktop linux.
928 bool shouldLock = state.EqualsLiteral("locked-foreground");
929 WAKE_LOCK_LOG("WakeLockListener topic %s state %s request lock %d",
930 NS_ConvertUTF16toUTF8(topic).get(),
931 NS_ConvertUTF16toUTF8(state).get(), shouldLock);
933 return shouldLock ? topicLock->InhibitScreensaver()
934 : topicLock->UninhibitScreensaver();