summaryrefslogtreecommitdiffstats
path: root/widget/gtk/nsUserIdleServiceGTK.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/gtk/nsUserIdleServiceGTK.cpp')
-rw-r--r--widget/gtk/nsUserIdleServiceGTK.cpp317
1 files changed, 317 insertions, 0 deletions
diff --git a/widget/gtk/nsUserIdleServiceGTK.cpp b/widget/gtk/nsUserIdleServiceGTK.cpp
new file mode 100644
index 0000000000..4cdc5ba13d
--- /dev/null
+++ b/widget/gtk/nsUserIdleServiceGTK.cpp
@@ -0,0 +1,317 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* 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 <gtk/gtk.h>
+
+#include "nsUserIdleServiceGTK.h"
+#include "nsDebug.h"
+#include "prlink.h"
+#include "mozilla/Logging.h"
+#include "WidgetUtilsGtk.h"
+#ifdef MOZ_X11
+# include <X11/Xlib.h>
+# include <X11/Xutil.h>
+# include <gdk/gdkx.h>
+#endif
+#ifdef MOZ_ENABLE_DBUS
+# include <gio/gio.h>
+# include "AsyncDBus.h"
+# include "WakeLockListener.h"
+# include "nsIObserverService.h"
+# include "mozilla/UniquePtrExtensions.h"
+#endif
+
+using mozilla::LogLevel;
+static mozilla::LazyLogModule sIdleLog("nsIUserIdleService");
+
+using namespace mozilla;
+using namespace mozilla::widget;
+
+#ifdef MOZ_X11
+typedef struct {
+ Window window; // Screen saver window
+ int state; // ScreenSaver(Off,On,Disabled)
+ int kind; // ScreenSaver(Blanked,Internal,External)
+ unsigned long til_or_since; // milliseconds since/til screensaver kicks in
+ unsigned long idle; // milliseconds idle
+ unsigned long event_mask; // event stuff
+} XScreenSaverInfo;
+
+typedef bool (*_XScreenSaverQueryExtension_fn)(Display* dpy, int* event_base,
+ int* error_base);
+typedef XScreenSaverInfo* (*_XScreenSaverAllocInfo_fn)(void);
+typedef void (*_XScreenSaverQueryInfo_fn)(Display* dpy, Drawable drw,
+ XScreenSaverInfo* info);
+
+class UserIdleServiceX11 : public UserIdleServiceImpl {
+ public:
+ bool PollIdleTime(uint32_t* aIdleTime) override {
+ // Ask xscreensaver about idle time:
+ *aIdleTime = 0;
+
+ // We might not have a display (cf. in xpcshell)
+ Display* dplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+ if (!dplay) {
+ MOZ_LOG(sIdleLog, LogLevel::Warning, ("No display found!\n"));
+ return false;
+ }
+
+ int event_base, error_base;
+ if (mXSSQueryExtension(dplay, &event_base, &error_base)) {
+ if (!mXssInfo) mXssInfo = mXSSAllocInfo();
+ if (!mXssInfo) return false;
+ mXSSQueryInfo(dplay, GDK_ROOT_WINDOW(), mXssInfo);
+ *aIdleTime = mXssInfo->idle;
+ MOZ_LOG(sIdleLog, LogLevel::Info,
+ ("UserIdleServiceX11::PollIdleTime() %d\n", *aIdleTime));
+ return true;
+ }
+ // If we get here, we couldn't get to XScreenSaver:
+ MOZ_LOG(sIdleLog, LogLevel::Warning,
+ ("XSSQueryExtension returned false!\n"));
+ return false;
+ }
+
+ bool ProbeImplementation() override {
+ MOZ_LOG(sIdleLog, LogLevel::Info,
+ ("UserIdleServiceX11::UserIdleServiceX11()\n"));
+
+ if (!mozilla::widget::GdkIsX11Display()) {
+ return false;
+ }
+
+ // This will leak - See comments in ~UserIdleServiceX11().
+ PRLibrary* xsslib = PR_LoadLibrary("libXss.so.1");
+ if (!xsslib) // ouch.
+ {
+ MOZ_LOG(sIdleLog, LogLevel::Warning, ("Failed to find libXss.so!\n"));
+ return false;
+ }
+
+ mXSSQueryExtension = (_XScreenSaverQueryExtension_fn)PR_FindFunctionSymbol(
+ xsslib, "XScreenSaverQueryExtension");
+ mXSSAllocInfo = (_XScreenSaverAllocInfo_fn)PR_FindFunctionSymbol(
+ xsslib, "XScreenSaverAllocInfo");
+ mXSSQueryInfo = (_XScreenSaverQueryInfo_fn)PR_FindFunctionSymbol(
+ xsslib, "XScreenSaverQueryInfo");
+
+ if (!mXSSQueryExtension) {
+ MOZ_LOG(sIdleLog, LogLevel::Warning,
+ ("Failed to get XSSQueryExtension!\n"));
+ }
+ if (!mXSSAllocInfo) {
+ MOZ_LOG(sIdleLog, LogLevel::Warning, ("Failed to get XSSAllocInfo!\n"));
+ }
+ if (!mXSSQueryInfo) {
+ MOZ_LOG(sIdleLog, LogLevel::Warning, ("Failed to get XSSQueryInfo!\n"));
+ }
+ if (!mXSSQueryExtension || !mXSSAllocInfo || !mXSSQueryInfo) {
+ // We're missing X11 symbols
+ return false;
+ }
+
+ // UserIdleServiceX11 uses sync init, confirm it now.
+ mUserIdleServiceGTK->AcceptServiceCallback();
+ return true;
+ }
+
+ explicit UserIdleServiceX11(nsUserIdleServiceGTK* aUserIdleService)
+ : UserIdleServiceImpl(aUserIdleService){};
+
+ ~UserIdleServiceX11() {
+# ifdef MOZ_X11
+ if (mXssInfo) {
+ XFree(mXssInfo);
+ }
+# endif
+
+// It is not safe to unload libXScrnSaver until each display is closed because
+// the library registers callbacks through XESetCloseDisplay (Bug 397607).
+// (Also the library and its functions are scoped for the file not the object.)
+# if 0
+ if (xsslib) {
+ PR_UnloadLibrary(xsslib);
+ xsslib = nullptr;
+ }
+# endif
+ }
+
+ private:
+ XScreenSaverInfo* mXssInfo = nullptr;
+ _XScreenSaverQueryExtension_fn mXSSQueryExtension = nullptr;
+ _XScreenSaverAllocInfo_fn mXSSAllocInfo = nullptr;
+ _XScreenSaverQueryInfo_fn mXSSQueryInfo = nullptr;
+};
+#endif
+
+#ifdef MOZ_ENABLE_DBUS
+class UserIdleServiceMutter : public UserIdleServiceImpl {
+ public:
+ bool PollIdleTime(uint32_t* aIdleTime) override {
+ MOZ_LOG(sIdleLog, LogLevel::Info, ("PollIdleTime() request\n"));
+
+ // We're not ready yet
+ if (!mProxy) {
+ return false;
+ }
+
+ if (!mPollInProgress) {
+ mPollInProgress = true;
+ DBusProxyCall(mProxy, "GetIdletime", nullptr, G_DBUS_CALL_FLAGS_NONE, -1,
+ mCancellable)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ // It's safe to capture this as we use mCancellable to stop
+ // listening.
+ [this](RefPtr<GVariant>&& aResult) {
+ if (!g_variant_is_of_type(aResult, G_VARIANT_TYPE_TUPLE) ||
+ g_variant_n_children(aResult) != 1) {
+ MOZ_LOG(sIdleLog, LogLevel::Info,
+ ("PollIdleTime() Unexpected params type: %s\n",
+ g_variant_get_type_string(aResult)));
+ mLastIdleTime = 0;
+ return;
+ }
+ RefPtr<GVariant> iTime =
+ dont_AddRef(g_variant_get_child_value(aResult, 0));
+ if (!g_variant_is_of_type(iTime, G_VARIANT_TYPE_UINT64)) {
+ MOZ_LOG(sIdleLog, LogLevel::Info,
+ ("PollIdleTime() Unexpected params type: %s\n",
+ g_variant_get_type_string(aResult)));
+ mLastIdleTime = 0;
+ return;
+ }
+ uint64_t idleTime = g_variant_get_uint64(iTime);
+ if (idleTime > std::numeric_limits<uint32_t>::max()) {
+ idleTime = std::numeric_limits<uint32_t>::max();
+ }
+ mLastIdleTime = idleTime;
+ mPollInProgress = false;
+ MOZ_LOG(sIdleLog, LogLevel::Info,
+ ("Async handler got %d\n", mLastIdleTime));
+ },
+ [this](GUniquePtr<GError>&& aError) {
+ mPollInProgress = false;
+ if (!IsCancelledGError(aError.get())) {
+ MOZ_LOG(
+ sIdleLog, LogLevel::Warning,
+ ("Failed to call GetIdletime(): %s\n", aError->message));
+ mUserIdleServiceGTK->RejectAndTryNextServiceCallback();
+ }
+ });
+ }
+
+ *aIdleTime = mLastIdleTime;
+ MOZ_LOG(sIdleLog, LogLevel::Info,
+ ("PollIdleTime() returns %d\n", *aIdleTime));
+ return true;
+ }
+
+ bool ProbeImplementation() override {
+ MOZ_LOG(sIdleLog, LogLevel::Info,
+ ("UserIdleServiceMutter::UserIdleServiceMutter()\n"));
+
+ mCancellable = dont_AddRef(g_cancellable_new());
+ CreateDBusProxyForBus(
+ G_BUS_TYPE_SESSION,
+ GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES),
+ nullptr, "org.gnome.Mutter.IdleMonitor",
+ "/org/gnome/Mutter/IdleMonitor/Core", "org.gnome.Mutter.IdleMonitor",
+ mCancellable)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [this](RefPtr<GDBusProxy>&& aProxy) {
+ mProxy = std::move(aProxy);
+ mUserIdleServiceGTK->AcceptServiceCallback();
+ },
+ [this](GUniquePtr<GError>&& aError) {
+ if (!IsCancelledGError(aError.get())) {
+ mUserIdleServiceGTK->RejectAndTryNextServiceCallback();
+ }
+ });
+ return true;
+ }
+
+ explicit UserIdleServiceMutter(nsUserIdleServiceGTK* aUserIdleService)
+ : UserIdleServiceImpl(aUserIdleService){};
+
+ ~UserIdleServiceMutter() {
+ if (mCancellable) {
+ g_cancellable_cancel(mCancellable);
+ mCancellable = nullptr;
+ }
+ mProxy = nullptr;
+ }
+
+ private:
+ RefPtr<GDBusProxy> mProxy;
+ RefPtr<GCancellable> mCancellable;
+ uint32_t mLastIdleTime = 0;
+ bool mPollInProgress = false;
+};
+#endif
+
+void nsUserIdleServiceGTK::ProbeService() {
+ MOZ_LOG(sIdleLog, LogLevel::Info,
+ ("nsUserIdleServiceGTK::ProbeService() mIdleServiceType %d\n",
+ mIdleServiceType));
+ MOZ_ASSERT(!mIdleService);
+
+ switch (mIdleServiceType) {
+#ifdef MOZ_ENABLE_DBUS
+ case IDLE_SERVICE_MUTTER:
+ mIdleService = MakeUnique<UserIdleServiceMutter>(this);
+ break;
+#endif
+#ifdef MOZ_X11
+ case IDLE_SERVICE_XSCREENSAVER:
+ mIdleService = MakeUnique<UserIdleServiceX11>(this);
+ break;
+#endif
+ default:
+ return;
+ }
+
+ if (!mIdleService->ProbeImplementation()) {
+ RejectAndTryNextServiceCallback();
+ }
+}
+
+void nsUserIdleServiceGTK::AcceptServiceCallback() {
+ MOZ_LOG(sIdleLog, LogLevel::Info,
+ ("nsUserIdleServiceGTK::AcceptServiceCallback() type %d\n",
+ mIdleServiceType));
+ mIdleServiceInitialized = true;
+}
+
+void nsUserIdleServiceGTK::RejectAndTryNextServiceCallback() {
+ MOZ_LOG(sIdleLog, LogLevel::Info,
+ ("nsUserIdleServiceGTK::RejectAndTryNextServiceCallback() type %d\n",
+ mIdleServiceType));
+
+ // Delete recent non-working service
+ MOZ_ASSERT(mIdleService, "Nothing to reject?");
+ mIdleService = nullptr;
+ mIdleServiceInitialized = false;
+
+ mIdleServiceType++;
+ if (mIdleServiceType < IDLE_SERVICE_NONE) {
+ MOZ_LOG(sIdleLog, LogLevel::Info,
+ ("nsUserIdleServiceGTK try next idle service\n"));
+ ProbeService();
+ } else {
+ MOZ_LOG(sIdleLog, LogLevel::Info, ("nsUserIdleServiceGTK failed\n"));
+ }
+}
+
+bool nsUserIdleServiceGTK::PollIdleTime(uint32_t* aIdleTime) {
+ if (!mIdleServiceInitialized) {
+ return false;
+ }
+ return mIdleService->PollIdleTime(aIdleTime);
+}