1 | /* |
2 | * Copyright (C) 2017 Igalia S.L. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 | * THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "SleepDisablerGLib.h" |
28 | |
29 | #include <gio/gio.h> |
30 | #include <wtf/glib/GUniquePtr.h> |
31 | |
32 | namespace PAL { |
33 | |
34 | std::unique_ptr<SleepDisabler> SleepDisabler::create(const char* reason, Type type) |
35 | { |
36 | return std::unique_ptr<SleepDisabler>(new SleepDisablerGLib(reason, type)); |
37 | } |
38 | |
39 | SleepDisablerGLib::SleepDisablerGLib(const char* reason, Type type) |
40 | : SleepDisabler(reason, type) |
41 | , m_cancellable(adoptGRef(g_cancellable_new())) |
42 | , m_reason(reason) |
43 | { |
44 | // We ignore Type because we always want to inhibit both screen lock and |
45 | // suspend, but only when idle. There is no reason for WebKit to ever block |
46 | // a user from manually suspending the computer, so inhibiting idle |
47 | // suffices. There's also probably no good reason for code taking a sleep |
48 | // disabler to differentiate between lock and suspend on our platform. If we |
49 | // ever need this distinction, which seems unlikely, then we'll need to |
50 | // audit all use of SleepDisabler. |
51 | |
52 | // First, try to use the ScreenSaver API. |
53 | g_dbus_proxy_new_for_bus(G_BUS_TYPE_SESSION, static_cast<GDBusProxyFlags>(G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS), |
54 | nullptr, "org.freedesktop.ScreenSaver" , "/org/freedesktop/ScreenSaver" , "org.freedesktop.ScreenSaver" , m_cancellable.get(), |
55 | [](GObject*, GAsyncResult* result, gpointer userData) { |
56 | GUniqueOutPtr<GError> error; |
57 | GRefPtr<GDBusProxy> proxy = adoptGRef(g_dbus_proxy_new_for_bus_finish(result, &error.outPtr())); |
58 | if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
59 | return; |
60 | |
61 | auto* self = static_cast<SleepDisablerGLib*>(userData); |
62 | if (proxy) { |
63 | // Under Flatpak, we'll get a useless proxy with no name owner. |
64 | GUniquePtr<char> nameOwner(g_dbus_proxy_get_name_owner(proxy.get())); |
65 | if (nameOwner) { |
66 | self->m_screenSaverProxy = WTFMove(proxy); |
67 | self->acquireInhibitor(); |
68 | return; |
69 | } |
70 | } |
71 | |
72 | // It failed. Try to use the Flatpak portal instead. |
73 | g_dbus_proxy_new_for_bus(G_BUS_TYPE_SESSION, static_cast<GDBusProxyFlags>(G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS), |
74 | nullptr, "org.freedesktop.portal.Desktop" , "/org/freedesktop/portal/desktop" , "org.freedesktop.portal.Inhibit" , self->m_cancellable.get(), |
75 | [](GObject*, GAsyncResult* result, gpointer userData) { |
76 | GUniqueOutPtr<GError> error; |
77 | GRefPtr<GDBusProxy> proxy = adoptGRef(g_dbus_proxy_new_for_bus_finish(result, &error.outPtr())); |
78 | if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
79 | return; |
80 | |
81 | auto* self = static_cast<SleepDisablerGLib*>(userData); |
82 | if (proxy) { |
83 | GUniquePtr<char> nameOwner(g_dbus_proxy_get_name_owner(proxy.get())); |
84 | if (nameOwner) { |
85 | self->m_inhibitPortalProxy = WTFMove(proxy); |
86 | self->acquireInhibitor(); |
87 | return; |
88 | } |
89 | } |
90 | |
91 | // Give up. Don't warn the user: this is expected. |
92 | self->m_cancellable = nullptr; |
93 | }, self); |
94 | }, this); |
95 | } |
96 | |
97 | SleepDisablerGLib::~SleepDisablerGLib() |
98 | { |
99 | if (m_cancellable) |
100 | g_cancellable_cancel(m_cancellable.get()); |
101 | else if (m_screenSaverCookie || m_inhibitPortalRequestObjectPath) |
102 | releaseInhibitor(); |
103 | } |
104 | |
105 | void SleepDisablerGLib::acquireInhibitor() |
106 | { |
107 | if (m_screenSaverProxy) { |
108 | ASSERT(!m_inhibitPortalProxy); |
109 | acquireInhibitorViaScreenSaverProxy(); |
110 | } else { |
111 | ASSERT(m_inhibitPortalProxy); |
112 | acquireInhibitorViaInhibitPortalProxy(); |
113 | } |
114 | } |
115 | |
116 | void SleepDisablerGLib::acquireInhibitorViaScreenSaverProxy() |
117 | { |
118 | g_dbus_proxy_call(m_screenSaverProxy.get(), "Inhibit" , g_variant_new("(ss)" , g_get_prgname(), m_reason.data()), |
119 | G_DBUS_CALL_FLAGS_NONE, -1, m_cancellable.get(), [](GObject* proxy, GAsyncResult* result, gpointer userData) { |
120 | GUniqueOutPtr<GError> error; |
121 | GRefPtr<GVariant> returnValue = adoptGRef(g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), result, &error.outPtr())); |
122 | if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
123 | return; |
124 | |
125 | auto* self = static_cast<SleepDisablerGLib*>(userData); |
126 | if (error) |
127 | g_warning("Calling %s.Inhibit failed: %s" , g_dbus_proxy_get_interface_name(G_DBUS_PROXY(proxy)), error->message); |
128 | else { |
129 | ASSERT(returnValue); |
130 | g_variant_get(returnValue.get(), "(u)" , &self->m_screenSaverCookie); |
131 | } |
132 | self->m_cancellable = nullptr; |
133 | }, this); |
134 | } |
135 | |
136 | void SleepDisablerGLib::acquireInhibitorViaInhibitPortalProxy() |
137 | { |
138 | GVariantBuilder builder; |
139 | g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); |
140 | g_variant_builder_add(&builder, "{sv}" , "reason" , g_variant_new_string(m_reason.data())); |
141 | g_dbus_proxy_call(m_inhibitPortalProxy.get(), "Inhibit" , g_variant_new("(su@a{sv})" , "" /* no window */, 8 /* idle */, g_variant_builder_end(&builder)), |
142 | G_DBUS_CALL_FLAGS_NONE, -1, m_cancellable.get(), [](GObject* proxy, GAsyncResult* result, gpointer userData) { |
143 | GUniqueOutPtr<GError> error; |
144 | GRefPtr<GVariant> returnValue = adoptGRef(g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), result, &error.outPtr())); |
145 | if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
146 | return; |
147 | |
148 | auto* self = static_cast<SleepDisablerGLib*>(userData); |
149 | if (error) |
150 | g_warning("Calling %s.Inhibit failed: %s" , g_dbus_proxy_get_interface_name(G_DBUS_PROXY(proxy)), error->message); |
151 | else { |
152 | ASSERT(returnValue); |
153 | g_variant_get(returnValue.get(), "(o)" , &self->m_inhibitPortalRequestObjectPath.outPtr()); |
154 | } |
155 | self->m_cancellable = nullptr; |
156 | }, this); |
157 | } |
158 | |
159 | void SleepDisablerGLib::releaseInhibitor() |
160 | { |
161 | if (m_screenSaverProxy) { |
162 | ASSERT(!m_inhibitPortalProxy); |
163 | releaseInhibitorViaScreenSaverProxy(); |
164 | } else { |
165 | ASSERT(m_inhibitPortalProxy); |
166 | releaseInhibitorViaInhibitPortalProxy(); |
167 | } |
168 | } |
169 | |
170 | void SleepDisablerGLib::releaseInhibitorViaScreenSaverProxy() |
171 | { |
172 | ASSERT(m_screenSaverCookie); |
173 | |
174 | g_dbus_proxy_call(m_screenSaverProxy.get(), "UnInhibit" , g_variant_new("(u)" , m_screenSaverCookie), |
175 | G_DBUS_CALL_FLAGS_NONE, -1, nullptr, [](GObject* proxy, GAsyncResult* result, gpointer) { |
176 | GUniqueOutPtr<GError> error; |
177 | GRefPtr<GVariant> returnValue = adoptGRef(g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), result, &error.outPtr())); |
178 | if (error) |
179 | g_warning("Calling %s.UnInhibit failed: %s" , g_dbus_proxy_get_interface_name(G_DBUS_PROXY(proxy)), error->message); |
180 | }, nullptr); |
181 | } |
182 | |
183 | void SleepDisablerGLib::releaseInhibitorViaInhibitPortalProxy() |
184 | { |
185 | ASSERT(m_inhibitPortalRequestObjectPath); |
186 | |
187 | g_dbus_proxy_new_for_bus(G_BUS_TYPE_SESSION, static_cast<GDBusProxyFlags>(G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS), |
188 | nullptr, "org.freedesktop.portal.Desktop" , m_inhibitPortalRequestObjectPath.get(), "org.freedesktop.portal.Request" , nullptr, |
189 | [](GObject*, GAsyncResult* result, gpointer) { |
190 | GUniqueOutPtr<GError> error; |
191 | GRefPtr<GDBusProxy> requestProxy = adoptGRef(g_dbus_proxy_new_for_bus_finish(result, &error.outPtr())); |
192 | if (error) { |
193 | g_warning("Failed to create org.freedesktop.portal.Request proxy: %s" , error->message); |
194 | return; |
195 | } |
196 | |
197 | ASSERT(requestProxy); |
198 | g_dbus_proxy_call(requestProxy.get(), "Close" , g_variant_new("()" ), G_DBUS_CALL_FLAGS_NONE, -1, nullptr, |
199 | [](GObject* proxy, GAsyncResult* result, gpointer) { |
200 | GUniqueOutPtr<GError> error; |
201 | GRefPtr<GVariant> returnValue = adoptGRef(g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), result, &error.outPtr())); |
202 | if (error) |
203 | g_warning("Calling %s.Close failed: %s" , g_dbus_proxy_get_interface_name(G_DBUS_PROXY(proxy)), error->message); |
204 | }, nullptr); |
205 | }, nullptr); |
206 | } |
207 | |
208 | } // namespace PAL |
209 | |