summaryrefslogtreecommitdiffstats
path: root/widget/gtk/WakeLockListener.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /widget/gtk/WakeLockListener.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'widget/gtk/WakeLockListener.cpp')
-rw-r--r--widget/gtk/WakeLockListener.cpp365
1 files changed, 365 insertions, 0 deletions
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 <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#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<DBusConnection> 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<DBusPendingCall> 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<DBusMessage> message = already_AddRefed<DBusMessage>(
+ 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<DBusMessage> message = already_AddRefed<DBusMessage>(
+ 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<DBusMessage> message;
+
+ if (mDesktopEnvironment == FreeDesktop) {
+ message = already_AddRefed<DBusMessage>(
+ dbus_message_new_method_call(FREEDESKTOP_SCREENSAVER_TARGET,
+ FREEDESKTOP_SCREENSAVER_OBJECT,
+ FREEDESKTOP_SCREENSAVER_INTERFACE,
+ "UnInhibit"));
+ } else if (mDesktopEnvironment == GNOME) {
+ message = already_AddRefed<DBusMessage>(
+ 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<WakeLockTopic*>(user_data);
+
+ RefPtr<DBusMessage> msg = already_AddRefed<DBusMessage>(
+ 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<DBusConnection>(
+ 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