1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=2:tabstop=2:
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
14 # include "AsyncDBus.h"
20 # include <gdk/gdkx.h>
21 # include "X11UndefineNone.h"
24 #if defined(MOZ_WAYLAND)
25 # include "mozilla/widget/nsWaylandDisplay.h"
26 # include "nsWindow.h"
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)
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");
62 #if defined(MOZ_ENABLE_DBUS)
63 FreeDesktopScreensaver
= 1,
65 FreeDesktopPortal
= 3,
71 #if defined(MOZ_WAYLAND)
72 WaylandIdleInhibit
= 6,
77 #if defined(MOZ_ENABLE_DBUS)
78 bool IsDBusWakeLock(int aWakeLockType
) {
79 switch (aWakeLockType
) {
80 case FreeDesktopScreensaver
:
81 case FreeDesktopPower
:
83 case FreeDesktopPortal
:
92 const char* WakeLockTypeNames
[] = {
94 "FreeDesktopScreensaver",
104 class WakeLockTopic
{
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());
119 nsresult
InhibitScreensaver();
120 nsresult
UninhibitScreensaver();
126 bool SendUninhibit();
129 bool CheckXScreenSaverSupport();
130 bool InhibitXScreenSaver(bool inhibit
);
133 #if defined(MOZ_WAYLAND)
134 zwp_idle_inhibitor_v1
* mWaylandInhibitor
= nullptr;
135 static bool CheckWaylandIdleInhibitSupport();
136 bool InhibitWaylandIdle();
137 bool UninhibitWaylandIdle();
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();
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();
167 ~WakeLockTopic() = default;
169 // Why is screensaver inhibited
173 bool mShouldInhibit
= false;
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
;
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
);
204 "WakeLockTopic::DBusInhibitSucceeded(), mInhibitRequestID %u "
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()) {
227 void WakeLockTopic::DBusUninhibitSucceeded() {
228 WAKE_LOCK_LOG("WakeLockTopic::DBusUninhibitSucceeded() mShouldInhibit %d",
231 mWaitingForDBusUninhibit
= 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
,
256 RefPtr
<GVariant
> aArgs
) {
258 "WakeLockTopic::DBusInhibitScreensaver() mWaitingForDBusInhibit %d "
259 "mWaitingForDBusUninhibit %d",
260 mWaitingForDBusInhibit
, mWaitingForDBusUninhibit
);
261 if (mWaitingForDBusInhibit
) {
262 WAKE_LOCK_LOG(" already waiting to inihibit, 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(
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
)
278 GetCurrentSerialEventTarget(), __func__
,
279 [self
= RefPtr
{this}, this, args
= RefPtr
{aArgs
},
280 aMethod
](RefPtr
<GDBusProxy
>&& aProxy
) {
282 "WakeLockTopic::DBusInhibitScreensaver() proxy created");
283 DBusProxyCall(aProxy
.get(), aMethod
, args
.get(),
284 G_DBUS_CALL_FLAGS_NONE
, DBUS_TIMEOUT
, mCancellable
)
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) {
292 "WakeLockTopic::DBusInhibitScreensaver() wrong "
294 g_variant_get_type_string(aResult
.get()));
295 DBusInhibitFailed(/* aFatal */ true);
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
)) {
303 "WakeLockTopic::DBusInhibitScreensaver() wrong "
305 g_variant_get_type_string(aResult
.get()));
306 DBusInhibitFailed(/* aFatal */ true);
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.
316 "WakeLockTopic::DBusInhibitFailed() %s call failed : "
318 aMethod
, aError
->message
);
320 /* aFatal */ !IsCancelledGError(aError
.get()));
323 [self
= RefPtr
{this}, this](GUniquePtr
<GError
>&& aError
) {
324 // We failed to create DBus proxy. Switch to another
327 "WakeLockTopic::DBusInhibitScreensaver() Proxy creation "
330 DBusInhibitFailed(/* aFatal */ !IsCancelledGError(aError
.get()));
334 void WakeLockTopic::DBusUninhibitScreensaver(const char* aName
,
337 const char* aMethod
) {
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");
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.
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(
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
)
372 [self
= RefPtr
{this}, this, args
= std::move(variant
), target
,
373 aMethod
](RefPtr
<GDBusProxy
>&& aProxy
) {
375 "WakeLockTopic::DBusUninhibitScreensaver() proxy created");
376 DBusProxyCall(aProxy
.get(), aMethod
, args
.get(),
377 G_DBUS_CALL_FLAGS_NONE
, DBUS_TIMEOUT
, mCancellable
)
380 [s
= RefPtr
{this}, this](RefPtr
<GVariant
>&& aResult
) {
381 DBusUninhibitSucceeded();
383 [s
= RefPtr
{this}, this,
384 aMethod
](GUniquePtr
<GError
>&& aError
) {
386 "WakeLockTopic::DBusUninhibitFailed() %s call failed "
388 aMethod
, aError
->message
);
389 DBusUninhibitFailed();
392 [self
= RefPtr
{this}, this](GUniquePtr
<GError
>&& aError
) {
394 "WakeLockTopic::DBusUninhibitFailed() Proxy creation failed: "
397 DBusUninhibitFailed();
401 void WakeLockTopic::InhibitFreeDesktopPortal() {
403 "WakeLockTopic::InhibitFreeDesktopPortal() mWaitingForDBusInhibit %d "
404 "mWaitingForDBusUninhibit %d",
405 mWaitingForDBusInhibit
, mWaitingForDBusUninhibit
);
406 if (mWaitingForDBusInhibit
) {
407 WAKE_LOCK_LOG(" already waiting to inihibit, 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(
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
,
425 GetCurrentSerialEventTarget(), __func__
,
426 [self
= RefPtr
{this}, this](RefPtr
<GDBusProxy
>&& aProxy
) {
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()));
433 // https://flatpak.github.io/xdg-desktop-portal/docs/#gdbus-org.freedesktop.portal.Inhibit
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
)
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
) {
446 "WakeLockTopic::InhibitFreeDesktopPortal(): Unable "
447 "to get requestObjectPath\n");
448 DBusInhibitFailed(/* aFatal */ true);
452 "WakeLockTopic::InhibitFreeDesktopPortal(): "
453 "inhibited, objpath to unihibit: %s\n",
455 mRequestObjectPath
.Adopt(requestObjectPath
);
456 DBusInhibitSucceeded(0);
458 [s
= RefPtr
{this}, this](GUniquePtr
<GError
>&& aError
) {
460 /* aFatal */ !IsCancelledGError(aError
.get()));
462 "Failed to create DBus proxy for "
463 "org.freedesktop.portal.Desktop: %s\n",
467 [self
= RefPtr
{this}, this](GUniquePtr
<GError
>&& aError
) {
469 "Failed to create DBus proxy for "
470 "org.freedesktop.portal.Desktop: %s\n",
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
,
500 dont_AddRef(g_variant_ref_sink(
501 g_variant_new("(susu)", g_get_prgname(), xid
, mTopic
.get(), flags
))));
504 void WakeLockTopic::UninhibitFreeDesktopPortal() {
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");
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");
525 mWaitingForDBusUninhibit
= true;
527 nsCOMPtr
<nsISerialEventTarget
> target
= GetCurrentSerialEventTarget();
528 CreateDBusProxyForBus(
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
)
536 [self
= RefPtr
{this}, target
, this](RefPtr
<GDBusProxy
>&& aProxy
) {
537 DBusProxyCall(aProxy
.get(), "Close", nullptr,
538 G_DBUS_CALL_FLAGS_NONE
, DBUS_TIMEOUT
, mCancellable
)
541 [s
= RefPtr
{this}, this](RefPtr
<GVariant
>&& aResult
) {
542 DBusUninhibitSucceeded();
544 "WakeLockTopic::UninhibitFreeDesktopPortal() Inhibit "
547 [s
= RefPtr
{this}, this](GUniquePtr
<GError
>&& aError
) {
548 DBusUninhibitFailed();
550 "WakeLockTopic::UninhibitFreeDesktopPortal() "
551 "Removing inhibit failed: %s\n",
555 [self
= RefPtr
{this}, this](GUniquePtr
<GError
>&& aError
) {
557 "WakeLockTopic::UninhibitFreeDesktopPortal() Proxy creation "
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");
585 // TODO: Merge with Idle service?
586 typedef Bool (*_XScreenSaverQueryExtension_fn
)(Display
* dpy
, int* event_base
,
588 typedef Bool (*_XScreenSaverQueryVersion_fn
)(Display
* dpy
, int* major
,
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;
598 bool WakeLockTopic::CheckXScreenSaverSupport() {
600 sXssLib
= PR_LoadLibrary("libXss.so.1");
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
) {
616 GdkDisplay
* gDisplay
= gdk_display_get_default();
617 if (!GdkIsX11Display(gDisplay
)) {
620 Display
* display
= GDK_DISPLAY_XDISPLAY(gDisplay
);
623 if (!_XSSQueryExtension(display
, &throwaway
, &throwaway
)) return false;
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.");
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.
644 GdkDisplay
* gDisplay
= gdk_display_get_default();
645 if (!GdkIsX11Display(gDisplay
)) {
648 Display
* display
= GDK_DISPLAY_XDISPLAY(gDisplay
);
649 _XSSSuspend(display
, inhibit
);
651 WAKE_LOCK_LOG("InhibitXScreenSaver %d succeeded", inhibit
);
652 mInhibited
= inhibit
;
657 #if defined(MOZ_WAYLAND)
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
) {
672 nsWindow
* focusedWindow
= nsWindow::GetFocusedWindow();
673 if (!focusedWindow
) {
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
);
687 WAKE_LOCK_LOG("InhibitWaylandIdle() %s",
688 !!mWaylandInhibitor
? "succeeded" : "failed");
689 return !!mWaylandInhibitor
;
692 bool WakeLockTopic::UninhibitWaylandIdle() {
693 WAKE_LOCK_LOG("UninhibitWaylandIdle() mWaylandInhibitor %p",
697 if (!mWaylandInhibitor
) {
700 zwp_idle_inhibitor_v1_destroy(mWaylandInhibitor
);
701 mWaylandInhibitor
= nullptr;
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();
716 case FreeDesktopScreensaver
:
717 InhibitFreeDesktopScreensaver();
719 case FreeDesktopPower
:
720 InhibitFreeDesktopPower();
728 return InhibitXScreenSaver(true);
730 #if defined(MOZ_WAYLAND)
731 case WaylandIdleInhibit
:
732 return InhibitWaylandIdle();
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();
749 case FreeDesktopScreensaver
:
750 UninhibitFreeDesktopScreensaver();
752 case FreeDesktopPower
:
753 UninhibitFreeDesktopPower();
761 return InhibitXScreenSaver(false);
763 #if defined(MOZ_WAYLAND)
764 case WaylandIdleInhibit
:
765 return UninhibitWaylandIdle();
773 nsresult
WakeLockTopic::InhibitScreensaver() {
774 WAKE_LOCK_LOG("WakeLockTopic::InhibitScreensaver() Inhibited %d", mInhibited
);
777 // Screensaver is inhibited. Nothing to do here.
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
) {
802 g_cancellable_cancel(mCancellable
);
805 UninhibitScreensaver();
809 nsresult
WakeLockTopic::UninhibitScreensaver() {
810 WAKE_LOCK_LOG("WakeLockTopic::UninhibitScreensaver() Inhibited %d",
814 // Screensaver isn't inhibited. Nothing to do here.
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
:
835 if (!GdkIsX11Display()) {
838 if (!CheckXScreenSaverSupport()) {
839 WAKE_LOCK_LOG(" XScreenSaverSupport is missing!");
844 #if defined(MOZ_WAYLAND)
845 case WaylandIdleInhibit
:
846 if (!GdkIsWaylandDisplay()) {
849 if (!CheckWaylandIdleInhibitSupport()) {
850 WAKE_LOCK_LOG(" WaylandIdleInhibitSupport is missing!");
860 bool WakeLockTopic::IsNativeWakeLock(int aWakeLockType
) {
861 switch (aWakeLockType
) {
866 #if defined(MOZ_WAYLAND)
867 case WaylandIdleInhibit
:
875 bool WakeLockTopic::SwitchToNextWakeLockType() {
876 WAKE_LOCK_LOG("WakeLockTopic::SwitchToNextWakeLockType() WakeLockType %s",
877 WakeLockTypeNames
[sWakeLockType
]);
879 if (sWakeLockType
== Unsupported
) {
884 auto printWakeLocktype
= MakeScopeExit([&] {
885 WAKE_LOCK_LOG(" switched to WakeLockType %s",
886 WakeLockTypeNames
[sWakeLockType
]);
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;
896 ClearDBusInhibitToken();
900 while (sWakeLockType
!= Unsupported
) {
902 if (IsWakeLockTypeAvailable(sWakeLockType
)) {
909 WakeLockListener::WakeLockListener() = default;
911 WakeLockListener::~WakeLockListener() {
912 for (const auto& topic
: mTopics
.Values()) {
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
)) {
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();