summaryrefslogtreecommitdiffstats
path: root/widget/gtk/WakeLockListener.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/gtk/WakeLockListener.cpp')
-rw-r--r--widget/gtk/WakeLockListener.cpp913
1 files changed, 913 insertions, 0 deletions
diff --git a/widget/gtk/WakeLockListener.cpp b/widget/gtk/WakeLockListener.cpp
new file mode 100644
index 0000000000..b2c43cb485
--- /dev/null
+++ b/widget/gtk/WakeLockListener.cpp
@@ -0,0 +1,913 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=2:tabstop=2:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WakeLockListener.h"
+#include "WidgetUtilsGtk.h"
+#include "mozilla/ScopeExit.h"
+
+#ifdef MOZ_ENABLE_DBUS
+# include <gio/gio.h>
+# include "AsyncDBus.h"
+#endif
+
+#if defined(MOZ_X11)
+# include "prlink.h"
+# include <gdk/gdk.h>
+# include <gdk/gdkx.h>
+# include "X11UndefineNone.h"
+#endif
+
+#if defined(MOZ_WAYLAND)
+# include "mozilla/widget/nsWaylandDisplay.h"
+# include "nsWindow.h"
+#endif
+
+#ifdef MOZ_ENABLE_DBUS
+# define FREEDESKTOP_PORTAL_DESKTOP_TARGET "org.freedesktop.portal.Desktop"
+# define FREEDESKTOP_PORTAL_DESKTOP_OBJECT "/org/freedesktop/portal/desktop"
+# define FREEDESKTOP_PORTAL_DESKTOP_INTERFACE "org.freedesktop.portal.Inhibit"
+# define FREEDESKTOP_PORTAL_DESKTOP_INHIBIT_IDLE_FLAG 8
+
+# define FREEDESKTOP_SCREENSAVER_TARGET "org.freedesktop.ScreenSaver"
+# define FREEDESKTOP_SCREENSAVER_OBJECT "/ScreenSaver"
+# define FREEDESKTOP_SCREENSAVER_INTERFACE "org.freedesktop.ScreenSaver"
+
+# define FREEDESKTOP_POWER_TARGET "org.freedesktop.PowerManagement"
+# define FREEDESKTOP_POWER_OBJECT "/org/freedesktop/PowerManagement/Inhibit"
+# define FREEDESKTOP_POWER_INTERFACE "org.freedesktop.PowerManagement.Inhibit"
+
+# define SESSION_MANAGER_TARGET "org.gnome.SessionManager"
+# define SESSION_MANAGER_OBJECT "/org/gnome/SessionManager"
+# define SESSION_MANAGER_INTERFACE "org.gnome.SessionManager"
+
+# define DBUS_TIMEOUT (-1)
+#endif
+
+using namespace mozilla;
+using namespace mozilla::widget;
+
+NS_IMPL_ISUPPORTS(WakeLockListener, nsIDOMMozWakeLockListener)
+
+#define WAKE_LOCK_LOG(str, ...) \
+ MOZ_LOG(gLinuxWakeLockLog, mozilla::LogLevel::Debug, \
+ ("[%p] " str, this, ##__VA_ARGS__))
+static mozilla::LazyLogModule gLinuxWakeLockLog("LinuxWakeLock");
+
+enum WakeLockType {
+ Initial = 0,
+#if defined(MOZ_ENABLE_DBUS)
+ FreeDesktopScreensaver = 1,
+ FreeDesktopPower = 2,
+ FreeDesktopPortal = 3,
+ GNOME = 4,
+#endif
+#if defined(MOZ_X11)
+ XScreenSaver = 5,
+#endif
+#if defined(MOZ_WAYLAND)
+ WaylandIdleInhibit = 6,
+#endif
+ Unsupported = 7,
+};
+
+#if defined(MOZ_ENABLE_DBUS)
+bool IsDBusWakeLock(int aWakeLockType) {
+ switch (aWakeLockType) {
+ case FreeDesktopScreensaver:
+ case FreeDesktopPower:
+ case GNOME:
+ case FreeDesktopPortal:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+#ifdef MOZ_LOGGING
+const char* WakeLockTypeNames[] = {
+ "Initial",
+ "FreeDesktopScreensaver",
+ "FreeDesktopPower",
+ "FreeDesktopPortal",
+ "GNOME",
+ "XScreenSaver",
+ "WaylandIdleInhibit",
+ "Unsupported",
+};
+#endif
+
+class WakeLockTopic {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(WakeLockTopic)
+
+ explicit WakeLockTopic(const nsAString& aTopic) {
+ CopyUTF16toUTF8(aTopic, mTopic);
+ WAKE_LOCK_LOG("WakeLockTopic::WakeLockTopic() created %s", mTopic.get());
+ if (sWakeLockType == Initial) {
+ SwitchToNextWakeLockType();
+ }
+#ifdef MOZ_ENABLE_DBUS
+ mCancellable = dont_AddRef(g_cancellable_new());
+#endif
+ }
+
+ nsresult InhibitScreensaver();
+ nsresult UninhibitScreensaver();
+
+ void Shutdown();
+
+ private:
+ bool SendInhibit();
+ bool SendUninhibit();
+
+#if defined(MOZ_X11)
+ bool CheckXScreenSaverSupport();
+ bool InhibitXScreenSaver(bool inhibit);
+#endif
+
+#if defined(MOZ_WAYLAND)
+ zwp_idle_inhibitor_v1* mWaylandInhibitor = nullptr;
+ static bool CheckWaylandIdleInhibitSupport();
+ bool InhibitWaylandIdle();
+ bool UninhibitWaylandIdle();
+#endif
+
+ bool IsWakeLockTypeAvailable(int aWakeLockType);
+ bool SwitchToNextWakeLockType();
+
+#ifdef MOZ_ENABLE_DBUS
+ void DBusInhibitScreensaver(const char* aName, const char* aPath,
+ const char* aCall, const char* aMethod,
+ RefPtr<GVariant> aArgs);
+ void DBusUninhibitScreensaver(const char* aName, const char* aPath,
+ const char* aCall, const char* aMethod);
+
+ void InhibitFreeDesktopPortal();
+ void InhibitFreeDesktopScreensaver();
+ void InhibitFreeDesktopPower();
+ void InhibitGNOME();
+
+ void UninhibitFreeDesktopPortal();
+ void UninhibitFreeDesktopScreensaver();
+ void UninhibitFreeDesktopPower();
+ void UninhibitGNOME();
+
+ void DBusInhibitSucceeded(uint32_t aInhibitRequestID);
+ void DBusInhibitFailed(bool aFatal);
+ void DBusUninhibitSucceeded();
+ void DBusUninhibitFailed();
+ void ClearDBusInhibitToken();
+#endif
+ ~WakeLockTopic() = default;
+
+ // Why is screensaver inhibited
+ nsCString mTopic;
+
+ // Our desired state
+ bool mShouldInhibit = false;
+
+ // Our actual sate
+ bool mInhibited = false;
+
+#ifdef MOZ_ENABLE_DBUS
+ // We're waiting for DBus reply (inhibit/uninhibit calls).
+ bool mWaitingForDBusInhibit = false;
+ bool mWaitingForDBusUninhibit = false;
+
+ // mInhibitRequestID is received from success screen saver inhibit call
+ // and it's needed for screen saver enablement.
+ Maybe<uint32_t> mInhibitRequestID;
+
+ RefPtr<GCancellable> mCancellable;
+ // Used to uninhibit org.freedesktop.portal.Inhibit request
+ nsCString mRequestObjectPath;
+#endif
+
+ static int sWakeLockType;
+};
+
+int WakeLockTopic::sWakeLockType = Initial;
+
+#ifdef MOZ_ENABLE_DBUS
+void WakeLockTopic::DBusInhibitSucceeded(uint32_t aInhibitRequestID) {
+ mWaitingForDBusInhibit = false;
+ mInhibitRequestID = Some(aInhibitRequestID);
+ mInhibited = true;
+
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::DBusInhibitSucceeded(), mInhibitRequestID %u "
+ "mShouldInhibit %d",
+ *mInhibitRequestID, mShouldInhibit);
+
+ // Uninhibit was requested before inhibit request was finished.
+ // So ask for it now.
+ if (!mShouldInhibit) {
+ UninhibitScreensaver();
+ }
+}
+
+void WakeLockTopic::DBusInhibitFailed(bool aFatal) {
+ WAKE_LOCK_LOG("WakeLockTopic::DBusInhibitFailed(%d)", aFatal);
+
+ mWaitingForDBusInhibit = false;
+ ClearDBusInhibitToken();
+
+ // Non-recoverable DBus error. Switch to another wake lock type.
+ if (aFatal && SwitchToNextWakeLockType()) {
+ SendInhibit();
+ }
+}
+
+void WakeLockTopic::DBusUninhibitSucceeded() {
+ WAKE_LOCK_LOG("WakeLockTopic::DBusUninhibitSucceeded() mShouldInhibit %d",
+ mShouldInhibit);
+
+ mWaitingForDBusUninhibit = false;
+ mInhibited = false;
+ ClearDBusInhibitToken();
+
+ // Inhibit was requested before uninhibit request was finished.
+ // So ask for it now.
+ if (mShouldInhibit) {
+ InhibitScreensaver();
+ }
+}
+
+void WakeLockTopic::DBusUninhibitFailed() {
+ WAKE_LOCK_LOG("WakeLockTopic::DBusUninhibitFailed()");
+ mWaitingForDBusUninhibit = false;
+ mInhibitRequestID = Nothing();
+}
+
+void WakeLockTopic::ClearDBusInhibitToken() {
+ mRequestObjectPath.Truncate();
+ mInhibitRequestID = Nothing();
+}
+
+void WakeLockTopic::DBusInhibitScreensaver(const char* aName, const char* aPath,
+ const char* aCall,
+ const char* aMethod,
+ RefPtr<GVariant> aArgs) {
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::DBusInhibitScreensaver() mWaitingForDBusInhibit %d "
+ "mWaitingForDBusUninhibit %d",
+ mWaitingForDBusInhibit, mWaitingForDBusUninhibit);
+ if (mWaitingForDBusInhibit) {
+ WAKE_LOCK_LOG(" already waiting to inihibit, return");
+ return;
+ }
+ if (mWaitingForDBusUninhibit) {
+ WAKE_LOCK_LOG(" cancel un-inihibit request");
+ g_cancellable_cancel(mCancellable);
+ mWaitingForDBusUninhibit = false;
+ }
+ mWaitingForDBusInhibit = true;
+
+ widget::CreateDBusProxyForBus(
+ G_BUS_TYPE_SESSION,
+ GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
+ /* aInterfaceInfo = */ nullptr, aName, aPath, aCall, mCancellable)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [self = RefPtr{this}, this, args = RefPtr{aArgs},
+ aMethod](RefPtr<GDBusProxy>&& aProxy) {
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::DBusInhibitScreensaver() proxy created");
+ DBusProxyCall(aProxy.get(), aMethod, args.get(),
+ G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
+ if (!g_variant_is_of_type(aResult.get(),
+ G_VARIANT_TYPE_TUPLE) ||
+ g_variant_n_children(aResult.get()) != 1) {
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::DBusInhibitScreensaver() wrong "
+ "reply type %s\n",
+ g_variant_get_type_string(aResult.get()));
+ DBusInhibitFailed(/* aFatal */ true);
+ return;
+ }
+ RefPtr<GVariant> variant = dont_AddRef(
+ g_variant_get_child_value(aResult.get(), 0));
+ if (!g_variant_is_of_type(variant,
+ G_VARIANT_TYPE_UINT32)) {
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::DBusInhibitScreensaver() wrong "
+ "reply type %s\n",
+ g_variant_get_type_string(aResult.get()));
+ DBusInhibitFailed(/* aFatal */ true);
+ return;
+ }
+ DBusInhibitSucceeded(g_variant_get_uint32(variant));
+ },
+ [s = RefPtr{this}, this,
+ aMethod](GUniquePtr<GError>&& aError) {
+ // Failed to send inhibit request over proxy.
+ // Switch to another wake lock type.
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::DBusInhibitFailed() %s call failed : "
+ "%s\n",
+ aMethod, aError->message);
+ DBusInhibitFailed(
+ /* aFatal */ !IsCancelledGError(aError.get()));
+ });
+ },
+ [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
+ // We failed to create DBus proxy. Switch to another
+ // wake lock type.
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::DBusInhibitScreensaver() Proxy creation "
+ "failed: %s\n",
+ aError->message);
+ DBusInhibitFailed(/* aFatal */ !IsCancelledGError(aError.get()));
+ });
+}
+
+void WakeLockTopic::DBusUninhibitScreensaver(const char* aName,
+ const char* aPath,
+ const char* aCall,
+ const char* aMethod) {
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::DBusUninhibitScreensaver() mWaitingForDBusInhibit %d "
+ "mWaitingForDBusUninhibit %d request id %d",
+ mWaitingForDBusInhibit, mWaitingForDBusUninhibit,
+ mInhibitRequestID ? *mInhibitRequestID : -1);
+
+ if (mWaitingForDBusUninhibit) {
+ WAKE_LOCK_LOG(" already waiting to uninihibit, return");
+ return;
+ }
+
+ if (mWaitingForDBusInhibit) {
+ WAKE_LOCK_LOG(" cancel inihibit request");
+ g_cancellable_cancel(mCancellable);
+ mWaitingForDBusInhibit = false;
+ }
+
+ if (!mInhibitRequestID.isSome()) {
+ WAKE_LOCK_LOG(" missing inihibit token, quit.");
+ // missing uninhibit token, just quit.
+ return;
+ }
+ mWaitingForDBusUninhibit = true;
+
+ RefPtr<GVariant> variant =
+ dont_AddRef(g_variant_ref_sink(g_variant_new("(u)", *mInhibitRequestID)));
+ nsCOMPtr<nsISerialEventTarget> target = GetCurrentSerialEventTarget();
+ widget::CreateDBusProxyForBus(
+ G_BUS_TYPE_SESSION,
+ GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
+ /* aInterfaceInfo = */ nullptr, aName, aPath, aCall, mCancellable)
+ ->Then(
+ target, __func__,
+ [self = RefPtr{this}, this, args = std::move(variant), target,
+ aMethod](RefPtr<GDBusProxy>&& aProxy) {
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::DBusUninhibitScreensaver() proxy created");
+ DBusProxyCall(aProxy.get(), aMethod, args.get(),
+ G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
+ ->Then(
+ target, __func__,
+ [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
+ DBusUninhibitSucceeded();
+ },
+ [s = RefPtr{this}, this,
+ aMethod](GUniquePtr<GError>&& aError) {
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::DBusUninhibitFailed() %s call failed "
+ ": %s\n",
+ aMethod, aError->message);
+ DBusUninhibitFailed();
+ });
+ },
+ [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::DBusUninhibitFailed() Proxy creation failed: "
+ "%s\n",
+ aError->message);
+ DBusUninhibitFailed();
+ });
+}
+
+void WakeLockTopic::InhibitFreeDesktopPortal() {
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::InhibitFreeDesktopPortal() mWaitingForDBusInhibit %d "
+ "mWaitingForDBusUninhibit %d",
+ mWaitingForDBusInhibit, mWaitingForDBusUninhibit);
+ if (mWaitingForDBusInhibit) {
+ WAKE_LOCK_LOG(" already waiting to inihibit, return");
+ return;
+ }
+ if (mWaitingForDBusUninhibit) {
+ WAKE_LOCK_LOG(" cancel un-inihibit request");
+ g_cancellable_cancel(mCancellable);
+ mWaitingForDBusUninhibit = false;
+ }
+ mWaitingForDBusInhibit = true;
+
+ CreateDBusProxyForBus(
+ G_BUS_TYPE_SESSION,
+ GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
+ nullptr, FREEDESKTOP_PORTAL_DESKTOP_TARGET,
+ FREEDESKTOP_PORTAL_DESKTOP_OBJECT, FREEDESKTOP_PORTAL_DESKTOP_INTERFACE,
+ mCancellable)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [self = RefPtr{this}, this](RefPtr<GDBusProxy>&& aProxy) {
+ GVariantBuilder b;
+ g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add(&b, "{sv}", "reason",
+ g_variant_new_string(self->mTopic.get()));
+
+ // From
+ // https://flatpak.github.io/xdg-desktop-portal/docs/#gdbus-org.freedesktop.portal.Inhibit
+ DBusProxyCall(
+ aProxy.get(), "Inhibit",
+ g_variant_new("(sua{sv})", g_get_prgname(),
+ FREEDESKTOP_PORTAL_DESKTOP_INHIBIT_IDLE_FLAG, &b),
+ G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
+ gchar* requestObjectPath = nullptr;
+ g_variant_get(aResult, "(o)", &requestObjectPath);
+ if (!requestObjectPath) {
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::InhibitFreeDesktopPortal(): Unable "
+ "to get requestObjectPath\n");
+ DBusInhibitFailed(/* aFatal */ true);
+ return;
+ }
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::InhibitFreeDesktopPortal(): "
+ "inhibited, objpath to unihibit: %s\n",
+ requestObjectPath);
+ mRequestObjectPath.Adopt(requestObjectPath);
+ DBusInhibitSucceeded(0);
+ },
+ [s = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
+ DBusInhibitFailed(
+ /* aFatal */ !IsCancelledGError(aError.get()));
+ WAKE_LOCK_LOG(
+ "Failed to create DBus proxy for "
+ "org.freedesktop.portal.Desktop: %s\n",
+ aError->message);
+ });
+ },
+ [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
+ WAKE_LOCK_LOG(
+ "Failed to create DBus proxy for "
+ "org.freedesktop.portal.Desktop: %s\n",
+ aError->message);
+ DBusInhibitFailed(/* aFatal */ !IsCancelledGError(aError.get()));
+ });
+}
+
+void WakeLockTopic::InhibitFreeDesktopScreensaver() {
+ WAKE_LOCK_LOG("InhibitFreeDesktopScreensaver()");
+ DBusInhibitScreensaver(FREEDESKTOP_SCREENSAVER_TARGET,
+ FREEDESKTOP_SCREENSAVER_OBJECT,
+ FREEDESKTOP_SCREENSAVER_INTERFACE, "Inhibit",
+ dont_AddRef(g_variant_ref_sink(g_variant_new(
+ "(ss)", g_get_prgname(), mTopic.get()))));
+}
+
+void WakeLockTopic::InhibitFreeDesktopPower() {
+ WAKE_LOCK_LOG("InhibitFreeDesktopPower()");
+ DBusInhibitScreensaver(FREEDESKTOP_POWER_TARGET, FREEDESKTOP_POWER_OBJECT,
+ FREEDESKTOP_POWER_INTERFACE, "Inhibit",
+ dont_AddRef(g_variant_ref_sink(g_variant_new(
+ "(ss)", g_get_prgname(), mTopic.get()))));
+}
+
+void WakeLockTopic::InhibitGNOME() {
+ WAKE_LOCK_LOG("InhibitGNOME()");
+ static const uint32_t xid = 0;
+ static const uint32_t flags = (1 << 3); // Inhibit idle
+ DBusInhibitScreensaver(
+ SESSION_MANAGER_TARGET, SESSION_MANAGER_OBJECT, SESSION_MANAGER_INTERFACE,
+ "Inhibit",
+ dont_AddRef(g_variant_ref_sink(
+ g_variant_new("(susu)", g_get_prgname(), xid, mTopic.get(), flags))));
+}
+
+void WakeLockTopic::UninhibitFreeDesktopPortal() {
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::UninhibitFreeDesktopPortal() mWaitingForDBusInhibit %d "
+ "mWaitingForDBusUninhibit %d object path: %s",
+ mWaitingForDBusInhibit, mWaitingForDBusUninhibit,
+ mRequestObjectPath.get());
+
+ if (mWaitingForDBusUninhibit) {
+ WAKE_LOCK_LOG(" already waiting to uninihibit, return");
+ return;
+ }
+
+ if (mWaitingForDBusInhibit) {
+ WAKE_LOCK_LOG(" cancel inihibit request");
+ g_cancellable_cancel(mCancellable);
+ mWaitingForDBusInhibit = false;
+ }
+ if (mRequestObjectPath.IsEmpty()) {
+ WAKE_LOCK_LOG("UninhibitFreeDesktopPortal() failed: unknown object path\n");
+ return;
+ }
+ mWaitingForDBusUninhibit = true;
+
+ nsCOMPtr<nsISerialEventTarget> target = GetCurrentSerialEventTarget();
+ CreateDBusProxyForBus(
+ G_BUS_TYPE_SESSION,
+ GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
+ nullptr, FREEDESKTOP_PORTAL_DESKTOP_TARGET, mRequestObjectPath.get(),
+ "org.freedesktop.portal.Request", mCancellable)
+ ->Then(
+ target, __func__,
+ [self = RefPtr{this}, target, this](RefPtr<GDBusProxy>&& aProxy) {
+ DBusProxyCall(aProxy.get(), "Close", nullptr,
+ G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, mCancellable)
+ ->Then(
+ target, __func__,
+ [s = RefPtr{this}, this](RefPtr<GVariant>&& aResult) {
+ DBusUninhibitSucceeded();
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::UninhibitFreeDesktopPortal() Inhibit "
+ "removed\n");
+ },
+ [s = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
+ DBusUninhibitFailed();
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::UninhibitFreeDesktopPortal() "
+ "Removing inhibit failed: %s\n",
+ aError->message);
+ });
+ },
+ [self = RefPtr{this}, this](GUniquePtr<GError>&& aError) {
+ WAKE_LOCK_LOG(
+ "WakeLockTopic::UninhibitFreeDesktopPortal() Proxy creation "
+ "failed: %s\n",
+ aError->message);
+ DBusUninhibitFailed();
+ });
+}
+
+void WakeLockTopic::UninhibitFreeDesktopScreensaver() {
+ WAKE_LOCK_LOG("UninhibitFreeDesktopScreensaver()");
+ DBusUninhibitScreensaver(FREEDESKTOP_SCREENSAVER_TARGET,
+ FREEDESKTOP_SCREENSAVER_OBJECT,
+ FREEDESKTOP_SCREENSAVER_INTERFACE, "UnInhibit");
+}
+
+void WakeLockTopic::UninhibitFreeDesktopPower() {
+ WAKE_LOCK_LOG("UninhibitFreeDesktopPower()");
+ DBusUninhibitScreensaver(FREEDESKTOP_POWER_TARGET, FREEDESKTOP_POWER_OBJECT,
+ FREEDESKTOP_POWER_INTERFACE, "UnInhibit");
+}
+
+void WakeLockTopic::UninhibitGNOME() {
+ WAKE_LOCK_LOG("UninhibitGNOME()");
+ DBusUninhibitScreensaver(SESSION_MANAGER_TARGET, SESSION_MANAGER_OBJECT,
+ SESSION_MANAGER_INTERFACE, "Uninhibit");
+}
+#endif
+
+#if defined(MOZ_X11)
+// TODO: Merge with Idle service?
+typedef Bool (*_XScreenSaverQueryExtension_fn)(Display* dpy, int* event_base,
+ int* error_base);
+typedef Bool (*_XScreenSaverQueryVersion_fn)(Display* dpy, int* major,
+ int* minor);
+typedef void (*_XScreenSaverSuspend_fn)(Display* dpy, Bool suspend);
+
+static PRLibrary* sXssLib = nullptr;
+static _XScreenSaverQueryExtension_fn _XSSQueryExtension = nullptr;
+static _XScreenSaverQueryVersion_fn _XSSQueryVersion = nullptr;
+static _XScreenSaverSuspend_fn _XSSSuspend = nullptr;
+
+/* static */
+bool WakeLockTopic::CheckXScreenSaverSupport() {
+ if (!sXssLib) {
+ sXssLib = PR_LoadLibrary("libXss.so.1");
+ if (!sXssLib) {
+ return false;
+ }
+ }
+
+ _XSSQueryExtension = (_XScreenSaverQueryExtension_fn)PR_FindFunctionSymbol(
+ sXssLib, "XScreenSaverQueryExtension");
+ _XSSQueryVersion = (_XScreenSaverQueryVersion_fn)PR_FindFunctionSymbol(
+ sXssLib, "XScreenSaverQueryVersion");
+ _XSSSuspend = (_XScreenSaverSuspend_fn)PR_FindFunctionSymbol(
+ sXssLib, "XScreenSaverSuspend");
+ if (!_XSSQueryExtension || !_XSSQueryVersion || !_XSSSuspend) {
+ return false;
+ }
+
+ GdkDisplay* gDisplay = gdk_display_get_default();
+ if (!GdkIsX11Display(gDisplay)) {
+ return false;
+ }
+ Display* display = GDK_DISPLAY_XDISPLAY(gDisplay);
+
+ int throwaway;
+ if (!_XSSQueryExtension(display, &throwaway, &throwaway)) return false;
+
+ int major, minor;
+ if (!_XSSQueryVersion(display, &major, &minor)) return false;
+ // Needs to be compatible with version 1.1
+ if (major != 1) return false;
+ if (minor < 1) return false;
+
+ WAKE_LOCK_LOG("XScreenSaver supported.");
+ return true;
+}
+
+/* static */
+bool WakeLockTopic::InhibitXScreenSaver(bool inhibit) {
+ WAKE_LOCK_LOG("InhibitXScreenSaver %d", inhibit);
+
+ // Should only be called if CheckXScreenSaverSupport returns true.
+ // There's a couple of safety checks here nonetheless.
+ if (!_XSSSuspend) {
+ return false;
+ }
+ GdkDisplay* gDisplay = gdk_display_get_default();
+ if (!GdkIsX11Display(gDisplay)) {
+ return false;
+ }
+ Display* display = GDK_DISPLAY_XDISPLAY(gDisplay);
+ _XSSSuspend(display, inhibit);
+
+ WAKE_LOCK_LOG("InhibitXScreenSaver %d succeeded", inhibit);
+ mInhibited = inhibit;
+ return true;
+}
+#endif
+
+#if defined(MOZ_WAYLAND)
+/* static */
+bool WakeLockTopic::CheckWaylandIdleInhibitSupport() {
+ nsWaylandDisplay* waylandDisplay = WaylandDisplayGet();
+ return waylandDisplay && waylandDisplay->GetIdleInhibitManager() != nullptr;
+}
+
+bool WakeLockTopic::InhibitWaylandIdle() {
+ WAKE_LOCK_LOG("InhibitWaylandIdle()");
+
+ nsWaylandDisplay* waylandDisplay = WaylandDisplayGet();
+ if (!waylandDisplay) {
+ return false;
+ }
+
+ nsWindow* focusedWindow = nsWindow::GetFocusedWindow();
+ if (!focusedWindow) {
+ return false;
+ }
+
+ UninhibitWaylandIdle();
+
+ MozContainerSurfaceLock lock(focusedWindow->GetMozContainer());
+ struct wl_surface* waylandSurface = lock.GetSurface();
+ if (waylandSurface) {
+ mWaylandInhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor(
+ waylandDisplay->GetIdleInhibitManager(), waylandSurface);
+ mInhibited = true;
+ }
+
+ WAKE_LOCK_LOG("InhibitWaylandIdle() %s",
+ !!mWaylandInhibitor ? "succeeded" : "failed");
+ return !!mWaylandInhibitor;
+}
+
+bool WakeLockTopic::UninhibitWaylandIdle() {
+ WAKE_LOCK_LOG("UninhibitWaylandIdle() mWaylandInhibitor %p",
+ mWaylandInhibitor);
+
+ mInhibited = false;
+ if (!mWaylandInhibitor) {
+ return false;
+ }
+ zwp_idle_inhibitor_v1_destroy(mWaylandInhibitor);
+ mWaylandInhibitor = nullptr;
+ return true;
+}
+#endif
+
+bool WakeLockTopic::SendInhibit() {
+ WAKE_LOCK_LOG("WakeLockTopic::SendInhibit() WakeLockType %s",
+ WakeLockTypeNames[sWakeLockType]);
+ MOZ_ASSERT(sWakeLockType != Initial);
+
+ switch (sWakeLockType) {
+#if defined(MOZ_ENABLE_DBUS)
+ case FreeDesktopPortal:
+ InhibitFreeDesktopPortal();
+ break;
+ case FreeDesktopScreensaver:
+ InhibitFreeDesktopScreensaver();
+ break;
+ case FreeDesktopPower:
+ InhibitFreeDesktopPower();
+ break;
+ case GNOME:
+ InhibitGNOME();
+ break;
+#endif
+#if defined(MOZ_X11)
+ case XScreenSaver:
+ return InhibitXScreenSaver(true);
+#endif
+#if defined(MOZ_WAYLAND)
+ case WaylandIdleInhibit:
+ return InhibitWaylandIdle();
+#endif
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool WakeLockTopic::SendUninhibit() {
+ WAKE_LOCK_LOG("WakeLockTopic::SendUninhibit() WakeLockType %s",
+ WakeLockTypeNames[sWakeLockType]);
+ MOZ_ASSERT(sWakeLockType != Initial);
+ switch (sWakeLockType) {
+#if defined(MOZ_ENABLE_DBUS)
+ case FreeDesktopPortal:
+ UninhibitFreeDesktopPortal();
+ break;
+ case FreeDesktopScreensaver:
+ UninhibitFreeDesktopScreensaver();
+ break;
+ case FreeDesktopPower:
+ UninhibitFreeDesktopPower();
+ break;
+ case GNOME:
+ UninhibitGNOME();
+ break;
+#endif
+#if defined(MOZ_X11)
+ case XScreenSaver:
+ return InhibitXScreenSaver(false);
+#endif
+#if defined(MOZ_WAYLAND)
+ case WaylandIdleInhibit:
+ return UninhibitWaylandIdle();
+#endif
+ default:
+ return false;
+ }
+ return true;
+}
+
+nsresult WakeLockTopic::InhibitScreensaver() {
+ WAKE_LOCK_LOG("WakeLockTopic::InhibitScreensaver() Inhibited %d", mInhibited);
+
+ if (mInhibited) {
+ // Screensaver is inhibited. Nothing to do here.
+ return NS_OK;
+ }
+ mShouldInhibit = true;
+
+ // Iterate through wake lock types in case of failure.
+ while (!SendInhibit() && SwitchToNextWakeLockType()) {
+ ;
+ }
+
+ return (sWakeLockType != Unsupported) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+void WakeLockTopic::Shutdown() {
+ WAKE_LOCK_LOG("WakeLockTopic::Shutdown() state %d", mInhibited);
+#ifdef MOZ_ENABLE_DBUS
+ if (mWaitingForDBusUninhibit) {
+ return;
+ }
+ g_cancellable_cancel(mCancellable);
+#endif
+ if (mInhibited) {
+ UninhibitScreensaver();
+ }
+}
+
+nsresult WakeLockTopic::UninhibitScreensaver() {
+ WAKE_LOCK_LOG("WakeLockTopic::UninhibitScreensaver() Inhibited %d",
+ mInhibited);
+
+ if (!mInhibited) {
+ // Screensaver isn't inhibited. Nothing to do here.
+ return NS_OK;
+ }
+ mShouldInhibit = false;
+
+ // Don't switch wake lock type in case of failure.
+ // We need to use the same lock/unlock type.
+ return SendUninhibit() ? NS_OK : NS_ERROR_FAILURE;
+}
+
+bool WakeLockTopic::IsWakeLockTypeAvailable(int aWakeLockType) {
+ switch (aWakeLockType) {
+#if defined(MOZ_ENABLE_DBUS)
+ case FreeDesktopPortal:
+ case FreeDesktopScreensaver:
+ case FreeDesktopPower:
+ case GNOME:
+ return true;
+#endif
+#if defined(MOZ_X11)
+ case XScreenSaver:
+ if (!GdkIsX11Display()) {
+ return false;
+ }
+ if (!CheckXScreenSaverSupport()) {
+ WAKE_LOCK_LOG(" XScreenSaverSupport is missing!");
+ return false;
+ }
+ return true;
+#endif
+#if defined(MOZ_WAYLAND)
+ case WaylandIdleInhibit:
+ if (!GdkIsWaylandDisplay()) {
+ return false;
+ }
+ if (!CheckWaylandIdleInhibitSupport()) {
+ WAKE_LOCK_LOG(" WaylandIdleInhibitSupport is missing!");
+ return false;
+ }
+ return true;
+#endif
+ default:
+ return false;
+ }
+}
+
+bool WakeLockTopic::SwitchToNextWakeLockType() {
+ WAKE_LOCK_LOG("WakeLockTopic::SwitchToNextWakeLockType() WakeLockType %s",
+ WakeLockTypeNames[sWakeLockType]);
+
+ if (sWakeLockType == Unsupported) {
+ return false;
+ }
+
+#ifdef MOZ_LOGGING
+ auto printWakeLocktype = MakeScopeExit([&] {
+ WAKE_LOCK_LOG(" switched to WakeLockType %s",
+ WakeLockTypeNames[sWakeLockType]);
+ });
+#endif
+
+#if defined(MOZ_ENABLE_DBUS)
+ if (IsDBusWakeLock(sWakeLockType)) {
+ // We're switching out of DBus wakelock - clear our recent DBus states.
+ mWaitingForDBusInhibit = false;
+ mWaitingForDBusUninhibit = false;
+ mInhibited = false;
+ ClearDBusInhibitToken();
+ }
+#endif
+
+ while (sWakeLockType != Unsupported) {
+ sWakeLockType++;
+ if (IsWakeLockTypeAvailable(sWakeLockType)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+WakeLockListener::WakeLockListener() = default;
+
+WakeLockListener::~WakeLockListener() {
+ for (const auto& topic : mTopics.Values()) {
+ topic->Shutdown();
+ }
+}
+
+nsresult WakeLockListener::Callback(const nsAString& topic,
+ const nsAString& state) {
+ if (!topic.Equals(u"screen"_ns) && !topic.Equals(u"video-playing"_ns) &&
+ !topic.Equals(u"autoscroll"_ns)) {
+ return NS_OK;
+ }
+
+ RefPtr<WakeLockTopic> topicLock = mTopics.LookupOrInsertWith(
+ topic, [&] { return MakeRefPtr<WakeLockTopic>(topic); });
+
+ // Treat "locked-background" the same as "unlocked" on desktop linux.
+ bool shouldLock = state.EqualsLiteral("locked-foreground");
+ WAKE_LOCK_LOG("WakeLockListener topic %s state %s request lock %d",
+ NS_ConvertUTF16toUTF8(topic).get(),
+ NS_ConvertUTF16toUTF8(state).get(), shouldLock);
+
+ return shouldLock ? topicLock->InhibitScreensaver()
+ : topicLock->UninhibitScreensaver();
+}