From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- widget/gtk/WakeLockListener.cpp | 365 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 365 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 000000000..54bad17ff --- /dev/null +++ b/widget/gtk/WakeLockListener.cpp @@ -0,0 +1,365 @@ +/* -*- 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 "mozilla/ipc/DBusMessageRefPtr.h" +#include "mozilla/ipc/DBusPendingCallRefPtr.h" + +#define FREEDESKTOP_SCREENSAVER_TARGET "org.freedesktop.ScreenSaver" +#define FREEDESKTOP_SCREENSAVER_OBJECT "/ScreenSaver" +#define FREEDESKTOP_SCREENSAVER_INTERFACE "org.freedesktop.ScreenSaver" + +#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; + +NS_IMPL_ISUPPORTS(WakeLockListener, nsIDOMMozWakeLockListener) + +WakeLockListener* WakeLockListener::sSingleton = nullptr; + + +enum DesktopEnvironment { + FreeDesktop, + GNOME, + Unsupported, +}; + +class WakeLockTopic +{ +public: + WakeLockTopic(const nsAString& aTopic, DBusConnection* aConnection) + : mTopic(NS_ConvertUTF16toUTF8(aTopic)) + , mConnection(aConnection) + , mDesktopEnvironment(FreeDesktop) + , mInhibitRequest(0) + , mShouldInhibit(false) + , mWaitingForReply(false) + { + } + + nsresult InhibitScreensaver(void); + nsresult UninhibitScreensaver(void); + +private: + bool SendInhibit(); + bool SendUninhibit(); + + bool SendFreeDesktopInhibitMessage(); + bool SendGNOMEInhibitMessage(); + bool SendMessage(DBusMessage* aMessage); + + static void ReceiveInhibitReply(DBusPendingCall* aPending, void* aUserData); + void InhibitFailed(); + void InhibitSucceeded(uint32_t aInhibitRequest); + + nsCString mTopic; + RefPtr mConnection; + + DesktopEnvironment 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::SendFreeDesktopInhibitMessage() +{ + 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); +} + + +bool +WakeLockTopic::SendInhibit() +{ + bool sendOk = false; + + switch (mDesktopEnvironment) + { + case FreeDesktop: + sendOk = SendFreeDesktopInhibitMessage(); + break; + case GNOME: + sendOk = SendGNOMEInhibitMessage(); + break; + case Unsupported: + return false; + } + + if (sendOk) { + mWaitingForReply = true; + } + + return sendOk; +} + +bool +WakeLockTopic::SendUninhibit() +{ + RefPtr message; + + if (mDesktopEnvironment == FreeDesktop) { + message = already_AddRefed( + dbus_message_new_method_call(FREEDESKTOP_SCREENSAVER_TARGET, + FREEDESKTOP_SCREENSAVER_OBJECT, + FREEDESKTOP_SCREENSAVER_INTERFACE, + "UnInhibit")); + } else if (mDesktopEnvironment == GNOME) { + message = already_AddRefed( + dbus_message_new_method_call(SESSION_MANAGER_TARGET, + SESSION_MANAGER_OBJECT, + SESSION_MANAGER_INTERFACE, + "Uninhibit")); + } + + 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 == FreeDesktop) { + mDesktopEnvironment = GNOME; + } else { + NS_ASSERTION(mDesktopEnvironment == GNOME, "Unknown desktop environment"); + 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(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); + } +} + +/* static */ WakeLockListener* +WakeLockListener::GetSingleton(bool aCreate) +{ + if (!sSingleton && aCreate) { + sSingleton = new WakeLockListener(); + sSingleton->AddRef(); + } + + return sSingleton; +} + +/* static */ void +WakeLockListener::Shutdown() +{ + sSingleton->Release(); + sSingleton = nullptr; +} + +nsresult +WakeLockListener::Callback(const nsAString& topic, const nsAString& state) +{ + if (!mConnection) { + return NS_ERROR_FAILURE; + } + + if(!topic.Equals(NS_LITERAL_STRING("screen"))) + return NS_OK; + + WakeLockTopic* topicLock = mTopics.Get(topic); + if (!topicLock) { + topicLock = new WakeLockTopic(topic, mConnection); + mTopics.Put(topic, topicLock); + } + + // Treat "locked-background" the same as "unlocked" on desktop linux. + bool shouldLock = state.EqualsLiteral("locked-foreground"); + + return shouldLock ? + topicLock->InhibitScreensaver() : + topicLock->UninhibitScreensaver(); +} + +#endif -- cgit v1.2.3