Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / widget / gtk / nsUserIdleServiceGTK.cpp
blob4cdc5ba13dd4eaddac7558c1cb2b36dd93ba1c0e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=4:tabstop=4:
3 */
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/. */
8 #include <gtk/gtk.h>
10 #include "nsUserIdleServiceGTK.h"
11 #include "nsDebug.h"
12 #include "prlink.h"
13 #include "mozilla/Logging.h"
14 #include "WidgetUtilsGtk.h"
15 #ifdef MOZ_X11
16 # include <X11/Xlib.h>
17 # include <X11/Xutil.h>
18 # include <gdk/gdkx.h>
19 #endif
20 #ifdef MOZ_ENABLE_DBUS
21 # include <gio/gio.h>
22 # include "AsyncDBus.h"
23 # include "WakeLockListener.h"
24 # include "nsIObserverService.h"
25 # include "mozilla/UniquePtrExtensions.h"
26 #endif
28 using mozilla::LogLevel;
29 static mozilla::LazyLogModule sIdleLog("nsIUserIdleService");
31 using namespace mozilla;
32 using namespace mozilla::widget;
34 #ifdef MOZ_X11
35 typedef struct {
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
42 } XScreenSaverInfo;
44 typedef bool (*_XScreenSaverQueryExtension_fn)(Display* dpy, int* event_base,
45 int* error_base);
46 typedef XScreenSaverInfo* (*_XScreenSaverAllocInfo_fn)(void);
47 typedef void (*_XScreenSaverQueryInfo_fn)(Display* dpy, Drawable drw,
48 XScreenSaverInfo* info);
50 class UserIdleServiceX11 : public UserIdleServiceImpl {
51 public:
52 bool PollIdleTime(uint32_t* aIdleTime) override {
53 // Ask xscreensaver about idle time:
54 *aIdleTime = 0;
56 // We might not have a display (cf. in xpcshell)
57 Display* dplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
58 if (!dplay) {
59 MOZ_LOG(sIdleLog, LogLevel::Warning, ("No display found!\n"));
60 return false;
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));
71 return true;
73 // If we get here, we couldn't get to XScreenSaver:
74 MOZ_LOG(sIdleLog, LogLevel::Warning,
75 ("XSSQueryExtension returned false!\n"));
76 return false;
79 bool ProbeImplementation() override {
80 MOZ_LOG(sIdleLog, LogLevel::Info,
81 ("UserIdleServiceX11::UserIdleServiceX11()\n"));
83 if (!mozilla::widget::GdkIsX11Display()) {
84 return false;
87 // This will leak - See comments in ~UserIdleServiceX11().
88 PRLibrary* xsslib = PR_LoadLibrary("libXss.so.1");
89 if (!xsslib) // ouch.
91 MOZ_LOG(sIdleLog, LogLevel::Warning, ("Failed to find libXss.so!\n"));
92 return false;
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
114 return false;
117 // UserIdleServiceX11 uses sync init, confirm it now.
118 mUserIdleServiceGTK->AcceptServiceCallback();
119 return true;
122 explicit UserIdleServiceX11(nsUserIdleServiceGTK* aUserIdleService)
123 : UserIdleServiceImpl(aUserIdleService){};
125 ~UserIdleServiceX11() {
126 # ifdef MOZ_X11
127 if (mXssInfo) {
128 XFree(mXssInfo);
130 # endif
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.)
135 # if 0
136 if (xsslib) {
137 PR_UnloadLibrary(xsslib);
138 xsslib = nullptr;
140 # endif
143 private:
144 XScreenSaverInfo* mXssInfo = nullptr;
145 _XScreenSaverQueryExtension_fn mXSSQueryExtension = nullptr;
146 _XScreenSaverAllocInfo_fn mXSSAllocInfo = nullptr;
147 _XScreenSaverQueryInfo_fn mXSSQueryInfo = nullptr;
149 #endif
151 #ifdef MOZ_ENABLE_DBUS
152 class UserIdleServiceMutter : public UserIdleServiceImpl {
153 public:
154 bool PollIdleTime(uint32_t* aIdleTime) override {
155 MOZ_LOG(sIdleLog, LogLevel::Info, ("PollIdleTime() request\n"));
157 // We're not ready yet
158 if (!mProxy) {
159 return false;
162 if (!mPollInProgress) {
163 mPollInProgress = true;
164 DBusProxyCall(mProxy, "GetIdletime", nullptr, G_DBUS_CALL_FLAGS_NONE, -1,
165 mCancellable)
166 ->Then(
167 GetCurrentSerialEventTarget(), __func__,
168 // It's safe to capture this as we use mCancellable to stop
169 // listening.
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)));
176 mLastIdleTime = 0;
177 return;
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)));
185 mLastIdleTime = 0;
186 return;
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())) {
200 MOZ_LOG(
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));
211 return true;
214 bool ProbeImplementation() override {
215 MOZ_LOG(sIdleLog, LogLevel::Info,
216 ("UserIdleServiceMutter::UserIdleServiceMutter()\n"));
218 mCancellable = dont_AddRef(g_cancellable_new());
219 CreateDBusProxyForBus(
220 G_BUS_TYPE_SESSION,
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",
225 mCancellable)
226 ->Then(
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();
237 return true;
240 explicit UserIdleServiceMutter(nsUserIdleServiceGTK* aUserIdleService)
241 : UserIdleServiceImpl(aUserIdleService){};
243 ~UserIdleServiceMutter() {
244 if (mCancellable) {
245 g_cancellable_cancel(mCancellable);
246 mCancellable = nullptr;
248 mProxy = nullptr;
251 private:
252 RefPtr<GDBusProxy> mProxy;
253 RefPtr<GCancellable> mCancellable;
254 uint32_t mLastIdleTime = 0;
255 bool mPollInProgress = false;
257 #endif
259 void nsUserIdleServiceGTK::ProbeService() {
260 MOZ_LOG(sIdleLog, LogLevel::Info,
261 ("nsUserIdleServiceGTK::ProbeService() mIdleServiceType %d\n",
262 mIdleServiceType));
263 MOZ_ASSERT(!mIdleService);
265 switch (mIdleServiceType) {
266 #ifdef MOZ_ENABLE_DBUS
267 case IDLE_SERVICE_MUTTER:
268 mIdleService = MakeUnique<UserIdleServiceMutter>(this);
269 break;
270 #endif
271 #ifdef MOZ_X11
272 case IDLE_SERVICE_XSCREENSAVER:
273 mIdleService = MakeUnique<UserIdleServiceX11>(this);
274 break;
275 #endif
276 default:
277 return;
280 if (!mIdleService->ProbeImplementation()) {
281 RejectAndTryNextServiceCallback();
285 void nsUserIdleServiceGTK::AcceptServiceCallback() {
286 MOZ_LOG(sIdleLog, LogLevel::Info,
287 ("nsUserIdleServiceGTK::AcceptServiceCallback() type %d\n",
288 mIdleServiceType));
289 mIdleServiceInitialized = true;
292 void nsUserIdleServiceGTK::RejectAndTryNextServiceCallback() {
293 MOZ_LOG(sIdleLog, LogLevel::Info,
294 ("nsUserIdleServiceGTK::RejectAndTryNextServiceCallback() type %d\n",
295 mIdleServiceType));
297 // Delete recent non-working service
298 MOZ_ASSERT(mIdleService, "Nothing to reject?");
299 mIdleService = nullptr;
300 mIdleServiceInitialized = false;
302 mIdleServiceType++;
303 if (mIdleServiceType < IDLE_SERVICE_NONE) {
304 MOZ_LOG(sIdleLog, LogLevel::Info,
305 ("nsUserIdleServiceGTK try next idle service\n"));
306 ProbeService();
307 } else {
308 MOZ_LOG(sIdleLog, LogLevel::Info, ("nsUserIdleServiceGTK failed\n"));
312 bool nsUserIdleServiceGTK::PollIdleTime(uint32_t* aIdleTime) {
313 if (!mIdleServiceInitialized) {
314 return false;
316 return mIdleService->PollIdleTime(aIdleTime);