From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- widget/gtk/WakeLockListener.cpp | 546 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 546 insertions(+) create mode 100644 widget/gtk/WakeLockListener.cpp (limited to 'widget/gtk/WakeLockListener.cpp') diff --git a/widget/gtk/WakeLockListener.cpp b/widget/gtk/WakeLockListener.cpp new file mode 100644 index 0000000000..e3517c5437 --- /dev/null +++ b/widget/gtk/WakeLockListener.cpp @@ -0,0 +1,546 @@ +/* -*- 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/. */ + +#ifdef MOZ_ENABLE_DBUS + +# include "WakeLockListener.h" + +# include +# include + +# include "WidgetUtilsGtk.h" + +# if defined(MOZ_X11) +# include "prlink.h" +# include +# include +# include "X11UndefineNone.h" +# endif + +# if defined(MOZ_WAYLAND) +# include "mozilla/widget/nsWaylandDisplay.h" +# include "nsWindow.h" +# include "mozilla/dom/power/PowerManagerService.h" +# endif + +# 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) + +using namespace mozilla; +using namespace mozilla::widget; + +NS_IMPL_ISUPPORTS(WakeLockListener, nsIDOMMozWakeLockListener) + +StaticRefPtr WakeLockListener::sSingleton; + +# define WAKE_LOCK_LOG(...) \ + MOZ_LOG(gLinuxWakeLockLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) +static mozilla::LazyLogModule gLinuxWakeLockLog("LinuxWakeLock"); + +enum WakeLockDesktopEnvironment { + FreeDesktopScreensaver, + FreeDesktopPower, + GNOME, +# if defined(MOZ_X11) + XScreenSaver, +# endif +# if defined(MOZ_WAYLAND) + WaylandIdleInhibit, +# endif + Unsupported, +}; + +class WakeLockTopic { + public: + WakeLockTopic(const nsAString& aTopic, DBusConnection* aConnection) + : +# if defined(MOZ_WAYLAND) + mWaylandInhibitor(nullptr), +# endif + mTopic(NS_ConvertUTF16toUTF8(aTopic)), + mConnection(aConnection), + mDesktopEnvironment(FreeDesktopScreensaver), + mInhibitRequest(0), + mShouldInhibit(false), + mWaitingForReply(false) { + } + + nsresult InhibitScreensaver(void); + nsresult UninhibitScreensaver(void); + + private: + bool SendInhibit(); + bool SendUninhibit(); + + bool SendFreeDesktopPowerInhibitMessage(); + bool SendFreeDesktopScreensaverInhibitMessage(); + bool SendGNOMEInhibitMessage(); + bool SendMessage(DBusMessage* aMessage); + +# if defined(MOZ_X11) + static bool CheckXScreenSaverSupport(); + static bool InhibitXScreenSaver(bool inhibit); +# endif + +# if defined(MOZ_WAYLAND) + zwp_idle_inhibitor_v1* mWaylandInhibitor; + static bool CheckWaylandIdleInhibitSupport(); + bool InhibitWaylandIdle(); + bool UninhibitWaylandIdle(); +# endif + + static void ReceiveInhibitReply(DBusPendingCall* aPending, void* aUserData); + void InhibitFailed(); + void InhibitSucceeded(uint32_t aInhibitRequest); + + nsCString mTopic; + RefPtr mConnection; + + WakeLockDesktopEnvironment mDesktopEnvironment; + + uint32_t mInhibitRequest; + + bool mShouldInhibit; + bool mWaitingForReply; +}; + +bool WakeLockTopic::SendMessage(DBusMessage* aMessage) { + // send message and get a handle for a reply + RefPtr reply; + dbus_connection_send_with_reply(mConnection, aMessage, + reply.StartAssignment(), DBUS_TIMEOUT); + if (!reply) { + return false; + } + + dbus_pending_call_set_notify(reply, &ReceiveInhibitReply, this, NULL); + + return true; +} + +bool WakeLockTopic::SendFreeDesktopPowerInhibitMessage() { + RefPtr message = + already_AddRefed(dbus_message_new_method_call( + FREEDESKTOP_POWER_TARGET, FREEDESKTOP_POWER_OBJECT, + FREEDESKTOP_POWER_INTERFACE, "Inhibit")); + + if (!message) { + return false; + } + + const char* app = g_get_prgname(); + const char* topic = mTopic.get(); + dbus_message_append_args(message, DBUS_TYPE_STRING, &app, DBUS_TYPE_STRING, + &topic, DBUS_TYPE_INVALID); + + return SendMessage(message); +} + +bool WakeLockTopic::SendFreeDesktopScreensaverInhibitMessage() { + RefPtr message = + already_AddRefed(dbus_message_new_method_call( + FREEDESKTOP_SCREENSAVER_TARGET, FREEDESKTOP_SCREENSAVER_OBJECT, + FREEDESKTOP_SCREENSAVER_INTERFACE, "Inhibit")); + + if (!message) { + return false; + } + + const char* app = g_get_prgname(); + const char* topic = mTopic.get(); + dbus_message_append_args(message, DBUS_TYPE_STRING, &app, DBUS_TYPE_STRING, + &topic, DBUS_TYPE_INVALID); + + return SendMessage(message); +} + +bool WakeLockTopic::SendGNOMEInhibitMessage() { + RefPtr message = + already_AddRefed(dbus_message_new_method_call( + SESSION_MANAGER_TARGET, SESSION_MANAGER_OBJECT, + SESSION_MANAGER_INTERFACE, "Inhibit")); + + if (!message) { + return false; + } + + static const uint32_t xid = 0; + static const uint32_t flags = (1 << 3); // Inhibit idle + const char* app = g_get_prgname(); + const char* topic = mTopic.get(); + dbus_message_append_args(message, DBUS_TYPE_STRING, &app, DBUS_TYPE_UINT32, + &xid, DBUS_TYPE_STRING, &topic, DBUS_TYPE_UINT32, + &flags, DBUS_TYPE_INVALID); + + return SendMessage(message); +} + +# if defined(MOZ_X11) + +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; + + return true; +} + +/* static */ +bool WakeLockTopic::InhibitXScreenSaver(bool 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); + return true; +} + +# endif + +# if defined(MOZ_WAYLAND) + +/* static */ +bool WakeLockTopic::CheckWaylandIdleInhibitSupport() { + nsWaylandDisplay* waylandDisplay = WaylandDisplayGet(); + return waylandDisplay && waylandDisplay->GetIdleInhibitManager() != nullptr; +} + +bool WakeLockTopic::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); + } + return true; +} + +bool WakeLockTopic::UninhibitWaylandIdle() { + if (mWaylandInhibitor == nullptr) return false; + + zwp_idle_inhibitor_v1_destroy(mWaylandInhibitor); + mWaylandInhibitor = nullptr; + + return true; +} + +# endif + +bool WakeLockTopic::SendInhibit() { + bool sendOk = false; + + switch (mDesktopEnvironment) { + case FreeDesktopScreensaver: + WAKE_LOCK_LOG("SendInhibit(): FreeDesktopScreensaver"); + sendOk = SendFreeDesktopScreensaverInhibitMessage(); + break; + case FreeDesktopPower: + WAKE_LOCK_LOG("SendInhibit(): FreeDesktopPower"); + sendOk = SendFreeDesktopPowerInhibitMessage(); + break; + case GNOME: + WAKE_LOCK_LOG("SendInhibit(): GNOME"); + sendOk = SendGNOMEInhibitMessage(); + break; +# if defined(MOZ_X11) + case XScreenSaver: + WAKE_LOCK_LOG("SendInhibit(): InhibitXScreenSaver"); + return InhibitXScreenSaver(true); +# endif +# if defined(MOZ_WAYLAND) + case WaylandIdleInhibit: + WAKE_LOCK_LOG("SendInhibit(): WaylandIdleInhibit"); + return InhibitWaylandIdle(); +# endif + case Unsupported: + return false; + } + + if (sendOk) { + mWaitingForReply = true; + } + + return sendOk; +} + +bool WakeLockTopic::SendUninhibit() { + RefPtr message; + + if (mDesktopEnvironment == FreeDesktopScreensaver) { + WAKE_LOCK_LOG("SendUninhibit(): FreeDesktopScreensaver"); + message = already_AddRefed(dbus_message_new_method_call( + FREEDESKTOP_SCREENSAVER_TARGET, FREEDESKTOP_SCREENSAVER_OBJECT, + FREEDESKTOP_SCREENSAVER_INTERFACE, "UnInhibit")); + } else if (mDesktopEnvironment == FreeDesktopPower) { + WAKE_LOCK_LOG("SendUninhibit(): FreeDesktopPower"); + message = already_AddRefed(dbus_message_new_method_call( + FREEDESKTOP_POWER_TARGET, FREEDESKTOP_POWER_OBJECT, + FREEDESKTOP_POWER_INTERFACE, "UnInhibit")); + } else if (mDesktopEnvironment == GNOME) { + WAKE_LOCK_LOG("SendUninhibit(): GNOME"); + message = already_AddRefed(dbus_message_new_method_call( + SESSION_MANAGER_TARGET, SESSION_MANAGER_OBJECT, + SESSION_MANAGER_INTERFACE, "Uninhibit")); + } +# if defined(MOZ_X11) + else if (mDesktopEnvironment == XScreenSaver) { + WAKE_LOCK_LOG("SendUninhibit(): XScreenSaver"); + return InhibitXScreenSaver(false); + } +# endif +# if defined(MOZ_WAYLAND) + else if (mDesktopEnvironment == WaylandIdleInhibit) { + WAKE_LOCK_LOG("SendUninhibit(): Wayland"); + return UninhibitWaylandIdle(); + } +# endif + + if (!message) { + return false; + } + + dbus_message_append_args(message, DBUS_TYPE_UINT32, &mInhibitRequest, + DBUS_TYPE_INVALID); + + dbus_connection_send(mConnection, message, nullptr); + dbus_connection_flush(mConnection); + + mInhibitRequest = 0; + + return true; +} + +nsresult WakeLockTopic::InhibitScreensaver() { + if (mShouldInhibit) { + // Screensaver is inhibited. Nothing to do here. + return NS_OK; + } + + mShouldInhibit = true; + + if (mWaitingForReply) { + // We already have a screensaver inhibit request pending. This can happen + // if InhibitScreensaver is called, then UninhibitScreensaver, then + // InhibitScreensaver again quickly. + return NS_OK; + } + + return SendInhibit() ? NS_OK : NS_ERROR_FAILURE; +} + +nsresult WakeLockTopic::UninhibitScreensaver() { + if (!mShouldInhibit) { + // Screensaver isn't inhibited. Nothing to do here. + return NS_OK; + } + + mShouldInhibit = false; + + if (mWaitingForReply) { + // If we're still waiting for a response to our inhibit request, we can't + // do anything until we get a dbus message back. The callbacks below will + // check |mShouldInhibit| and act accordingly. + return NS_OK; + } + + return SendUninhibit() ? NS_OK : NS_ERROR_FAILURE; +} + +void WakeLockTopic::InhibitFailed() { + mWaitingForReply = false; + + if (mDesktopEnvironment == FreeDesktopScreensaver) { + mDesktopEnvironment = GNOME; + } else if (mDesktopEnvironment == GNOME) { + mDesktopEnvironment = FreeDesktopPower; +# if defined(MOZ_X11) + } else if (mDesktopEnvironment == FreeDesktopPower && + CheckXScreenSaverSupport()) { + mDesktopEnvironment = XScreenSaver; +# endif +# if defined(MOZ_WAYLAND) + } else if (mDesktopEnvironment == FreeDesktopPower && + CheckWaylandIdleInhibitSupport()) { + mDesktopEnvironment = WaylandIdleInhibit; +# endif + } else { + mDesktopEnvironment = Unsupported; + mShouldInhibit = false; + } + + if (!mShouldInhibit) { + // We were interrupted by UninhibitScreensaver() before we could find the + // correct desktop environment. + return; + } + + SendInhibit(); +} + +void WakeLockTopic::InhibitSucceeded(uint32_t aInhibitRequest) { + mWaitingForReply = false; + mInhibitRequest = aInhibitRequest; + + if (!mShouldInhibit) { + // We successfully inhibited the screensaver, but UninhibitScreensaver() + // was called while we were waiting for a reply. + SendUninhibit(); + } +} + +/* static */ +void WakeLockTopic::ReceiveInhibitReply(DBusPendingCall* pending, + void* user_data) { + if (!WakeLockListener::GetSingleton(false)) { + // The WakeLockListener (and therefore our topic) was deleted while we were + // waiting for a reply. + return; + } + + WakeLockTopic* self = static_cast(user_data); + + RefPtr msg = + already_AddRefed(dbus_pending_call_steal_reply(pending)); + if (!msg) { + return; + } + + if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_RETURN) { + uint32_t inhibitRequest; + + if (dbus_message_get_args(msg, nullptr, DBUS_TYPE_UINT32, &inhibitRequest, + DBUS_TYPE_INVALID)) { + self->InhibitSucceeded(inhibitRequest); + } + } else { + self->InhibitFailed(); + } +} + +WakeLockListener::WakeLockListener() : mConnection(nullptr) {} + +/* static */ +WakeLockListener* WakeLockListener::GetSingleton(bool aCreate) { + if (!sSingleton && aCreate) { + sSingleton = new WakeLockListener(); + } + + return sSingleton; +} + +/* static */ +void WakeLockListener::Shutdown() { sSingleton = nullptr; } + +bool WakeLockListener::EnsureDBusConnection() { + if (!mConnection) { + mConnection = already_AddRefed( + dbus_bus_get(DBUS_BUS_SESSION, nullptr)); + + if (mConnection) { + dbus_connection_set_exit_on_disconnect(mConnection, false); + dbus_connection_setup_with_g_main(mConnection, nullptr); + } + } + + return mConnection != nullptr; +} + +nsresult WakeLockListener::Callback(const nsAString& topic, + const nsAString& state) { + if (!EnsureDBusConnection()) { + return NS_ERROR_FAILURE; + } + + WAKE_LOCK_LOG("WakeLockListener %s state %s", + NS_ConvertUTF16toUTF8(topic).get(), + NS_ConvertUTF16toUTF8(state).get()); + + if (!topic.Equals(u"screen"_ns) && !topic.Equals(u"video-playing"_ns) && + !topic.Equals(u"autoscroll"_ns)) + return NS_OK; + + WakeLockTopic* const topicLock = + mTopics.GetOrInsertNew(topic, topic, mConnection); + + // Treat "locked-background" the same as "unlocked" on desktop linux. + bool shouldLock = state.EqualsLiteral("locked-foreground"); + WAKE_LOCK_LOG("shouldLock %d", shouldLock); + + return shouldLock ? topicLock->InhibitScreensaver() + : topicLock->UninhibitScreensaver(); +} + +#endif -- cgit v1.2.3