Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / widget / gtk / WakeLockListener.cpp
blobc2f935a855d1176ab6f7ffea5d467764e9a88309
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 <queue>
10 #include "WakeLockListener.h"
11 #include "WidgetUtilsGtk.h"
12 #include "mozilla/ScopeExit.h"
14 #ifdef MOZ_ENABLE_DBUS
15 # include <gio/gio.h>
16 # include "AsyncDBus.h"
17 #endif
19 #if defined(MOZ_X11)
20 # include "prlink.h"
21 # include <gdk/gdk.h>
22 # include <gdk/gdkx.h>
23 # include "X11UndefineNone.h"
24 #endif
26 #if defined(MOZ_WAYLAND)
27 # include "mozilla/widget/nsWaylandDisplay.h"
28 # include "nsWindow.h"
29 #endif
31 #ifdef MOZ_ENABLE_DBUS
32 # define FREEDESKTOP_PORTAL_DESKTOP_TARGET "org.freedesktop.portal.Desktop"
33 # define FREEDESKTOP_PORTAL_DESKTOP_OBJECT "/org/freedesktop/portal/desktop"
34 # define FREEDESKTOP_PORTAL_DESKTOP_INTERFACE "org.freedesktop.portal.Inhibit"
35 # define FREEDESKTOP_PORTAL_DESKTOP_INHIBIT_IDLE_FLAG 8
37 # define FREEDESKTOP_SCREENSAVER_TARGET "org.freedesktop.ScreenSaver"
38 # define FREEDESKTOP_SCREENSAVER_OBJECT "/ScreenSaver"
39 # define FREEDESKTOP_SCREENSAVER_INTERFACE "org.freedesktop.ScreenSaver"
41 # define FREEDESKTOP_POWER_TARGET "org.freedesktop.PowerManagement"
42 # define FREEDESKTOP_POWER_OBJECT "/org/freedesktop/PowerManagement/Inhibit"
43 # define FREEDESKTOP_POWER_INTERFACE "org.freedesktop.PowerManagement.Inhibit"
45 # define SESSION_MANAGER_TARGET "org.gnome.SessionManager"
46 # define SESSION_MANAGER_OBJECT "/org/gnome/SessionManager"
47 # define SESSION_MANAGER_INTERFACE "org.gnome.SessionManager"
49 # define DBUS_TIMEOUT (-1)
50 #endif
52 using namespace mozilla;
53 using namespace mozilla::widget;
55 NS_IMPL_ISUPPORTS(WakeLockListener, nsIDOMMozWakeLockListener)
57 #define WAKE_LOCK_LOG(str, ...) \
58 MOZ_LOG(gLinuxWakeLockLog, mozilla::LogLevel::Debug, \
59 ("[%p] " str, this, ##__VA_ARGS__))
60 static mozilla::LazyLogModule gLinuxWakeLockLog("LinuxWakeLock");
62 enum WakeLockType {
63 Initial = 0,
64 #if defined(MOZ_ENABLE_DBUS)
65 FreeDesktopScreensaver = 1,
66 FreeDesktopPower = 2,
67 FreeDesktopPortal = 3,
68 GNOME = 4,
69 #endif
70 #if defined(MOZ_X11)
71 XScreenSaver = 5,
72 #endif
73 #if defined(MOZ_WAYLAND)
74 WaylandIdleInhibit = 6,
75 #endif
76 Unsupported = 7,
79 #if defined(MOZ_ENABLE_DBUS)
80 bool IsDBusWakeLock(int aWakeLockType) {
81 switch (aWakeLockType) {
82 case FreeDesktopScreensaver:
83 case FreeDesktopPower:
84 case GNOME:
85 case FreeDesktopPortal:
86 return true;
87 default:
88 return false;
91 #endif
93 #ifdef MOZ_LOGGING
94 const char* WakeLockTypeNames[] = {
95 "Initial",
96 "FreeDesktopScreensaver",
97 "FreeDesktopPower",
98 "FreeDesktopPortal",
99 "GNOME",
100 "XScreenSaver",
101 "WaylandIdleInhibit",
102 "Unsupported",
104 #endif
106 class WakeLockTopic {
107 public:
108 NS_INLINE_DECL_REFCOUNTING(WakeLockTopic)
110 explicit WakeLockTopic(const nsAString& aTopic) {
111 CopyUTF16toUTF8(aTopic, mTopic);
112 WAKE_LOCK_LOG("WakeLockTopic::WakeLockTopic() created %s", mTopic.get());
113 if (sWakeLockType == Initial) {
114 SwitchToNextWakeLockType();
118 nsresult InhibitScreensaver();
119 nsresult UninhibitScreensaver();
121 void Shutdown();
123 private:
124 bool SendInhibit();
125 bool SendUninhibit();
127 nsresult ProcessNextRequest();
129 #if defined(MOZ_X11)
130 bool CheckXScreenSaverSupport();
131 bool InhibitXScreenSaver(bool inhibit);
132 #endif
134 #if defined(MOZ_WAYLAND)
135 zwp_idle_inhibitor_v1* mWaylandInhibitor = nullptr;
136 static bool CheckWaylandIdleInhibitSupport();
137 bool InhibitWaylandIdle();
138 bool UninhibitWaylandIdle();
139 #endif
141 bool IsNativeWakeLock(int aWakeLockType);
142 bool IsWakeLockTypeAvailable(int aWakeLockType);
143 bool SwitchToNextWakeLockType();
145 #ifdef MOZ_ENABLE_DBUS
146 void DBusInhibitScreensaver(const char* aName, const char* aPath,
147 const char* aCall, const char* aMethod,
148 RefPtr<GVariant> aArgs);
149 void DBusUninhibitScreensaver(const char* aName, const char* aPath,
150 const char* aCall, const char* aMethod);
152 void InhibitFreeDesktopPortal();
153 void InhibitFreeDesktopScreensaver();
154 void InhibitFreeDesktopPower();
155 void InhibitGNOME();
157 void UninhibitFreeDesktopPortal();
158 void UninhibitFreeDesktopScreensaver();
159 void UninhibitFreeDesktopPower();
160 void UninhibitGNOME();
162 void DBusInhibitSucceeded(uint32_t aInhibitRequestID);
163 void DBusInhibitFailed(bool aFatal);
164 void DBusUninhibitSucceeded();
165 void DBusUninhibitFailed();
166 void ClearDBusInhibitToken();
167 #endif
168 ~WakeLockTopic() = default;
170 // Why is screensaver inhibited
171 nsCString mTopic;
173 enum WakeLockState {
174 Inhibited,
175 WaitingToInhibit,
176 Uninhibited,
177 WaitingToUninhibit
178 } mState = Uninhibited;
180 #if MOZ_LOGGING
181 const char* GetInhibitStateName(WakeLockState aState) {
182 switch (aState) {
183 case Inhibited:
184 return "inhibited";
185 case WaitingToInhibit:
186 return "waiting to inhibit";
187 case Uninhibited:
188 return "uninhibited";
189 case WaitingToUninhibit:
190 return "waiting to uninhibit";
192 return "invalid";
194 #endif
196 #ifdef MOZ_ENABLE_DBUS
197 // mInhibitRequestID is received from success screen saver inhibit call
198 // and it's needed for screen saver enablement.
199 Maybe<uint32_t> mInhibitRequestID;
200 // Used to uninhibit org.freedesktop.portal.Inhibit request
201 nsCString mRequestObjectPath;
202 // It's used to quit DBus operation on shutdown.
203 RefPtr<GCancellable> mCancellable;
204 // If we fail to uninhibit DBus screensaver just disable
205 // it completelly.
206 int mUninhibitAttempts = 5;
207 #endif
209 std::queue<WakeLockState> mStateQueue;
210 static int sWakeLockType;
213 int WakeLockTopic::sWakeLockType = Initial;
215 #ifdef MOZ_ENABLE_DBUS
216 void WakeLockTopic::DBusInhibitSucceeded(uint32_t aInhibitRequestID) {
217 mState = Inhibited;
218 mCancellable = nullptr;
219 mInhibitRequestID = Some(aInhibitRequestID);
221 WAKE_LOCK_LOG("WakeLockTopic::DBusInhibitSucceeded(), mInhibitRequestID %u",
222 *mInhibitRequestID);
224 ProcessNextRequest();
227 void WakeLockTopic::DBusInhibitFailed(bool aFatal) {
228 WAKE_LOCK_LOG("WakeLockTopic::DBusInhibitFailed(%d)", aFatal);
230 mCancellable = nullptr;
231 ClearDBusInhibitToken();
233 // Non-recoverable DBus error. Switch to another wake lock type.
234 if (aFatal && SwitchToNextWakeLockType()) {
235 mState = WaitingToInhibit;
236 SendInhibit();
237 return;
240 // Flip back to uninhibited state as we failed.
241 mState = Uninhibited;
244 void WakeLockTopic::DBusUninhibitSucceeded() {
245 WAKE_LOCK_LOG("WakeLockTopic::DBusUninhibitSucceeded()");
246 mState = Uninhibited;
247 mCancellable = nullptr;
248 ClearDBusInhibitToken();
249 ProcessNextRequest();
252 void WakeLockTopic::DBusUninhibitFailed() {
253 WAKE_LOCK_LOG("WakeLockTopic::DBusUninhibitFailed()");
254 mState = Inhibited;
255 mCancellable = nullptr;
257 // We're in inhibited state and we can't switch back.
258 // Let's try again but there isn't much to do.
259 if (--mUninhibitAttempts == 0) {
260 sWakeLockType = Unsupported;
264 void WakeLockTopic::ClearDBusInhibitToken() {
265 mRequestObjectPath.Truncate();
266 mInhibitRequestID = Nothing();
269 void WakeLockTopic::DBusInhibitScreensaver(const char* aName, const char* aPath,
270 const char* aCall,
271 const char* aMethod,
272 RefPtr<GVariant> aArgs) {
273 WAKE_LOCK_LOG("WakeLockTopic::DBusInhibitScreensaver()");
275 MOZ_DIAGNOSTIC_ASSERT(!mCancellable);
276 MOZ_DIAGNOSTIC_ASSERT(mState == WaitingToInhibit);
278 mCancellable = dont_AddRef(g_cancellable_new());
280 widget::CreateDBusProxyForBus(
281 G_BUS_TYPE_SESSION,
282 GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
283 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
284 /* aInterfaceInfo = */ nullptr, aName, aPath, aCall, mCancellable)
285 ->Then(
286 GetCurrentSerialEventTarget(), __func__,
287 [self = RefPtr{this}, this, args = RefPtr{aArgs},
288 aMethod](RefPtr<GDBusProxy>&& aProxy) {
289 WAKE_LOCK_LOG(
290 "WakeLockTopic::DBusInhibitScreensaver() proxy created");
291 DBusProxyCall(aProxy.get(), aMethod, args.get(),
292 G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
293 ->Then(
294 GetCurrentSerialEventTarget(), __func__,
295 [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
296 if (!g_variant_is_of_type(aResult.get(),
297 G_VARIANT_TYPE_TUPLE) ||
298 g_variant_n_children(aResult.get()) != 1) {
299 WAKE_LOCK_LOG(
300 "WakeLockTopic::DBusInhibitScreensaver() wrong "
301 "reply type %s\n",
302 g_variant_get_type_string(aResult.get()));
303 DBusInhibitFailed(/* aFatal */ true);
304 return;
306 RefPtr<GVariant> variant = dont_AddRef(
307 g_variant_get_child_value(aResult.get(), 0));
308 if (!g_variant_is_of_type(variant,
309 G_VARIANT_TYPE_UINT32)) {
310 WAKE_LOCK_LOG(
311 "WakeLockTopic::DBusInhibitScreensaver() wrong "
312 "reply type %s\n",
313 g_variant_get_type_string(aResult.get()));
314 DBusInhibitFailed(/* aFatal */ true);
315 return;
317 DBusInhibitSucceeded(g_variant_get_uint32(variant));
319 [s = RefPtr{this}, this,
320 aMethod](GUniquePtr<GError>&& aError) {
321 // Failed to send inhibit request over proxy.
322 // Switch to another wake lock type.
323 WAKE_LOCK_LOG(
324 "WakeLockTopic::DBusInhibitFailed() %s call failed : "
325 "%s\n",
326 aMethod, aError->message);
327 DBusInhibitFailed(
328 /* aFatal */ !IsCancelledGError(aError.get()));
331 [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
332 // We failed to create DBus proxy. Switch to another
333 // wake lock type.
334 WAKE_LOCK_LOG(
335 "WakeLockTopic::DBusInhibitScreensaver() Proxy creation "
336 "failed: %s\n",
337 aError->message);
338 DBusInhibitFailed(/* aFatal */ !IsCancelledGError(aError.get()));
342 void WakeLockTopic::DBusUninhibitScreensaver(const char* aName,
343 const char* aPath,
344 const char* aCall,
345 const char* aMethod) {
346 WAKE_LOCK_LOG("WakeLockTopic::DBusUninhibitScreensaver() request id %d",
347 mInhibitRequestID ? *mInhibitRequestID : -1);
349 if (!mInhibitRequestID.isSome()) {
350 WAKE_LOCK_LOG(" missing inihibit token, quit.");
351 DBusUninhibitFailed();
352 return;
355 MOZ_DIAGNOSTIC_ASSERT(!mCancellable);
356 MOZ_DIAGNOSTIC_ASSERT(mState == WaitingToUninhibit);
358 mCancellable = dont_AddRef(g_cancellable_new());
360 RefPtr<GVariant> variant =
361 dont_AddRef(g_variant_ref_sink(g_variant_new("(u)", *mInhibitRequestID)));
362 nsCOMPtr<nsISerialEventTarget> target = GetCurrentSerialEventTarget();
363 widget::CreateDBusProxyForBus(
364 G_BUS_TYPE_SESSION,
365 GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
366 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
367 /* aInterfaceInfo = */ nullptr, aName, aPath, aCall, mCancellable)
368 ->Then(
369 target, __func__,
370 [self = RefPtr{this}, this, args = std::move(variant), target,
371 aMethod](RefPtr<GDBusProxy>&& aProxy) {
372 WAKE_LOCK_LOG(
373 "WakeLockTopic::DBusUninhibitScreensaver() proxy created");
374 DBusProxyCall(aProxy.get(), aMethod, args.get(),
375 G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
376 ->Then(
377 target, __func__,
378 [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
379 DBusUninhibitSucceeded();
381 [s = RefPtr{this}, this,
382 aMethod](GUniquePtr<GError>&& aError) {
383 WAKE_LOCK_LOG(
384 "WakeLockTopic::DBusUninhibitFailed() %s call failed "
385 ": %s\n",
386 aMethod, aError->message);
387 DBusUninhibitFailed();
390 [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
391 WAKE_LOCK_LOG(
392 "WakeLockTopic::DBusUninhibitFailed() Proxy creation failed: "
393 "%s\n",
394 aError->message);
395 DBusUninhibitFailed();
399 void WakeLockTopic::InhibitFreeDesktopPortal() {
400 WAKE_LOCK_LOG("WakeLockTopic::InhibitFreeDesktopPortal()");
402 MOZ_DIAGNOSTIC_ASSERT(!mCancellable);
403 MOZ_DIAGNOSTIC_ASSERT(mState == WaitingToInhibit);
405 mCancellable = dont_AddRef(g_cancellable_new());
407 CreateDBusProxyForBus(
408 G_BUS_TYPE_SESSION,
409 GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
410 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
411 nullptr, FREEDESKTOP_PORTAL_DESKTOP_TARGET,
412 FREEDESKTOP_PORTAL_DESKTOP_OBJECT, FREEDESKTOP_PORTAL_DESKTOP_INTERFACE,
413 mCancellable)
414 ->Then(
415 GetCurrentSerialEventTarget(), __func__,
416 [self = RefPtr{this}, this](RefPtr<GDBusProxy>&& aProxy) {
417 GVariantBuilder b;
418 g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT);
419 g_variant_builder_add(&b, "{sv}", "reason",
420 g_variant_new_string(self->mTopic.get()));
422 // From
423 // https://flatpak.github.io/xdg-desktop-portal/docs/#gdbus-org.freedesktop.portal.Inhibit
424 DBusProxyCall(
425 aProxy.get(), "Inhibit",
426 g_variant_new("(sua{sv})", g_get_prgname(),
427 FREEDESKTOP_PORTAL_DESKTOP_INHIBIT_IDLE_FLAG, &b),
428 G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
429 ->Then(
430 GetCurrentSerialEventTarget(), __func__,
431 [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
432 gchar* requestObjectPath = nullptr;
433 g_variant_get(aResult, "(o)", &requestObjectPath);
434 if (!requestObjectPath) {
435 WAKE_LOCK_LOG(
436 "WakeLockTopic::InhibitFreeDesktopPortal(): Unable "
437 "to get requestObjectPath\n");
438 DBusInhibitFailed(/* aFatal */ true);
439 return;
441 WAKE_LOCK_LOG(
442 "WakeLockTopic::InhibitFreeDesktopPortal(): "
443 "inhibited, objpath to unihibit: %s\n",
444 requestObjectPath);
445 mRequestObjectPath.Adopt(requestObjectPath);
446 DBusInhibitSucceeded(0);
448 [s = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
449 DBusInhibitFailed(
450 /* aFatal */ !IsCancelledGError(aError.get()));
451 WAKE_LOCK_LOG(
452 "Failed to create DBus proxy for "
453 "org.freedesktop.portal.Desktop: %s\n",
454 aError->message);
457 [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
458 WAKE_LOCK_LOG(
459 "Failed to create DBus proxy for "
460 "org.freedesktop.portal.Desktop: %s\n",
461 aError->message);
462 DBusInhibitFailed(/* aFatal */ !IsCancelledGError(aError.get()));
466 void WakeLockTopic::InhibitFreeDesktopScreensaver() {
467 WAKE_LOCK_LOG("InhibitFreeDesktopScreensaver()");
468 DBusInhibitScreensaver(FREEDESKTOP_SCREENSAVER_TARGET,
469 FREEDESKTOP_SCREENSAVER_OBJECT,
470 FREEDESKTOP_SCREENSAVER_INTERFACE, "Inhibit",
471 dont_AddRef(g_variant_ref_sink(g_variant_new(
472 "(ss)", g_get_prgname(), mTopic.get()))));
475 void WakeLockTopic::InhibitFreeDesktopPower() {
476 WAKE_LOCK_LOG("InhibitFreeDesktopPower()");
477 DBusInhibitScreensaver(FREEDESKTOP_POWER_TARGET, FREEDESKTOP_POWER_OBJECT,
478 FREEDESKTOP_POWER_INTERFACE, "Inhibit",
479 dont_AddRef(g_variant_ref_sink(g_variant_new(
480 "(ss)", g_get_prgname(), mTopic.get()))));
483 void WakeLockTopic::InhibitGNOME() {
484 WAKE_LOCK_LOG("InhibitGNOME()");
485 static const uint32_t xid = 0;
486 static const uint32_t flags = (1 << 3); // Inhibit idle
487 DBusInhibitScreensaver(
488 SESSION_MANAGER_TARGET, SESSION_MANAGER_OBJECT, SESSION_MANAGER_INTERFACE,
489 "Inhibit",
490 dont_AddRef(g_variant_ref_sink(
491 g_variant_new("(susu)", g_get_prgname(), xid, mTopic.get(), flags))));
494 void WakeLockTopic::UninhibitFreeDesktopPortal() {
495 WAKE_LOCK_LOG("WakeLockTopic::UninhibitFreeDesktopPortal() object path: %s",
496 mRequestObjectPath.get());
498 if (mRequestObjectPath.IsEmpty()) {
499 WAKE_LOCK_LOG("UninhibitFreeDesktopPortal() failed: unknown object path\n");
500 DBusUninhibitFailed();
501 return;
504 MOZ_DIAGNOSTIC_ASSERT(!mCancellable);
505 MOZ_DIAGNOSTIC_ASSERT(mState == WaitingToUninhibit);
507 mCancellable = dont_AddRef(g_cancellable_new());
509 nsCOMPtr<nsISerialEventTarget> target = GetCurrentSerialEventTarget();
510 CreateDBusProxyForBus(
511 G_BUS_TYPE_SESSION,
512 GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
513 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
514 nullptr, FREEDESKTOP_PORTAL_DESKTOP_TARGET, mRequestObjectPath.get(),
515 "org.freedesktop.portal.Request", mCancellable)
516 ->Then(
517 target, __func__,
518 [self = RefPtr{this}, target, this](RefPtr<GDBusProxy>&& aProxy) {
519 DBusProxyCall(aProxy.get(), "Close", nullptr,
520 G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
521 ->Then(
522 target, __func__,
523 [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
524 DBusUninhibitSucceeded();
525 WAKE_LOCK_LOG(
526 "WakeLockTopic::UninhibitFreeDesktopPortal() Inhibit "
527 "removed\n");
529 [s = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
530 DBusUninhibitFailed();
531 WAKE_LOCK_LOG(
532 "WakeLockTopic::UninhibitFreeDesktopPortal() "
533 "Removing inhibit failed: %s\n",
534 aError->message);
537 [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
538 WAKE_LOCK_LOG(
539 "WakeLockTopic::UninhibitFreeDesktopPortal() Proxy creation "
540 "failed: %s\n",
541 aError->message);
542 DBusUninhibitFailed();
546 void WakeLockTopic::UninhibitFreeDesktopScreensaver() {
547 WAKE_LOCK_LOG("UninhibitFreeDesktopScreensaver()");
548 DBusUninhibitScreensaver(FREEDESKTOP_SCREENSAVER_TARGET,
549 FREEDESKTOP_SCREENSAVER_OBJECT,
550 FREEDESKTOP_SCREENSAVER_INTERFACE, "UnInhibit");
553 void WakeLockTopic::UninhibitFreeDesktopPower() {
554 WAKE_LOCK_LOG("UninhibitFreeDesktopPower()");
555 DBusUninhibitScreensaver(FREEDESKTOP_POWER_TARGET, FREEDESKTOP_POWER_OBJECT,
556 FREEDESKTOP_POWER_INTERFACE, "UnInhibit");
559 void WakeLockTopic::UninhibitGNOME() {
560 WAKE_LOCK_LOG("UninhibitGNOME()");
561 DBusUninhibitScreensaver(SESSION_MANAGER_TARGET, SESSION_MANAGER_OBJECT,
562 SESSION_MANAGER_INTERFACE, "Uninhibit");
564 #endif
566 #if defined(MOZ_X11)
567 // TODO: Merge with Idle service?
568 typedef Bool (*_XScreenSaverQueryExtension_fn)(Display* dpy, int* event_base,
569 int* error_base);
570 typedef Bool (*_XScreenSaverQueryVersion_fn)(Display* dpy, int* major,
571 int* minor);
572 typedef void (*_XScreenSaverSuspend_fn)(Display* dpy, Bool suspend);
574 static PRLibrary* sXssLib = nullptr;
575 static _XScreenSaverQueryExtension_fn _XSSQueryExtension = nullptr;
576 static _XScreenSaverQueryVersion_fn _XSSQueryVersion = nullptr;
577 static _XScreenSaverSuspend_fn _XSSSuspend = nullptr;
579 /* static */
580 bool WakeLockTopic::CheckXScreenSaverSupport() {
581 if (!sXssLib) {
582 sXssLib = PR_LoadLibrary("libXss.so.1");
583 if (!sXssLib) {
584 return false;
588 _XSSQueryExtension = (_XScreenSaverQueryExtension_fn)PR_FindFunctionSymbol(
589 sXssLib, "XScreenSaverQueryExtension");
590 _XSSQueryVersion = (_XScreenSaverQueryVersion_fn)PR_FindFunctionSymbol(
591 sXssLib, "XScreenSaverQueryVersion");
592 _XSSSuspend = (_XScreenSaverSuspend_fn)PR_FindFunctionSymbol(
593 sXssLib, "XScreenSaverSuspend");
594 if (!_XSSQueryExtension || !_XSSQueryVersion || !_XSSSuspend) {
595 return false;
598 GdkDisplay* gDisplay = gdk_display_get_default();
599 if (!GdkIsX11Display(gDisplay)) {
600 return false;
602 Display* display = GDK_DISPLAY_XDISPLAY(gDisplay);
604 int throwaway;
605 if (!_XSSQueryExtension(display, &throwaway, &throwaway)) return false;
607 int major, minor;
608 if (!_XSSQueryVersion(display, &major, &minor)) return false;
609 // Needs to be compatible with version 1.1
610 if (major != 1) return false;
611 if (minor < 1) return false;
613 WAKE_LOCK_LOG("XScreenSaver supported.");
614 return true;
617 /* static */
618 bool WakeLockTopic::InhibitXScreenSaver(bool inhibit) {
619 WAKE_LOCK_LOG("InhibitXScreenSaver %d", inhibit);
621 // Set failed state now to remove WaitingTo* one
622 mState = inhibit ? Uninhibited : Inhibited;
624 // Should only be called if CheckXScreenSaverSupport returns true.
625 // There's a couple of safety checks here nonetheless.
626 if (!_XSSSuspend) {
627 return false;
629 GdkDisplay* gDisplay = gdk_display_get_default();
630 if (!GdkIsX11Display(gDisplay)) {
631 return false;
633 Display* display = GDK_DISPLAY_XDISPLAY(gDisplay);
634 _XSSSuspend(display, inhibit);
636 WAKE_LOCK_LOG("InhibitXScreenSaver %d succeeded", inhibit);
637 mState = inhibit ? Inhibited : Uninhibited;
638 return true;
640 #endif
642 #if defined(MOZ_WAYLAND)
643 /* static */
644 bool WakeLockTopic::CheckWaylandIdleInhibitSupport() {
645 nsWaylandDisplay* waylandDisplay = WaylandDisplayGet();
646 return waylandDisplay && waylandDisplay->GetIdleInhibitManager() != nullptr;
649 bool WakeLockTopic::InhibitWaylandIdle() {
650 WAKE_LOCK_LOG("InhibitWaylandIdle()");
652 // Set failed state now to remove WaitingTo* one
653 mState = Uninhibited;
655 nsWaylandDisplay* waylandDisplay = WaylandDisplayGet();
656 if (!waylandDisplay) {
657 return false;
660 nsWindow* focusedWindow = nsWindow::GetFocusedWindow();
661 if (!focusedWindow) {
662 return false;
665 UninhibitWaylandIdle();
667 if (GdkWindow* window = focusedWindow->GetGdkWindow()) {
668 wl_surface* waylandSurface = gdk_wayland_window_get_wl_surface(window);
669 if (waylandSurface) {
670 mWaylandInhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor(
671 waylandDisplay->GetIdleInhibitManager(), waylandSurface);
672 mState = Inhibited;
676 WAKE_LOCK_LOG("InhibitWaylandIdle() %s",
677 !!mWaylandInhibitor ? "succeeded" : "failed");
678 return !!mWaylandInhibitor;
681 bool WakeLockTopic::UninhibitWaylandIdle() {
682 WAKE_LOCK_LOG("UninhibitWaylandIdle() mWaylandInhibitor %p",
683 mWaylandInhibitor);
685 mState = Uninhibited;
686 if (!mWaylandInhibitor) {
687 return false;
689 zwp_idle_inhibitor_v1_destroy(mWaylandInhibitor);
690 mWaylandInhibitor = nullptr;
691 return true;
693 #endif
695 bool WakeLockTopic::SendInhibit() {
696 WAKE_LOCK_LOG("WakeLockTopic::SendInhibit() WakeLockType %s",
697 WakeLockTypeNames[sWakeLockType]);
698 MOZ_ASSERT(sWakeLockType != Initial);
699 switch (sWakeLockType) {
700 #if defined(MOZ_ENABLE_DBUS)
701 case FreeDesktopPortal:
702 InhibitFreeDesktopPortal();
703 break;
704 case FreeDesktopScreensaver:
705 InhibitFreeDesktopScreensaver();
706 break;
707 case FreeDesktopPower:
708 InhibitFreeDesktopPower();
709 break;
710 case GNOME:
711 InhibitGNOME();
712 break;
713 #endif
714 #if defined(MOZ_X11)
715 case XScreenSaver:
716 return InhibitXScreenSaver(true);
717 #endif
718 #if defined(MOZ_WAYLAND)
719 case WaylandIdleInhibit:
720 return InhibitWaylandIdle();
721 #endif
722 default:
723 return false;
725 return true;
728 bool WakeLockTopic::SendUninhibit() {
729 WAKE_LOCK_LOG("WakeLockTopic::SendUninhibit() WakeLockType %s",
730 WakeLockTypeNames[sWakeLockType]);
731 MOZ_ASSERT(sWakeLockType != Initial);
732 switch (sWakeLockType) {
733 #if defined(MOZ_ENABLE_DBUS)
734 case FreeDesktopPortal:
735 UninhibitFreeDesktopPortal();
736 break;
737 case FreeDesktopScreensaver:
738 UninhibitFreeDesktopScreensaver();
739 break;
740 case FreeDesktopPower:
741 UninhibitFreeDesktopPower();
742 break;
743 case GNOME:
744 UninhibitGNOME();
745 break;
746 #endif
747 #if defined(MOZ_X11)
748 case XScreenSaver:
749 return InhibitXScreenSaver(false);
750 #endif
751 #if defined(MOZ_WAYLAND)
752 case WaylandIdleInhibit:
753 return UninhibitWaylandIdle();
754 #endif
755 default:
756 return false;
758 return true;
761 nsresult WakeLockTopic::InhibitScreensaver() {
762 WAKE_LOCK_LOG("WakeLockTopic::InhibitScreensaver() state %s",
763 GetInhibitStateName(mState));
764 // We're broken, don't even try
765 if (sWakeLockType == Unsupported) {
766 return NS_ERROR_FAILURE;
768 mStateQueue.push(Inhibited);
769 if (mState == WaitingToInhibit || mState == WaitingToUninhibit) {
770 return NS_OK;
772 return ProcessNextRequest();
775 nsresult WakeLockTopic::UninhibitScreensaver() {
776 WAKE_LOCK_LOG("WakeLockTopic::UnInhibitScreensaver() state %s",
777 GetInhibitStateName(mState));
778 // We're broken, don't even try
779 if (sWakeLockType == Unsupported) {
780 return NS_ERROR_FAILURE;
782 mStateQueue.push(Uninhibited);
783 if (mState == WaitingToInhibit || mState == WaitingToUninhibit) {
784 return NS_OK;
786 return ProcessNextRequest();
789 nsresult WakeLockTopic::ProcessNextRequest() {
790 WAKE_LOCK_LOG("WakeLockTopic::ProcessNextRequest(): recent state %s",
791 GetInhibitStateName(mState));
792 MOZ_DIAGNOSTIC_ASSERT(mState == Inhibited || mState == Uninhibited);
794 while (!mStateQueue.empty()) {
795 WakeLockState nextState = mStateQueue.front();
796 mStateQueue.pop();
798 WAKE_LOCK_LOG("WakeLockTopic::ProcessNextRequest(): next state %s",
799 GetInhibitStateName(nextState));
801 if (nextState == mState) {
802 continue;
805 switch (nextState) {
806 case Inhibited:
807 mState = WaitingToInhibit;
808 return SendInhibit() ? NS_OK : NS_ERROR_FAILURE;
809 break;
810 case Uninhibited:
811 mState = WaitingToUninhibit;
812 return SendUninhibit() ? NS_OK : NS_ERROR_FAILURE;
813 default:
814 MOZ_DIAGNOSTIC_CRASH("Wrong state!");
815 return NS_ERROR_FAILURE;
819 WAKE_LOCK_LOG("WakeLockTopic::ProcessNextRequest(): empty queue");
820 return NS_OK;
823 void WakeLockTopic::Shutdown() {
824 WAKE_LOCK_LOG("WakeLockTopic::Shutdown() state %s",
825 GetInhibitStateName(mState));
826 #if defined(MOZ_ENABLE_DBUS)
827 if (mCancellable) {
828 g_cancellable_cancel(mCancellable);
829 mCancellable = nullptr;
831 #endif
834 bool WakeLockTopic::IsWakeLockTypeAvailable(int aWakeLockType) {
835 switch (aWakeLockType) {
836 #if defined(MOZ_ENABLE_DBUS)
837 case FreeDesktopPortal:
838 case FreeDesktopScreensaver:
839 case FreeDesktopPower:
840 case GNOME:
841 return true;
842 #endif
843 #if defined(MOZ_X11)
844 case XScreenSaver:
845 if (!GdkIsX11Display()) {
846 return false;
848 if (!CheckXScreenSaverSupport()) {
849 WAKE_LOCK_LOG(" XScreenSaverSupport is missing!");
850 return false;
852 return true;
853 #endif
854 #if defined(MOZ_WAYLAND)
855 case WaylandIdleInhibit:
856 if (!GdkIsWaylandDisplay()) {
857 return false;
859 if (!CheckWaylandIdleInhibitSupport()) {
860 WAKE_LOCK_LOG(" WaylandIdleInhibitSupport is missing!");
861 return false;
863 return true;
864 #endif
865 default:
866 return false;
870 bool WakeLockTopic::IsNativeWakeLock(int aWakeLockType) {
871 switch (aWakeLockType) {
872 #if defined(MOZ_X11)
873 case XScreenSaver:
874 return true;
875 #endif
876 #if defined(MOZ_WAYLAND)
877 case WaylandIdleInhibit:
878 return true;
879 #endif
880 default:
881 return false;
885 bool WakeLockTopic::SwitchToNextWakeLockType() {
886 WAKE_LOCK_LOG("WakeLockTopic::SwitchToNextWakeLockType() WakeLockType %s",
887 WakeLockTypeNames[sWakeLockType]);
889 if (sWakeLockType == Unsupported) {
890 return false;
893 #ifdef MOZ_LOGGING
894 auto printWakeLocktype = MakeScopeExit([&] {
895 WAKE_LOCK_LOG(" switched to WakeLockType %s",
896 WakeLockTypeNames[sWakeLockType]);
898 #endif
900 #if defined(MOZ_ENABLE_DBUS)
901 if (IsDBusWakeLock(sWakeLockType)) {
902 mState = Uninhibited;
903 mCancellable = nullptr;
904 ClearDBusInhibitToken();
906 #endif
908 while (sWakeLockType != Unsupported) {
909 sWakeLockType++;
910 if (IsWakeLockTypeAvailable(sWakeLockType)) {
911 return true;
914 return false;
917 WakeLockListener::WakeLockListener() = default;
919 WakeLockListener::~WakeLockListener() {
920 for (const auto& topic : mTopics.Values()) {
921 topic->Shutdown();
925 nsresult WakeLockListener::Callback(const nsAString& topic,
926 const nsAString& state) {
927 if (!topic.Equals(u"screen"_ns) && !topic.Equals(u"video-playing"_ns) &&
928 !topic.Equals(u"autoscroll"_ns)) {
929 return NS_OK;
932 RefPtr<WakeLockTopic> topicLock = mTopics.LookupOrInsertWith(
933 topic, [&] { return MakeRefPtr<WakeLockTopic>(topic); });
935 // Treat "locked-background" the same as "unlocked" on desktop linux.
936 bool shouldLock = state.EqualsLiteral("locked-foreground");
937 WAKE_LOCK_LOG("WakeLockListener topic %s state %s request lock %d",
938 NS_ConvertUTF16toUTF8(topic).get(),
939 NS_ConvertUTF16toUTF8(state).get(), shouldLock);
941 return shouldLock ? topicLock->InhibitScreensaver()
942 : topicLock->UninhibitScreensaver();