diff options
Diffstat (limited to 'widget/gtk/nsUserIdleServiceGTK.cpp')
-rw-r--r-- | widget/gtk/nsUserIdleServiceGTK.cpp | 317 |
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); +} |