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 IsWakeLockTypeAvailable(int aWakeLockType
);
141 bool SwitchToNextWakeLockType();
143 #ifdef MOZ_ENABLE_DBUS
144 void DBusInhibitScreensaver(const char* aName
, const char* aPath
,
145 const char* aCall
, const char* aMethod
,
146 RefPtr
<GVariant
> aArgs
);
147 void DBusUninhibitScreensaver(const char* aName
, const char* aPath
,
148 const char* aCall
, const char* aMethod
);
150 void InhibitFreeDesktopPortal();
151 void InhibitFreeDesktopScreensaver();
152 void InhibitFreeDesktopPower();
155 void UninhibitFreeDesktopPortal();
156 void UninhibitFreeDesktopScreensaver();
157 void UninhibitFreeDesktopPower();
158 void UninhibitGNOME();
160 void DBusInhibitSucceeded(uint32_t aInhibitRequestID
);
161 void DBusInhibitFailed(bool aFatal
);
162 void DBusUninhibitSucceeded();
163 void DBusUninhibitFailed();
164 void ClearDBusInhibitToken();
166 ~WakeLockTopic() = default;
168 // Why is screensaver inhibited
172 bool mShouldInhibit
= false;
175 bool mInhibited
= false;
177 #ifdef MOZ_ENABLE_DBUS
178 // We're waiting for DBus reply (inhibit/uninhibit calls).
179 bool mWaitingForDBusInhibit
= false;
180 bool mWaitingForDBusUninhibit
= false;
182 // mInhibitRequestID is received from success screen saver inhibit call
183 // and it's needed for screen saver enablement.
184 Maybe
<uint32_t> mInhibitRequestID
;
186 RefPtr
<GCancellable
> mCancellable
;
187 // Used to uninhibit org.freedesktop.portal.Inhibit request
188 nsCString mRequestObjectPath
;
191 static int sWakeLockType
;
194 int WakeLockTopic::sWakeLockType
= Initial
;
196 #ifdef MOZ_ENABLE_DBUS
197 void WakeLockTopic::DBusInhibitSucceeded(uint32_t aInhibitRequestID
) {
198 mWaitingForDBusInhibit
= false;
199 mInhibitRequestID
= Some(aInhibitRequestID
);
203 "WakeLockTopic::DBusInhibitSucceeded(), mInhibitRequestID %u "
205 *mInhibitRequestID
, mShouldInhibit
);
207 // Uninhibit was requested before inhibit request was finished.
208 // So ask for it now.
209 if (!mShouldInhibit
) {
210 UninhibitScreensaver();
214 void WakeLockTopic::DBusInhibitFailed(bool aFatal
) {
215 WAKE_LOCK_LOG("WakeLockTopic::DBusInhibitFailed(%d)", aFatal
);
217 mWaitingForDBusInhibit
= false;
218 ClearDBusInhibitToken();
220 // Non-recoverable DBus error. Switch to another wake lock type.
221 if (aFatal
&& SwitchToNextWakeLockType()) {
226 void WakeLockTopic::DBusUninhibitSucceeded() {
227 WAKE_LOCK_LOG("WakeLockTopic::DBusUninhibitSucceeded() mShouldInhibit %d",
230 mWaitingForDBusUninhibit
= false;
232 ClearDBusInhibitToken();
234 // Inhibit was requested before uninhibit request was finished.
235 // So ask for it now.
236 if (mShouldInhibit
) {
237 InhibitScreensaver();
241 void WakeLockTopic::DBusUninhibitFailed() {
242 WAKE_LOCK_LOG("WakeLockTopic::DBusUninhibitFailed()");
243 mWaitingForDBusUninhibit
= false;
244 mInhibitRequestID
= Nothing();
247 void WakeLockTopic::ClearDBusInhibitToken() {
248 mRequestObjectPath
.Truncate();
249 mInhibitRequestID
= Nothing();
252 void WakeLockTopic::DBusInhibitScreensaver(const char* aName
, const char* aPath
,
255 RefPtr
<GVariant
> aArgs
) {
257 "WakeLockTopic::DBusInhibitScreensaver() mWaitingForDBusInhibit %d "
258 "mWaitingForDBusUninhibit %d",
259 mWaitingForDBusInhibit
, mWaitingForDBusUninhibit
);
260 if (mWaitingForDBusInhibit
) {
261 WAKE_LOCK_LOG(" already waiting to inihibit, return");
264 if (mWaitingForDBusUninhibit
) {
265 WAKE_LOCK_LOG(" cancel un-inihibit request");
266 g_cancellable_cancel(mCancellable
);
267 mWaitingForDBusUninhibit
= false;
269 mWaitingForDBusInhibit
= true;
271 widget::CreateDBusProxyForBus(
273 GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS
|
274 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
),
275 /* aInterfaceInfo = */ nullptr, aName
, aPath
, aCall
, mCancellable
)
277 GetCurrentSerialEventTarget(), __func__
,
278 [self
= RefPtr
{this}, this, args
= RefPtr
{aArgs
},
279 aMethod
](RefPtr
<GDBusProxy
>&& aProxy
) {
281 "WakeLockTopic::DBusInhibitScreensaver() proxy created");
282 DBusProxyCall(aProxy
.get(), aMethod
, args
.get(),
283 G_DBUS_CALL_FLAGS_NONE
, DBUS_TIMEOUT
, mCancellable
)
285 GetCurrentSerialEventTarget(), __func__
,
286 [s
= RefPtr
{this}, this](RefPtr
<GVariant
>&& aResult
) {
287 if (!g_variant_is_of_type(aResult
.get(),
288 G_VARIANT_TYPE_TUPLE
) ||
289 g_variant_n_children(aResult
.get()) != 1) {
291 "WakeLockTopic::DBusInhibitScreensaver() wrong "
293 g_variant_get_type_string(aResult
.get()));
294 DBusInhibitFailed(/* aFatal */ true);
297 RefPtr
<GVariant
> variant
= dont_AddRef(
298 g_variant_get_child_value(aResult
.get(), 0));
299 if (!g_variant_is_of_type(variant
,
300 G_VARIANT_TYPE_UINT32
)) {
302 "WakeLockTopic::DBusInhibitScreensaver() wrong "
304 g_variant_get_type_string(aResult
.get()));
305 DBusInhibitFailed(/* aFatal */ true);
308 DBusInhibitSucceeded(g_variant_get_uint32(variant
));
310 [s
= RefPtr
{this}, this,
311 aMethod
](GUniquePtr
<GError
>&& aError
) {
312 // Failed to send inhibit request over proxy.
313 // Switch to another wake lock type.
315 "WakeLockTopic::DBusInhibitFailed() %s call failed : "
317 aMethod
, aError
->message
);
319 /* aFatal */ !IsCancelledGError(aError
.get()));
322 [self
= RefPtr
{this}, this](GUniquePtr
<GError
>&& aError
) {
323 // We failed to create DBus proxy. Switch to another
326 "WakeLockTopic::DBusInhibitScreensaver() Proxy creation "
329 DBusInhibitFailed(/* aFatal */ !IsCancelledGError(aError
.get()));
333 void WakeLockTopic::DBusUninhibitScreensaver(const char* aName
,
336 const char* aMethod
) {
338 "WakeLockTopic::DBusUninhibitScreensaver() mWaitingForDBusInhibit %d "
339 "mWaitingForDBusUninhibit %d request id %d",
340 mWaitingForDBusInhibit
, mWaitingForDBusUninhibit
,
341 mInhibitRequestID
? *mInhibitRequestID
: -1);
343 if (mWaitingForDBusUninhibit
) {
344 WAKE_LOCK_LOG(" already waiting to uninihibit, return");
348 if (mWaitingForDBusInhibit
) {
349 WAKE_LOCK_LOG(" cancel inihibit request");
350 g_cancellable_cancel(mCancellable
);
351 mWaitingForDBusInhibit
= false;
354 if (!mInhibitRequestID
.isSome()) {
355 WAKE_LOCK_LOG(" missing inihibit token, quit.");
356 // missing uninhibit token, just quit.
359 mWaitingForDBusUninhibit
= true;
361 RefPtr
<GVariant
> variant
=
362 dont_AddRef(g_variant_ref_sink(g_variant_new("(u)", *mInhibitRequestID
)));
363 nsCOMPtr
<nsISerialEventTarget
> target
= GetCurrentSerialEventTarget();
364 widget::CreateDBusProxyForBus(
366 GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS
|
367 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
),
368 /* aInterfaceInfo = */ nullptr, aName
, aPath
, aCall
, mCancellable
)
371 [self
= RefPtr
{this}, this, args
= std::move(variant
), target
,
372 aMethod
](RefPtr
<GDBusProxy
>&& aProxy
) {
374 "WakeLockTopic::DBusUninhibitScreensaver() proxy created");
375 DBusProxyCall(aProxy
.get(), aMethod
, args
.get(),
376 G_DBUS_CALL_FLAGS_NONE
, DBUS_TIMEOUT
, mCancellable
)
379 [s
= RefPtr
{this}, this](RefPtr
<GVariant
>&& aResult
) {
380 DBusUninhibitSucceeded();
382 [s
= RefPtr
{this}, this,
383 aMethod
](GUniquePtr
<GError
>&& aError
) {
385 "WakeLockTopic::DBusUninhibitFailed() %s call failed "
387 aMethod
, aError
->message
);
388 DBusUninhibitFailed();
391 [self
= RefPtr
{this}, this](GUniquePtr
<GError
>&& aError
) {
393 "WakeLockTopic::DBusUninhibitFailed() Proxy creation failed: "
396 DBusUninhibitFailed();
400 void WakeLockTopic::InhibitFreeDesktopPortal() {
402 "WakeLockTopic::InhibitFreeDesktopPortal() mWaitingForDBusInhibit %d "
403 "mWaitingForDBusUninhibit %d",
404 mWaitingForDBusInhibit
, mWaitingForDBusUninhibit
);
405 if (mWaitingForDBusInhibit
) {
406 WAKE_LOCK_LOG(" already waiting to inihibit, return");
409 if (mWaitingForDBusUninhibit
) {
410 WAKE_LOCK_LOG(" cancel un-inihibit request");
411 g_cancellable_cancel(mCancellable
);
412 mWaitingForDBusUninhibit
= false;
414 mWaitingForDBusInhibit
= true;
416 CreateDBusProxyForBus(
418 GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS
|
419 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
),
420 nullptr, FREEDESKTOP_PORTAL_DESKTOP_TARGET
,
421 FREEDESKTOP_PORTAL_DESKTOP_OBJECT
, FREEDESKTOP_PORTAL_DESKTOP_INTERFACE
,
424 GetCurrentSerialEventTarget(), __func__
,
425 [self
= RefPtr
{this}, this](RefPtr
<GDBusProxy
>&& aProxy
) {
427 g_variant_builder_init(&b
, G_VARIANT_TYPE_VARDICT
);
428 g_variant_builder_add(&b
, "{sv}", "reason",
429 g_variant_new_string(self
->mTopic
.get()));
432 // https://flatpak.github.io/xdg-desktop-portal/docs/#gdbus-org.freedesktop.portal.Inhibit
434 aProxy
.get(), "Inhibit",
435 g_variant_new("(sua{sv})", g_get_prgname(),
436 FREEDESKTOP_PORTAL_DESKTOP_INHIBIT_IDLE_FLAG
, &b
),
437 G_DBUS_CALL_FLAGS_NONE
, DBUS_TIMEOUT
, mCancellable
)
439 GetCurrentSerialEventTarget(), __func__
,
440 [s
= RefPtr
{this}, this](RefPtr
<GVariant
>&& aResult
) {
441 gchar
* requestObjectPath
= nullptr;
442 g_variant_get(aResult
, "(o)", &requestObjectPath
);
443 if (!requestObjectPath
) {
445 "WakeLockTopic::InhibitFreeDesktopPortal(): Unable "
446 "to get requestObjectPath\n");
447 DBusInhibitFailed(/* aFatal */ true);
451 "WakeLockTopic::InhibitFreeDesktopPortal(): "
452 "inhibited, objpath to unihibit: %s\n",
454 mRequestObjectPath
.Adopt(requestObjectPath
);
455 DBusInhibitSucceeded(0);
457 [s
= RefPtr
{this}, this](GUniquePtr
<GError
>&& aError
) {
459 /* aFatal */ !IsCancelledGError(aError
.get()));
461 "Failed to create DBus proxy for "
462 "org.freedesktop.portal.Desktop: %s\n",
466 [self
= RefPtr
{this}, this](GUniquePtr
<GError
>&& aError
) {
468 "Failed to create DBus proxy for "
469 "org.freedesktop.portal.Desktop: %s\n",
471 DBusInhibitFailed(/* aFatal */ !IsCancelledGError(aError
.get()));
475 void WakeLockTopic::InhibitFreeDesktopScreensaver() {
476 WAKE_LOCK_LOG("InhibitFreeDesktopScreensaver()");
477 DBusInhibitScreensaver(FREEDESKTOP_SCREENSAVER_TARGET
,
478 FREEDESKTOP_SCREENSAVER_OBJECT
,
479 FREEDESKTOP_SCREENSAVER_INTERFACE
, "Inhibit",
480 dont_AddRef(g_variant_ref_sink(g_variant_new(
481 "(ss)", g_get_prgname(), mTopic
.get()))));
484 void WakeLockTopic::InhibitFreeDesktopPower() {
485 WAKE_LOCK_LOG("InhibitFreeDesktopPower()");
486 DBusInhibitScreensaver(FREEDESKTOP_POWER_TARGET
, FREEDESKTOP_POWER_OBJECT
,
487 FREEDESKTOP_POWER_INTERFACE
, "Inhibit",
488 dont_AddRef(g_variant_ref_sink(g_variant_new(
489 "(ss)", g_get_prgname(), mTopic
.get()))));
492 void WakeLockTopic::InhibitGNOME() {
493 WAKE_LOCK_LOG("InhibitGNOME()");
494 static const uint32_t xid
= 0;
495 static const uint32_t flags
= (1 << 3); // Inhibit idle
496 DBusInhibitScreensaver(
497 SESSION_MANAGER_TARGET
, SESSION_MANAGER_OBJECT
, SESSION_MANAGER_INTERFACE
,
499 dont_AddRef(g_variant_ref_sink(
500 g_variant_new("(susu)", g_get_prgname(), xid
, mTopic
.get(), flags
))));
503 void WakeLockTopic::UninhibitFreeDesktopPortal() {
505 "WakeLockTopic::UninhibitFreeDesktopPortal() mWaitingForDBusInhibit %d "
506 "mWaitingForDBusUninhibit %d object path: %s",
507 mWaitingForDBusInhibit
, mWaitingForDBusUninhibit
,
508 mRequestObjectPath
.get());
510 if (mWaitingForDBusUninhibit
) {
511 WAKE_LOCK_LOG(" already waiting to uninihibit, return");
515 if (mWaitingForDBusInhibit
) {
516 WAKE_LOCK_LOG(" cancel inihibit request");
517 g_cancellable_cancel(mCancellable
);
518 mWaitingForDBusInhibit
= false;
520 if (mRequestObjectPath
.IsEmpty()) {
521 WAKE_LOCK_LOG("UninhibitFreeDesktopPortal() failed: unknown object path\n");
524 mWaitingForDBusUninhibit
= true;
526 nsCOMPtr
<nsISerialEventTarget
> target
= GetCurrentSerialEventTarget();
527 CreateDBusProxyForBus(
529 GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS
|
530 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
),
531 nullptr, FREEDESKTOP_PORTAL_DESKTOP_TARGET
, mRequestObjectPath
.get(),
532 "org.freedesktop.portal.Request", mCancellable
)
535 [self
= RefPtr
{this}, target
, this](RefPtr
<GDBusProxy
>&& aProxy
) {
536 DBusProxyCall(aProxy
.get(), "Close", nullptr,
537 G_DBUS_CALL_FLAGS_NONE
, DBUS_TIMEOUT
, mCancellable
)
540 [s
= RefPtr
{this}, this](RefPtr
<GVariant
>&& aResult
) {
541 DBusUninhibitSucceeded();
543 "WakeLockTopic::UninhibitFreeDesktopPortal() Inhibit "
546 [s
= RefPtr
{this}, this](GUniquePtr
<GError
>&& aError
) {
547 DBusUninhibitFailed();
549 "WakeLockTopic::UninhibitFreeDesktopPortal() "
550 "Removing inhibit failed: %s\n",
554 [self
= RefPtr
{this}, this](GUniquePtr
<GError
>&& aError
) {
556 "WakeLockTopic::UninhibitFreeDesktopPortal() Proxy creation "
559 DBusUninhibitFailed();
563 void WakeLockTopic::UninhibitFreeDesktopScreensaver() {
564 WAKE_LOCK_LOG("UninhibitFreeDesktopScreensaver()");
565 DBusUninhibitScreensaver(FREEDESKTOP_SCREENSAVER_TARGET
,
566 FREEDESKTOP_SCREENSAVER_OBJECT
,
567 FREEDESKTOP_SCREENSAVER_INTERFACE
, "UnInhibit");
570 void WakeLockTopic::UninhibitFreeDesktopPower() {
571 WAKE_LOCK_LOG("UninhibitFreeDesktopPower()");
572 DBusUninhibitScreensaver(FREEDESKTOP_POWER_TARGET
, FREEDESKTOP_POWER_OBJECT
,
573 FREEDESKTOP_POWER_INTERFACE
, "UnInhibit");
576 void WakeLockTopic::UninhibitGNOME() {
577 WAKE_LOCK_LOG("UninhibitGNOME()");
578 DBusUninhibitScreensaver(SESSION_MANAGER_TARGET
, SESSION_MANAGER_OBJECT
,
579 SESSION_MANAGER_INTERFACE
, "Uninhibit");
584 // TODO: Merge with Idle service?
585 typedef Bool (*_XScreenSaverQueryExtension_fn
)(Display
* dpy
, int* event_base
,
587 typedef Bool (*_XScreenSaverQueryVersion_fn
)(Display
* dpy
, int* major
,
589 typedef void (*_XScreenSaverSuspend_fn
)(Display
* dpy
, Bool suspend
);
591 static PRLibrary
* sXssLib
= nullptr;
592 static _XScreenSaverQueryExtension_fn _XSSQueryExtension
= nullptr;
593 static _XScreenSaverQueryVersion_fn _XSSQueryVersion
= nullptr;
594 static _XScreenSaverSuspend_fn _XSSSuspend
= nullptr;
597 bool WakeLockTopic::CheckXScreenSaverSupport() {
599 sXssLib
= PR_LoadLibrary("libXss.so.1");
605 _XSSQueryExtension
= (_XScreenSaverQueryExtension_fn
)PR_FindFunctionSymbol(
606 sXssLib
, "XScreenSaverQueryExtension");
607 _XSSQueryVersion
= (_XScreenSaverQueryVersion_fn
)PR_FindFunctionSymbol(
608 sXssLib
, "XScreenSaverQueryVersion");
609 _XSSSuspend
= (_XScreenSaverSuspend_fn
)PR_FindFunctionSymbol(
610 sXssLib
, "XScreenSaverSuspend");
611 if (!_XSSQueryExtension
|| !_XSSQueryVersion
|| !_XSSSuspend
) {
615 GdkDisplay
* gDisplay
= gdk_display_get_default();
616 if (!GdkIsX11Display(gDisplay
)) {
619 Display
* display
= GDK_DISPLAY_XDISPLAY(gDisplay
);
622 if (!_XSSQueryExtension(display
, &throwaway
, &throwaway
)) return false;
625 if (!_XSSQueryVersion(display
, &major
, &minor
)) return false;
626 // Needs to be compatible with version 1.1
627 if (major
!= 1) return false;
628 if (minor
< 1) return false;
630 WAKE_LOCK_LOG("XScreenSaver supported.");
635 bool WakeLockTopic::InhibitXScreenSaver(bool inhibit
) {
636 WAKE_LOCK_LOG("InhibitXScreenSaver %d", inhibit
);
638 // Should only be called if CheckXScreenSaverSupport returns true.
639 // There's a couple of safety checks here nonetheless.
643 GdkDisplay
* gDisplay
= gdk_display_get_default();
644 if (!GdkIsX11Display(gDisplay
)) {
647 Display
* display
= GDK_DISPLAY_XDISPLAY(gDisplay
);
648 _XSSSuspend(display
, inhibit
);
650 WAKE_LOCK_LOG("InhibitXScreenSaver %d succeeded", inhibit
);
651 mInhibited
= inhibit
;
656 #if defined(MOZ_WAYLAND)
658 bool WakeLockTopic::CheckWaylandIdleInhibitSupport() {
659 nsWaylandDisplay
* waylandDisplay
= WaylandDisplayGet();
660 return waylandDisplay
&& waylandDisplay
->GetIdleInhibitManager() != nullptr;
663 bool WakeLockTopic::InhibitWaylandIdle() {
664 WAKE_LOCK_LOG("InhibitWaylandIdle()");
666 nsWaylandDisplay
* waylandDisplay
= WaylandDisplayGet();
667 if (!waylandDisplay
) {
671 nsWindow
* focusedWindow
= nsWindow::GetFocusedWindow();
672 if (!focusedWindow
) {
676 UninhibitWaylandIdle();
678 MozContainerSurfaceLock
lock(focusedWindow
->GetMozContainer());
679 struct wl_surface
* waylandSurface
= lock
.GetSurface();
680 if (waylandSurface
) {
681 mWaylandInhibitor
= zwp_idle_inhibit_manager_v1_create_inhibitor(
682 waylandDisplay
->GetIdleInhibitManager(), waylandSurface
);
686 WAKE_LOCK_LOG("InhibitWaylandIdle() %s",
687 !!mWaylandInhibitor
? "succeeded" : "failed");
688 return !!mWaylandInhibitor
;
691 bool WakeLockTopic::UninhibitWaylandIdle() {
692 WAKE_LOCK_LOG("UninhibitWaylandIdle() mWaylandInhibitor %p",
696 if (!mWaylandInhibitor
) {
699 zwp_idle_inhibitor_v1_destroy(mWaylandInhibitor
);
700 mWaylandInhibitor
= nullptr;
705 bool WakeLockTopic::SendInhibit() {
706 WAKE_LOCK_LOG("WakeLockTopic::SendInhibit() WakeLockType %s",
707 WakeLockTypeNames
[sWakeLockType
]);
708 MOZ_ASSERT(sWakeLockType
!= Initial
);
710 switch (sWakeLockType
) {
711 #if defined(MOZ_ENABLE_DBUS)
712 case FreeDesktopPortal
:
713 InhibitFreeDesktopPortal();
715 case FreeDesktopScreensaver
:
716 InhibitFreeDesktopScreensaver();
718 case FreeDesktopPower
:
719 InhibitFreeDesktopPower();
727 return InhibitXScreenSaver(true);
729 #if defined(MOZ_WAYLAND)
730 case WaylandIdleInhibit
:
731 return InhibitWaylandIdle();
739 bool WakeLockTopic::SendUninhibit() {
740 WAKE_LOCK_LOG("WakeLockTopic::SendUninhibit() WakeLockType %s",
741 WakeLockTypeNames
[sWakeLockType
]);
742 MOZ_ASSERT(sWakeLockType
!= Initial
);
743 switch (sWakeLockType
) {
744 #if defined(MOZ_ENABLE_DBUS)
745 case FreeDesktopPortal
:
746 UninhibitFreeDesktopPortal();
748 case FreeDesktopScreensaver
:
749 UninhibitFreeDesktopScreensaver();
751 case FreeDesktopPower
:
752 UninhibitFreeDesktopPower();
760 return InhibitXScreenSaver(false);
762 #if defined(MOZ_WAYLAND)
763 case WaylandIdleInhibit
:
764 return UninhibitWaylandIdle();
772 nsresult
WakeLockTopic::InhibitScreensaver() {
773 WAKE_LOCK_LOG("WakeLockTopic::InhibitScreensaver() Inhibited %d", mInhibited
);
776 // Screensaver is inhibited. Nothing to do here.
779 mShouldInhibit
= true;
781 // Iterate through wake lock types in case of failure.
782 while (!SendInhibit() && SwitchToNextWakeLockType()) {
786 return (sWakeLockType
!= Unsupported
) ? NS_OK
: NS_ERROR_FAILURE
;
789 void WakeLockTopic::Shutdown() {
790 WAKE_LOCK_LOG("WakeLockTopic::Shutdown() state %d", mInhibited
);
791 #ifdef MOZ_ENABLE_DBUS
792 if (mWaitingForDBusUninhibit
) {
795 g_cancellable_cancel(mCancellable
);
798 UninhibitScreensaver();
802 nsresult
WakeLockTopic::UninhibitScreensaver() {
803 WAKE_LOCK_LOG("WakeLockTopic::UninhibitScreensaver() Inhibited %d",
807 // Screensaver isn't inhibited. Nothing to do here.
810 mShouldInhibit
= false;
812 // Don't switch wake lock type in case of failure.
813 // We need to use the same lock/unlock type.
814 return SendUninhibit() ? NS_OK
: NS_ERROR_FAILURE
;
817 bool WakeLockTopic::IsWakeLockTypeAvailable(int aWakeLockType
) {
818 switch (aWakeLockType
) {
819 #if defined(MOZ_ENABLE_DBUS)
820 case FreeDesktopPortal
:
821 case FreeDesktopScreensaver
:
822 case FreeDesktopPower
:
828 if (!GdkIsX11Display()) {
831 if (!CheckXScreenSaverSupport()) {
832 WAKE_LOCK_LOG(" XScreenSaverSupport is missing!");
837 #if defined(MOZ_WAYLAND)
838 case WaylandIdleInhibit
:
839 if (!GdkIsWaylandDisplay()) {
842 if (!CheckWaylandIdleInhibitSupport()) {
843 WAKE_LOCK_LOG(" WaylandIdleInhibitSupport is missing!");
853 bool WakeLockTopic::SwitchToNextWakeLockType() {
854 WAKE_LOCK_LOG("WakeLockTopic::SwitchToNextWakeLockType() WakeLockType %s",
855 WakeLockTypeNames
[sWakeLockType
]);
857 if (sWakeLockType
== Unsupported
) {
862 auto printWakeLocktype
= MakeScopeExit([&] {
863 WAKE_LOCK_LOG(" switched to WakeLockType %s",
864 WakeLockTypeNames
[sWakeLockType
]);
868 #if defined(MOZ_ENABLE_DBUS)
869 if (IsDBusWakeLock(sWakeLockType
)) {
870 // We're switching out of DBus wakelock - clear our recent DBus states.
871 mWaitingForDBusInhibit
= false;
872 mWaitingForDBusUninhibit
= false;
874 ClearDBusInhibitToken();
878 while (sWakeLockType
!= Unsupported
) {
880 if (IsWakeLockTypeAvailable(sWakeLockType
)) {
887 WakeLockListener::WakeLockListener() = default;
889 WakeLockListener::~WakeLockListener() {
890 for (const auto& topic
: mTopics
.Values()) {
895 nsresult
WakeLockListener::Callback(const nsAString
& topic
,
896 const nsAString
& state
) {
897 if (!topic
.Equals(u
"screen"_ns
) && !topic
.Equals(u
"video-playing"_ns
) &&
898 !topic
.Equals(u
"autoscroll"_ns
)) {
902 RefPtr
<WakeLockTopic
> topicLock
= mTopics
.LookupOrInsertWith(
903 topic
, [&] { return MakeRefPtr
<WakeLockTopic
>(topic
); });
905 // Treat "locked-background" the same as "unlocked" on desktop linux.
906 bool shouldLock
= state
.EqualsLiteral("locked-foreground");
907 WAKE_LOCK_LOG("WakeLockListener topic %s state %s request lock %d",
908 NS_ConvertUTF16toUTF8(topic
).get(),
909 NS_ConvertUTF16toUTF8(state
).get(), shouldLock
);
911 return shouldLock
? topicLock
->InhibitScreensaver()
912 : topicLock
->UninhibitScreensaver();