1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=4:tabstop=4:
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
10 #include "nsUserIdleServiceGTK.h"
13 #include "mozilla/Logging.h"
14 #include "WidgetUtilsGtk.h"
16 # include <X11/Xlib.h>
17 # include <X11/Xutil.h>
18 # include <gdk/gdkx.h>
20 #ifdef MOZ_ENABLE_DBUS
22 # include "AsyncDBus.h"
23 # include "WakeLockListener.h"
24 # include "nsIObserverService.h"
25 # include "mozilla/UniquePtrExtensions.h"
28 using mozilla::LogLevel
;
29 static mozilla::LazyLogModule
sIdleLog("nsIUserIdleService");
31 using namespace mozilla
;
32 using namespace mozilla::widget
;
36 Window window
; // Screen saver window
37 int state
; // ScreenSaver(Off,On,Disabled)
38 int kind
; // ScreenSaver(Blanked,Internal,External)
39 unsigned long til_or_since
; // milliseconds since/til screensaver kicks in
40 unsigned long idle
; // milliseconds idle
41 unsigned long event_mask
; // event stuff
44 typedef bool (*_XScreenSaverQueryExtension_fn
)(Display
* dpy
, int* event_base
,
46 typedef XScreenSaverInfo
* (*_XScreenSaverAllocInfo_fn
)(void);
47 typedef void (*_XScreenSaverQueryInfo_fn
)(Display
* dpy
, Drawable drw
,
48 XScreenSaverInfo
* info
);
50 class UserIdleServiceX11
: public UserIdleServiceImpl
{
52 bool PollIdleTime(uint32_t* aIdleTime
) override
{
53 // Ask xscreensaver about idle time:
56 // We might not have a display (cf. in xpcshell)
57 Display
* dplay
= GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
59 MOZ_LOG(sIdleLog
, LogLevel::Warning
, ("No display found!\n"));
63 int event_base
, error_base
;
64 if (mXSSQueryExtension(dplay
, &event_base
, &error_base
)) {
65 if (!mXssInfo
) mXssInfo
= mXSSAllocInfo();
66 if (!mXssInfo
) return false;
67 mXSSQueryInfo(dplay
, GDK_ROOT_WINDOW(), mXssInfo
);
68 *aIdleTime
= mXssInfo
->idle
;
69 MOZ_LOG(sIdleLog
, LogLevel::Info
,
70 ("UserIdleServiceX11::PollIdleTime() %d\n", *aIdleTime
));
73 // If we get here, we couldn't get to XScreenSaver:
74 MOZ_LOG(sIdleLog
, LogLevel::Warning
,
75 ("XSSQueryExtension returned false!\n"));
79 bool ProbeImplementation() override
{
80 MOZ_LOG(sIdleLog
, LogLevel::Info
,
81 ("UserIdleServiceX11::UserIdleServiceX11()\n"));
83 if (!mozilla::widget::GdkIsX11Display()) {
87 // This will leak - See comments in ~UserIdleServiceX11().
88 PRLibrary
* xsslib
= PR_LoadLibrary("libXss.so.1");
91 MOZ_LOG(sIdleLog
, LogLevel::Warning
, ("Failed to find libXss.so!\n"));
95 mXSSQueryExtension
= (_XScreenSaverQueryExtension_fn
)PR_FindFunctionSymbol(
96 xsslib
, "XScreenSaverQueryExtension");
97 mXSSAllocInfo
= (_XScreenSaverAllocInfo_fn
)PR_FindFunctionSymbol(
98 xsslib
, "XScreenSaverAllocInfo");
99 mXSSQueryInfo
= (_XScreenSaverQueryInfo_fn
)PR_FindFunctionSymbol(
100 xsslib
, "XScreenSaverQueryInfo");
102 if (!mXSSQueryExtension
) {
103 MOZ_LOG(sIdleLog
, LogLevel::Warning
,
104 ("Failed to get XSSQueryExtension!\n"));
106 if (!mXSSAllocInfo
) {
107 MOZ_LOG(sIdleLog
, LogLevel::Warning
, ("Failed to get XSSAllocInfo!\n"));
109 if (!mXSSQueryInfo
) {
110 MOZ_LOG(sIdleLog
, LogLevel::Warning
, ("Failed to get XSSQueryInfo!\n"));
112 if (!mXSSQueryExtension
|| !mXSSAllocInfo
|| !mXSSQueryInfo
) {
113 // We're missing X11 symbols
117 // UserIdleServiceX11 uses sync init, confirm it now.
118 mUserIdleServiceGTK
->AcceptServiceCallback();
122 explicit UserIdleServiceX11(nsUserIdleServiceGTK
* aUserIdleService
)
123 : UserIdleServiceImpl(aUserIdleService
){};
125 ~UserIdleServiceX11() {
132 // It is not safe to unload libXScrnSaver until each display is closed because
133 // the library registers callbacks through XESetCloseDisplay (Bug 397607).
134 // (Also the library and its functions are scoped for the file not the object.)
137 PR_UnloadLibrary(xsslib
);
144 XScreenSaverInfo
* mXssInfo
= nullptr;
145 _XScreenSaverQueryExtension_fn mXSSQueryExtension
= nullptr;
146 _XScreenSaverAllocInfo_fn mXSSAllocInfo
= nullptr;
147 _XScreenSaverQueryInfo_fn mXSSQueryInfo
= nullptr;
151 #ifdef MOZ_ENABLE_DBUS
152 class UserIdleServiceMutter
: public UserIdleServiceImpl
{
154 bool PollIdleTime(uint32_t* aIdleTime
) override
{
155 MOZ_LOG(sIdleLog
, LogLevel::Info
, ("PollIdleTime() request\n"));
157 // We're not ready yet
162 if (!mPollInProgress
) {
163 mPollInProgress
= true;
164 DBusProxyCall(mProxy
, "GetIdletime", nullptr, G_DBUS_CALL_FLAGS_NONE
, -1,
167 GetCurrentSerialEventTarget(), __func__
,
168 // It's safe to capture this as we use mCancellable to stop
170 [this](RefPtr
<GVariant
>&& aResult
) {
171 if (!g_variant_is_of_type(aResult
, G_VARIANT_TYPE_TUPLE
) ||
172 g_variant_n_children(aResult
) != 1) {
173 MOZ_LOG(sIdleLog
, LogLevel::Info
,
174 ("PollIdleTime() Unexpected params type: %s\n",
175 g_variant_get_type_string(aResult
)));
179 RefPtr
<GVariant
> iTime
=
180 dont_AddRef(g_variant_get_child_value(aResult
, 0));
181 if (!g_variant_is_of_type(iTime
, G_VARIANT_TYPE_UINT64
)) {
182 MOZ_LOG(sIdleLog
, LogLevel::Info
,
183 ("PollIdleTime() Unexpected params type: %s\n",
184 g_variant_get_type_string(aResult
)));
188 uint64_t idleTime
= g_variant_get_uint64(iTime
);
189 if (idleTime
> std::numeric_limits
<uint32_t>::max()) {
190 idleTime
= std::numeric_limits
<uint32_t>::max();
192 mLastIdleTime
= idleTime
;
193 mPollInProgress
= false;
194 MOZ_LOG(sIdleLog
, LogLevel::Info
,
195 ("Async handler got %d\n", mLastIdleTime
));
197 [this](GUniquePtr
<GError
>&& aError
) {
198 mPollInProgress
= false;
199 if (!IsCancelledGError(aError
.get())) {
201 sIdleLog
, LogLevel::Warning
,
202 ("Failed to call GetIdletime(): %s\n", aError
->message
));
203 mUserIdleServiceGTK
->RejectAndTryNextServiceCallback();
208 *aIdleTime
= mLastIdleTime
;
209 MOZ_LOG(sIdleLog
, LogLevel::Info
,
210 ("PollIdleTime() returns %d\n", *aIdleTime
));
214 bool ProbeImplementation() override
{
215 MOZ_LOG(sIdleLog
, LogLevel::Info
,
216 ("UserIdleServiceMutter::UserIdleServiceMutter()\n"));
218 mCancellable
= dont_AddRef(g_cancellable_new());
219 CreateDBusProxyForBus(
221 GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS
|
222 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
),
223 nullptr, "org.gnome.Mutter.IdleMonitor",
224 "/org/gnome/Mutter/IdleMonitor/Core", "org.gnome.Mutter.IdleMonitor",
227 GetCurrentSerialEventTarget(), __func__
,
228 [this](RefPtr
<GDBusProxy
>&& aProxy
) {
229 mProxy
= std::move(aProxy
);
230 mUserIdleServiceGTK
->AcceptServiceCallback();
232 [this](GUniquePtr
<GError
>&& aError
) {
233 if (!IsCancelledGError(aError
.get())) {
234 mUserIdleServiceGTK
->RejectAndTryNextServiceCallback();
240 explicit UserIdleServiceMutter(nsUserIdleServiceGTK
* aUserIdleService
)
241 : UserIdleServiceImpl(aUserIdleService
){};
243 ~UserIdleServiceMutter() {
245 g_cancellable_cancel(mCancellable
);
246 mCancellable
= nullptr;
252 RefPtr
<GDBusProxy
> mProxy
;
253 RefPtr
<GCancellable
> mCancellable
;
254 uint32_t mLastIdleTime
= 0;
255 bool mPollInProgress
= false;
259 void nsUserIdleServiceGTK::ProbeService() {
260 MOZ_LOG(sIdleLog
, LogLevel::Info
,
261 ("nsUserIdleServiceGTK::ProbeService() mIdleServiceType %d\n",
263 MOZ_ASSERT(!mIdleService
);
265 switch (mIdleServiceType
) {
266 #ifdef MOZ_ENABLE_DBUS
267 case IDLE_SERVICE_MUTTER
:
268 mIdleService
= MakeUnique
<UserIdleServiceMutter
>(this);
272 case IDLE_SERVICE_XSCREENSAVER
:
273 mIdleService
= MakeUnique
<UserIdleServiceX11
>(this);
280 if (!mIdleService
->ProbeImplementation()) {
281 RejectAndTryNextServiceCallback();
285 void nsUserIdleServiceGTK::AcceptServiceCallback() {
286 MOZ_LOG(sIdleLog
, LogLevel::Info
,
287 ("nsUserIdleServiceGTK::AcceptServiceCallback() type %d\n",
289 mIdleServiceInitialized
= true;
292 void nsUserIdleServiceGTK::RejectAndTryNextServiceCallback() {
293 MOZ_LOG(sIdleLog
, LogLevel::Info
,
294 ("nsUserIdleServiceGTK::RejectAndTryNextServiceCallback() type %d\n",
297 // Delete recent non-working service
298 MOZ_ASSERT(mIdleService
, "Nothing to reject?");
299 mIdleService
= nullptr;
300 mIdleServiceInitialized
= false;
303 if (mIdleServiceType
< IDLE_SERVICE_NONE
) {
304 MOZ_LOG(sIdleLog
, LogLevel::Info
,
305 ("nsUserIdleServiceGTK try next idle service\n"));
308 MOZ_LOG(sIdleLog
, LogLevel::Info
, ("nsUserIdleServiceGTK failed\n"));
312 bool nsUserIdleServiceGTK::PollIdleTime(uint32_t* aIdleTime
) {
313 if (!mIdleServiceInitialized
) {
316 return mIdleService
->PollIdleTime(aIdleTime
);