Bug 1873042 - Part 3: Optimise substring(0, 1) pattern. r=jandem
[gecko.git] / widget / gtk / WakeLockListener.cpp
blobb2c43cb4853caa016f56c65a17c58f94bd564434
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 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();
153 void InhibitGNOME();
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();
165 #endif
166 ~WakeLockTopic() = default;
168 // Why is screensaver inhibited
169 nsCString mTopic;
171 // Our desired state
172 bool mShouldInhibit = false;
174 // Our actual sate
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;
189 #endif
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);
200 mInhibited = true;
202 WAKE_LOCK_LOG(
203 "WakeLockTopic::DBusInhibitSucceeded(), mInhibitRequestID %u "
204 "mShouldInhibit %d",
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()) {
222 SendInhibit();
226 void WakeLockTopic::DBusUninhibitSucceeded() {
227 WAKE_LOCK_LOG("WakeLockTopic::DBusUninhibitSucceeded() mShouldInhibit %d",
228 mShouldInhibit);
230 mWaitingForDBusUninhibit = false;
231 mInhibited = 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,
253 const char* aCall,
254 const char* aMethod,
255 RefPtr<GVariant> aArgs) {
256 WAKE_LOCK_LOG(
257 "WakeLockTopic::DBusInhibitScreensaver() mWaitingForDBusInhibit %d "
258 "mWaitingForDBusUninhibit %d",
259 mWaitingForDBusInhibit, mWaitingForDBusUninhibit);
260 if (mWaitingForDBusInhibit) {
261 WAKE_LOCK_LOG(" already waiting to inihibit, return");
262 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(
272 G_BUS_TYPE_SESSION,
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)
276 ->Then(
277 GetCurrentSerialEventTarget(), __func__,
278 [self = RefPtr{this}, this, args = RefPtr{aArgs},
279 aMethod](RefPtr<GDBusProxy>&& aProxy) {
280 WAKE_LOCK_LOG(
281 "WakeLockTopic::DBusInhibitScreensaver() proxy created");
282 DBusProxyCall(aProxy.get(), aMethod, args.get(),
283 G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
284 ->Then(
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) {
290 WAKE_LOCK_LOG(
291 "WakeLockTopic::DBusInhibitScreensaver() wrong "
292 "reply type %s\n",
293 g_variant_get_type_string(aResult.get()));
294 DBusInhibitFailed(/* aFatal */ true);
295 return;
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)) {
301 WAKE_LOCK_LOG(
302 "WakeLockTopic::DBusInhibitScreensaver() wrong "
303 "reply type %s\n",
304 g_variant_get_type_string(aResult.get()));
305 DBusInhibitFailed(/* aFatal */ true);
306 return;
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.
314 WAKE_LOCK_LOG(
315 "WakeLockTopic::DBusInhibitFailed() %s call failed : "
316 "%s\n",
317 aMethod, aError->message);
318 DBusInhibitFailed(
319 /* aFatal */ !IsCancelledGError(aError.get()));
322 [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
323 // We failed to create DBus proxy. Switch to another
324 // wake lock type.
325 WAKE_LOCK_LOG(
326 "WakeLockTopic::DBusInhibitScreensaver() Proxy creation "
327 "failed: %s\n",
328 aError->message);
329 DBusInhibitFailed(/* aFatal */ !IsCancelledGError(aError.get()));
333 void WakeLockTopic::DBusUninhibitScreensaver(const char* aName,
334 const char* aPath,
335 const char* aCall,
336 const char* aMethod) {
337 WAKE_LOCK_LOG(
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");
345 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.
357 return;
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(
365 G_BUS_TYPE_SESSION,
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)
369 ->Then(
370 target, __func__,
371 [self = RefPtr{this}, this, args = std::move(variant), target,
372 aMethod](RefPtr<GDBusProxy>&& aProxy) {
373 WAKE_LOCK_LOG(
374 "WakeLockTopic::DBusUninhibitScreensaver() proxy created");
375 DBusProxyCall(aProxy.get(), aMethod, args.get(),
376 G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
377 ->Then(
378 target, __func__,
379 [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
380 DBusUninhibitSucceeded();
382 [s = RefPtr{this}, this,
383 aMethod](GUniquePtr<GError>&& aError) {
384 WAKE_LOCK_LOG(
385 "WakeLockTopic::DBusUninhibitFailed() %s call failed "
386 ": %s\n",
387 aMethod, aError->message);
388 DBusUninhibitFailed();
391 [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
392 WAKE_LOCK_LOG(
393 "WakeLockTopic::DBusUninhibitFailed() Proxy creation failed: "
394 "%s\n",
395 aError->message);
396 DBusUninhibitFailed();
400 void WakeLockTopic::InhibitFreeDesktopPortal() {
401 WAKE_LOCK_LOG(
402 "WakeLockTopic::InhibitFreeDesktopPortal() mWaitingForDBusInhibit %d "
403 "mWaitingForDBusUninhibit %d",
404 mWaitingForDBusInhibit, mWaitingForDBusUninhibit);
405 if (mWaitingForDBusInhibit) {
406 WAKE_LOCK_LOG(" already waiting to inihibit, return");
407 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(
417 G_BUS_TYPE_SESSION,
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,
422 mCancellable)
423 ->Then(
424 GetCurrentSerialEventTarget(), __func__,
425 [self = RefPtr{this}, this](RefPtr<GDBusProxy>&& aProxy) {
426 GVariantBuilder b;
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()));
431 // From
432 // https://flatpak.github.io/xdg-desktop-portal/docs/#gdbus-org.freedesktop.portal.Inhibit
433 DBusProxyCall(
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)
438 ->Then(
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) {
444 WAKE_LOCK_LOG(
445 "WakeLockTopic::InhibitFreeDesktopPortal(): Unable "
446 "to get requestObjectPath\n");
447 DBusInhibitFailed(/* aFatal */ true);
448 return;
450 WAKE_LOCK_LOG(
451 "WakeLockTopic::InhibitFreeDesktopPortal(): "
452 "inhibited, objpath to unihibit: %s\n",
453 requestObjectPath);
454 mRequestObjectPath.Adopt(requestObjectPath);
455 DBusInhibitSucceeded(0);
457 [s = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
458 DBusInhibitFailed(
459 /* aFatal */ !IsCancelledGError(aError.get()));
460 WAKE_LOCK_LOG(
461 "Failed to create DBus proxy for "
462 "org.freedesktop.portal.Desktop: %s\n",
463 aError->message);
466 [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
467 WAKE_LOCK_LOG(
468 "Failed to create DBus proxy for "
469 "org.freedesktop.portal.Desktop: %s\n",
470 aError->message);
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,
498 "Inhibit",
499 dont_AddRef(g_variant_ref_sink(
500 g_variant_new("(susu)", g_get_prgname(), xid, mTopic.get(), flags))));
503 void WakeLockTopic::UninhibitFreeDesktopPortal() {
504 WAKE_LOCK_LOG(
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");
512 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");
522 return;
524 mWaitingForDBusUninhibit = true;
526 nsCOMPtr<nsISerialEventTarget> target = GetCurrentSerialEventTarget();
527 CreateDBusProxyForBus(
528 G_BUS_TYPE_SESSION,
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)
533 ->Then(
534 target, __func__,
535 [self = RefPtr{this}, target, this](RefPtr<GDBusProxy>&& aProxy) {
536 DBusProxyCall(aProxy.get(), "Close", nullptr,
537 G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
538 ->Then(
539 target, __func__,
540 [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
541 DBusUninhibitSucceeded();
542 WAKE_LOCK_LOG(
543 "WakeLockTopic::UninhibitFreeDesktopPortal() Inhibit "
544 "removed\n");
546 [s = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
547 DBusUninhibitFailed();
548 WAKE_LOCK_LOG(
549 "WakeLockTopic::UninhibitFreeDesktopPortal() "
550 "Removing inhibit failed: %s\n",
551 aError->message);
554 [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
555 WAKE_LOCK_LOG(
556 "WakeLockTopic::UninhibitFreeDesktopPortal() Proxy creation "
557 "failed: %s\n",
558 aError->message);
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");
581 #endif
583 #if defined(MOZ_X11)
584 // TODO: Merge with Idle service?
585 typedef Bool (*_XScreenSaverQueryExtension_fn)(Display* dpy, int* event_base,
586 int* error_base);
587 typedef Bool (*_XScreenSaverQueryVersion_fn)(Display* dpy, int* major,
588 int* minor);
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;
596 /* static */
597 bool WakeLockTopic::CheckXScreenSaverSupport() {
598 if (!sXssLib) {
599 sXssLib = PR_LoadLibrary("libXss.so.1");
600 if (!sXssLib) {
601 return false;
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) {
612 return false;
615 GdkDisplay* gDisplay = gdk_display_get_default();
616 if (!GdkIsX11Display(gDisplay)) {
617 return false;
619 Display* display = GDK_DISPLAY_XDISPLAY(gDisplay);
621 int throwaway;
622 if (!_XSSQueryExtension(display, &throwaway, &throwaway)) return false;
624 int major, minor;
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.");
631 return true;
634 /* static */
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.
640 if (!_XSSSuspend) {
641 return false;
643 GdkDisplay* gDisplay = gdk_display_get_default();
644 if (!GdkIsX11Display(gDisplay)) {
645 return false;
647 Display* display = GDK_DISPLAY_XDISPLAY(gDisplay);
648 _XSSSuspend(display, inhibit);
650 WAKE_LOCK_LOG("InhibitXScreenSaver %d succeeded", inhibit);
651 mInhibited = inhibit;
652 return true;
654 #endif
656 #if defined(MOZ_WAYLAND)
657 /* static */
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) {
668 return false;
671 nsWindow* focusedWindow = nsWindow::GetFocusedWindow();
672 if (!focusedWindow) {
673 return false;
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);
683 mInhibited = true;
686 WAKE_LOCK_LOG("InhibitWaylandIdle() %s",
687 !!mWaylandInhibitor ? "succeeded" : "failed");
688 return !!mWaylandInhibitor;
691 bool WakeLockTopic::UninhibitWaylandIdle() {
692 WAKE_LOCK_LOG("UninhibitWaylandIdle() mWaylandInhibitor %p",
693 mWaylandInhibitor);
695 mInhibited = false;
696 if (!mWaylandInhibitor) {
697 return false;
699 zwp_idle_inhibitor_v1_destroy(mWaylandInhibitor);
700 mWaylandInhibitor = nullptr;
701 return true;
703 #endif
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();
714 break;
715 case FreeDesktopScreensaver:
716 InhibitFreeDesktopScreensaver();
717 break;
718 case FreeDesktopPower:
719 InhibitFreeDesktopPower();
720 break;
721 case GNOME:
722 InhibitGNOME();
723 break;
724 #endif
725 #if defined(MOZ_X11)
726 case XScreenSaver:
727 return InhibitXScreenSaver(true);
728 #endif
729 #if defined(MOZ_WAYLAND)
730 case WaylandIdleInhibit:
731 return InhibitWaylandIdle();
732 #endif
733 default:
734 return false;
736 return true;
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();
747 break;
748 case FreeDesktopScreensaver:
749 UninhibitFreeDesktopScreensaver();
750 break;
751 case FreeDesktopPower:
752 UninhibitFreeDesktopPower();
753 break;
754 case GNOME:
755 UninhibitGNOME();
756 break;
757 #endif
758 #if defined(MOZ_X11)
759 case XScreenSaver:
760 return InhibitXScreenSaver(false);
761 #endif
762 #if defined(MOZ_WAYLAND)
763 case WaylandIdleInhibit:
764 return UninhibitWaylandIdle();
765 #endif
766 default:
767 return false;
769 return true;
772 nsresult WakeLockTopic::InhibitScreensaver() {
773 WAKE_LOCK_LOG("WakeLockTopic::InhibitScreensaver() Inhibited %d", mInhibited);
775 if (mInhibited) {
776 // Screensaver is inhibited. Nothing to do here.
777 return NS_OK;
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) {
793 return;
795 g_cancellable_cancel(mCancellable);
796 #endif
797 if (mInhibited) {
798 UninhibitScreensaver();
802 nsresult WakeLockTopic::UninhibitScreensaver() {
803 WAKE_LOCK_LOG("WakeLockTopic::UninhibitScreensaver() Inhibited %d",
804 mInhibited);
806 if (!mInhibited) {
807 // Screensaver isn't inhibited. Nothing to do here.
808 return NS_OK;
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:
823 case GNOME:
824 return true;
825 #endif
826 #if defined(MOZ_X11)
827 case XScreenSaver:
828 if (!GdkIsX11Display()) {
829 return false;
831 if (!CheckXScreenSaverSupport()) {
832 WAKE_LOCK_LOG(" XScreenSaverSupport is missing!");
833 return false;
835 return true;
836 #endif
837 #if defined(MOZ_WAYLAND)
838 case WaylandIdleInhibit:
839 if (!GdkIsWaylandDisplay()) {
840 return false;
842 if (!CheckWaylandIdleInhibitSupport()) {
843 WAKE_LOCK_LOG(" WaylandIdleInhibitSupport is missing!");
844 return false;
846 return true;
847 #endif
848 default:
849 return false;
853 bool WakeLockTopic::SwitchToNextWakeLockType() {
854 WAKE_LOCK_LOG("WakeLockTopic::SwitchToNextWakeLockType() WakeLockType %s",
855 WakeLockTypeNames[sWakeLockType]);
857 if (sWakeLockType == Unsupported) {
858 return false;
861 #ifdef MOZ_LOGGING
862 auto printWakeLocktype = MakeScopeExit([&] {
863 WAKE_LOCK_LOG(" switched to WakeLockType %s",
864 WakeLockTypeNames[sWakeLockType]);
866 #endif
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;
873 mInhibited = false;
874 ClearDBusInhibitToken();
876 #endif
878 while (sWakeLockType != Unsupported) {
879 sWakeLockType++;
880 if (IsWakeLockTypeAvailable(sWakeLockType)) {
881 return true;
884 return false;
887 WakeLockListener::WakeLockListener() = default;
889 WakeLockListener::~WakeLockListener() {
890 for (const auto& topic : mTopics.Values()) {
891 topic->Shutdown();
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)) {
899 return NS_OK;
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();